]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android-sdk/kernel-video.git/commitdiff
Merge branch 'linux-3.8.y' of http://git.kernel.org/pub/scm/linux/kernel/git/stable...
authorDan Murphy <dmurphy@ti.com>
Thu, 28 Mar 2013 19:56:01 +0000 (14:56 -0500)
committerDan Murphy <dmurphy@ti.com>
Thu, 28 Mar 2013 19:56:01 +0000 (14:56 -0500)
* 'linux-3.8.y' of http://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable: (99 commits)
  Linux 3.8.5
  rt2x00: error in configurations with mesh support disabled
  ARM: DMA-mapping: add missing GFP_DMA flag for atomic buffer allocation
  usb: musb: da8xx: Fix build breakage due to typo
  USB: io_ti: fix get_icount for two port adapters
  USB: garmin_gps: fix memory leak on disconnect
  udf: Fix bitmap overflow on large filesystems with small block size
  ACPI: Rework acpi_get_child() to be more efficient
  efivars: Fix check for CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE
  efivars: Add module parameter to disable use as a pstore backend
  efivars: Allow disabling use as a pstore backend
  USB: serial: fix interface refcounting
  usb: gadget: ffs: fix enable multiple instances
  USB: EHCI: fix regression in QH unlinking
  USB: EHCI: fix regression during bus resume
  USB: cdc-acm: fix device unregistration
  USB: xhci: correctly enable interrupts
  USB: xhci - fix bit definitions for IMAN register
  x86-64: Fix the failure case in copy_user_handle_tail()
  clockevents: Don't allow dummy broadcast timers
  ...

Signed-off-by: Dan Murphy <dmurphy@ti.com>
972 files changed:
Documentation/devicetree/bindings/arm/omap/timer.txt
Documentation/devicetree/bindings/clock/ti-clock-alias.txt [new file with mode: 0644]
Documentation/devicetree/bindings/crypto/omap-aes.txt [new file with mode: 0644]
Documentation/devicetree/bindings/crypto/omap-sham.txt [new file with mode: 0644]
Documentation/devicetree/bindings/dma/dma.txt [new file with mode: 0644]
Documentation/devicetree/bindings/dma/snps-dma.txt
Documentation/devicetree/bindings/dma/ti-edma.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/omap-usb-host.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/omap-usb-tll.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/palmas.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
Documentation/devicetree/bindings/net/cpsw.txt
Documentation/devicetree/bindings/sound/ak4642.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/cs4271.txt
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/omap-abe-core.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/omap-twl4030.txt
Documentation/devicetree/bindings/sound/renesas,fsi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8962.txt [new file with mode: 0644]
Documentation/devicetree/bindings/spi/omap-spi.txt
Documentation/devicetree/bindings/thermal/dove-thermal.txt [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/kirkwood-thermal.txt [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/rcar-thermal.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/dwc3.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/omap-ehci.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/omap-usb.txt
Documentation/devicetree/bindings/usb/omap3-ohci.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/twlxxxx-usb.txt
Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/usb-phy.txt
Documentation/nfc/nfc-hci.txt
Documentation/nfc/nfc-pn544.txt
Documentation/sound/alsa/soc/firmware [new file with mode: 0644]
Documentation/thermal/exynos_thermal_emulation [new file with mode: 0644]
Documentation/thermal/intel_powerclamp.txt [new file with mode: 0644]
Documentation/thermal/sysfs-api.txt
arch/arm/Kconfig
arch/arm/Makefile
arch/arm/boot/Makefile
arch/arm/boot/dts/Makefile
arch/arm/boot/dts/am335x-bone.dts
arch/arm/boot/dts/am335x-evm.dts
arch/arm/boot/dts/am335x-evmsk.dts
arch/arm/boot/dts/am33xx.dtsi
arch/arm/boot/dts/omap3-beagle-xm.dts
arch/arm/boot/dts/omap3-beagle.dts
arch/arm/boot/dts/omap3-evm.dts
arch/arm/boot/dts/omap3-overo.dtsi
arch/arm/boot/dts/omap3.dtsi
arch/arm/boot/dts/omap4-panda-es.dts
arch/arm/boot/dts/omap4-panda.dts
arch/arm/boot/dts/omap4-sdp.dts
arch/arm/boot/dts/omap4.dtsi
arch/arm/boot/dts/omap5-evm.dts [deleted file]
arch/arm/boot/dts/omap5-panda.dts [new file with mode: 0644]
arch/arm/boot/dts/omap5-sevm.dts [new file with mode: 0644]
arch/arm/boot/dts/omap5-uevm.dts [new file with mode: 0644]
arch/arm/boot/dts/omap5.dtsi
arch/arm/boot/dts/palmas.dtsi [new file with mode: 0644]
arch/arm/boot/dts/spear1340.dtsi
arch/arm/boot/dts/spear13xx.dtsi
arch/arm/boot/dts/twl4030.dtsi
arch/arm/boot/dts/twl6030.dtsi
arch/arm/boot/dts/twl6040.dtsi [new file with mode: 0644]
arch/arm/common/Kconfig
arch/arm/common/Makefile
arch/arm/common/edma.c [moved from arch/arm/mach-davinci/dma.c with 86% similarity]
arch/arm/configs/omap1_defconfig
arch/arm/configs/omap2plus_defconfig
arch/arm/include/asm/pgtable.h
arch/arm/kernel/smp.c
arch/arm/kernel/stacktrace.c
arch/arm/mach-davinci/Makefile
arch/arm/mach-davinci/board-tnetv107x-evm.c
arch/arm/mach-davinci/davinci.h
arch/arm/mach-davinci/devices-tnetv107x.c
arch/arm/mach-davinci/devices.c
arch/arm/mach-davinci/dm355.c
arch/arm/mach-davinci/dm365.c
arch/arm/mach-davinci/dm644x.c
arch/arm/mach-davinci/dm646x.c
arch/arm/mach-davinci/include/mach/da8xx.h
arch/arm/mach-omap1/Makefile
arch/arm/mach-omap2/Kconfig
arch/arm/mach-omap2/Makefile
arch/arm/mach-omap2/am33xx-restart.c [new file with mode: 0644]
arch/arm/mach-omap2/board-2430sdp.c
arch/arm/mach-omap2/board-3430sdp.c
arch/arm/mach-omap2/board-3630sdp.c
arch/arm/mach-omap2/board-4430sdp.c
arch/arm/mach-omap2/board-am3517crane.c
arch/arm/mach-omap2/board-am3517evm.c
arch/arm/mach-omap2/board-cm-t35.c
arch/arm/mach-omap2/board-cm-t3517.c
arch/arm/mach-omap2/board-devkit8000.c
arch/arm/mach-omap2/board-generic.c
arch/arm/mach-omap2/board-igep0020.c
arch/arm/mach-omap2/board-ldp.c
arch/arm/mach-omap2/board-omap3beagle.c
arch/arm/mach-omap2/board-omap3evm.c
arch/arm/mach-omap2/board-omap3logic.c
arch/arm/mach-omap2/board-omap3pandora.c
arch/arm/mach-omap2/board-omap3stalker.c
arch/arm/mach-omap2/board-omap3touchbook.c
arch/arm/mach-omap2/board-omap4panda.c
arch/arm/mach-omap2/board-overo.c
arch/arm/mach-omap2/board-rm680.c
arch/arm/mach-omap2/board-zoom-display.c
arch/arm/mach-omap2/board-zoom-peripherals.c
arch/arm/mach-omap2/board-zoom.c
arch/arm/mach-omap2/cclock2430_data.c
arch/arm/mach-omap2/cclock33xx_data.c
arch/arm/mach-omap2/cclock3xxx_data.c
arch/arm/mach-omap2/cclock54xx_data.c [new file with mode: 0644]
arch/arm/mach-omap2/clock.h
arch/arm/mach-omap2/clock54xx.h [new file with mode: 0644]
arch/arm/mach-omap2/clock_common_data.c
arch/arm/mach-omap2/clockdomain.c
arch/arm/mach-omap2/clockdomain.h
arch/arm/mach-omap2/clockdomains54xx_data.c [new file with mode: 0644]
arch/arm/mach-omap2/cm-regbits-54xx.h [new file with mode: 0644]
arch/arm/mach-omap2/cm1_54xx.h [new file with mode: 0644]
arch/arm/mach-omap2/cm2_54xx.h [new file with mode: 0644]
arch/arm/mach-omap2/cm2xxx.c
arch/arm/mach-omap2/cm33xx.c
arch/arm/mach-omap2/cm33xx.h
arch/arm/mach-omap2/cm3xxx.c
arch/arm/mach-omap2/cminst44xx.c
arch/arm/mach-omap2/common.h
arch/arm/mach-omap2/control.c
arch/arm/mach-omap2/control.h
arch/arm/mach-omap2/cpuidle34xx.c
arch/arm/mach-omap2/cpuidle_omap4plus.c [moved from arch/arm/mach-omap2/cpuidle44xx.c with 55% similarity]
arch/arm/mach-omap2/devices.c
arch/arm/mach-omap2/display.c
arch/arm/mach-omap2/dpll3xxx.c
arch/arm/mach-omap2/dss-common.c
arch/arm/mach-omap2/id.c
arch/arm/mach-omap2/io.c
arch/arm/mach-omap2/irq.c
arch/arm/mach-omap2/mux.c
arch/arm/mach-omap2/omap-hotplug.c
arch/arm/mach-omap2/omap-mpuss-lowpower.c
arch/arm/mach-omap2/omap-pm-noop.c
arch/arm/mach-omap2/omap-secure.h
arch/arm/mach-omap2/omap-smp.c
arch/arm/mach-omap2/omap-wakeupgen.c
arch/arm/mach-omap2/omap-wakeupgen.h
arch/arm/mach-omap2/omap4-common.c
arch/arm/mach-omap2/omap4-sar-layout.h
arch/arm/mach-omap2/omap54xx.h
arch/arm/mach-omap2/omap_hwmod.c
arch/arm/mach-omap2/omap_hwmod.h
arch/arm/mach-omap2/omap_hwmod_2420_data.c
arch/arm/mach-omap2/omap_hwmod_2430_data.c
arch/arm/mach-omap2/omap_hwmod_2xxx_interconnect_data.c
arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c
arch/arm/mach-omap2/omap_hwmod_33xx_data.c
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
arch/arm/mach-omap2/omap_hwmod_44xx_data.c
arch/arm/mach-omap2/omap_hwmod_54xx_data.c [new file with mode: 0644]
arch/arm/mach-omap2/omap_hwmod_common_data.h
arch/arm/mach-omap2/omap_hwmod_reset.c [new file with mode: 0644]
arch/arm/mach-omap2/opp4xxx_data.c
arch/arm/mach-omap2/pm-debug.c
arch/arm/mach-omap2/pm.c
arch/arm/mach-omap2/pm.h
arch/arm/mach-omap2/pm24xx.c
arch/arm/mach-omap2/pm33xx.c [new file with mode: 0644]
arch/arm/mach-omap2/pm33xx.h [new file with mode: 0644]
arch/arm/mach-omap2/pm34xx.c
arch/arm/mach-omap2/pm_omap4plus.c [moved from arch/arm/mach-omap2/pm44xx.c with 57% similarity]
arch/arm/mach-omap2/powerdomain.c
arch/arm/mach-omap2/powerdomain.h
arch/arm/mach-omap2/powerdomains2xxx_3xxx_data.c
arch/arm/mach-omap2/powerdomains2xxx_data.c
arch/arm/mach-omap2/powerdomains33xx_data.c
arch/arm/mach-omap2/powerdomains3xxx_data.c
arch/arm/mach-omap2/powerdomains44xx_data.c
arch/arm/mach-omap2/powerdomains54xx_data.c [new file with mode: 0644]
arch/arm/mach-omap2/prcm44xx.h
arch/arm/mach-omap2/prcm_mpu54xx.h [new file with mode: 0644]
arch/arm/mach-omap2/prm-regbits-54xx.h [new file with mode: 0644]
arch/arm/mach-omap2/prm2xxx.c
arch/arm/mach-omap2/prm2xxx_3xxx.c
arch/arm/mach-omap2/prm33xx.c
arch/arm/mach-omap2/prm33xx.h
arch/arm/mach-omap2/prm44xx.c
arch/arm/mach-omap2/prm54xx.h [new file with mode: 0644]
arch/arm/mach-omap2/prminst44xx.c
arch/arm/mach-omap2/sata.c [new file with mode: 0644]
arch/arm/mach-omap2/scrm54xx.h [new file with mode: 0644]
arch/arm/mach-omap2/serial.c
arch/arm/mach-omap2/sleep33xx.S [new file with mode: 0644]
arch/arm/mach-omap2/sleep_omap4plus.S [moved from arch/arm/mach-omap2/sleep44xx.S with 74% similarity]
arch/arm/mach-omap2/soc.h
arch/arm/mach-omap2/sram.c
arch/arm/mach-omap2/sram.h
arch/arm/mach-omap2/timer.c
arch/arm/mach-omap2/usb-host.c
arch/arm/mach-omap2/usb-musb.c
arch/arm/mach-omap2/usb.h
arch/arm/mach-omap2/voltage.h
arch/arm/mach-omap2/voltagedomains54xx_data.c [new file with mode: 0644]
arch/arm/mach-pxa/pxa27x.c
arch/arm/mach-s3c64xx/dma.c
arch/arm/mach-shmobile/board-ap4evb.c
arch/arm/mach-shmobile/board-armadillo800eva.c
arch/arm/mach-shmobile/board-kzm9g.c
arch/arm/mach-shmobile/board-mackerel.c
arch/arm/mach-spear13xx/include/mach/spear.h
arch/arm/mach-spear13xx/spear1310.c
arch/arm/mach-spear13xx/spear1340.c
arch/arm/mach-spear13xx/spear13xx.c
arch/arm/mach-spear3xx/spear3xx.c
arch/arm/mach-spear6xx/spear6xx.c
arch/arm/mm/mmu.c
arch/arm/plat-omap/Kconfig
arch/arm/plat-omap/Makefile
arch/arm/plat-omap/dma.c
arch/arm/plat-omap/include/plat/mailbox.h [deleted file]
arch/arm/plat-omap/include/plat/sata.h [new file with mode: 0644]
arch/arm/plat-omap/mailbox.c [deleted file]
arch/mips/bcm47xx/serial.c
arch/sh/boards/mach-ecovec24/setup.c
arch/sh/boards/mach-se/7724/setup.c
crypto/async_tx/async_memcpy.c
crypto/async_tx/async_memset.c
crypto/async_tx/async_tx.c
crypto/async_tx/async_xor.c
drivers/Kconfig
drivers/Makefile
drivers/ata/ahci_platform.c
drivers/base/dd.c
drivers/bcma/bcma_private.h
drivers/bcma/driver_chipcommon.c
drivers/bcma/driver_chipcommon_nflash.c
drivers/bcma/driver_chipcommon_sflash.c
drivers/bcma/driver_gpio.c
drivers/bcma/driver_mips.c
drivers/bcma/driver_pci_host.c
drivers/bcma/main.c
drivers/clk/Kconfig
drivers/clk/Makefile
drivers/clk/clk-palmas.c [new file with mode: 0644]
drivers/crypto/omap-aes.c
drivers/crypto/omap-sham.c
drivers/dma/Kconfig
drivers/dma/amba-pl08x.c
drivers/dma/at_hdmac.c
drivers/dma/at_hdmac_regs.h
drivers/dma/dmaengine.c
drivers/dma/dmatest.c
drivers/dma/dw_dmac.c
drivers/dma/dw_dmac_regs.h
drivers/dma/edma.c
drivers/dma/ep93xx_dma.c
drivers/dma/ioat/dma.c
drivers/dma/ioat/dma_v3.c
drivers/dma/ioat/hw.h
drivers/dma/ioat/pci.c
drivers/dma/iop-adma.c
drivers/dma/ipu/ipu_idmac.c
drivers/dma/ipu/ipu_irq.c
drivers/dma/mmp_pdma.c
drivers/dma/mv_xor.c
drivers/dma/mxs-dma.c
drivers/dma/pch_dma.c
drivers/dma/pl330.c
drivers/dma/sh/shdma-base.c
drivers/dma/sirf-dma.c
drivers/dma/ste_dma40.c
drivers/dma/ste_dma40_ll.c
drivers/dma/ste_dma40_ll.h
drivers/dma/tegra20-apb-dma.c
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-palmas.c [new file with mode: 0644]
drivers/gpio/gpio-pca953x.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/palmas-pwrbutton.c [new file with mode: 0644]
drivers/input/misc/twl4030-vibra.c
drivers/input/misc/twl6040-vibra.c
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/leds-palmas.c [new file with mode: 0644]
drivers/mailbox/Kconfig [new file with mode: 0644]
drivers/mailbox/Makefile [new file with mode: 0644]
drivers/mailbox/mailbox-dbx500.c [new file with mode: 0644]
drivers/mailbox/mailbox-omap1.c [moved from arch/arm/mach-omap1/mailbox.c with 64% similarity]
drivers/mailbox/mailbox-omap2.c [moved from arch/arm/mach-omap2/mailbox.c with 50% similarity]
drivers/mailbox/mailbox.c [new file with mode: 0644]
drivers/mailbox/mailbox_internal.h [new file with mode: 0644]
drivers/memory/emif.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/omap-usb-host.c
drivers/mfd/omap-usb-tll.c
drivers/mfd/omap-usb.h
drivers/mfd/palmas-gpadc.c [new file with mode: 0644]
drivers/mfd/palmas-pwm.c [new file with mode: 0644]
drivers/mfd/palmas-resource.c [new file with mode: 0644]
drivers/mfd/palmas.c
drivers/mfd/twl-core.c
drivers/misc/Kconfig
drivers/misc/atmel-ssc.c
drivers/misc/carma/carma-fpga-program.c
drivers/misc/carma/carma-fpga.c
drivers/mmc/host/davinci_mmc.c
drivers/mmc/host/omap_hsmmc.c
drivers/mtd/nand/fsmc_nand.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpsw_ale.c
drivers/net/ethernet/ti/cpsw_ale.h
drivers/net/ethernet/ti/davinci_cpdma.c
drivers/net/ethernet/ti/davinci_cpdma.h
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/ahb.c
drivers/net/wireless/ath/ath9k/ani.c
drivers/net/wireless/ath/ath9k/ani.h
drivers/net/wireless/ath/ath9k/ar5008_initvals.h
drivers/net/wireless/ath/ath9k/ar5008_phy.c
drivers/net/wireless/ath/ath9k/ar9001_initvals.h
drivers/net/wireless/ath/ath9k/ar9002_hw.c
drivers/net/wireless/ath/ath9k/ar9002_phy.c
drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
drivers/net/wireless/ath/ath9k/ar9003_calib.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_hw.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/ar9340_initvals.h
drivers/net/wireless/ath/ath9k/ar9485_initvals.h
drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h
drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/hw-ops.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/mac.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/mci.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/reg.h
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/carl9170/carl9170.h
drivers/net/wireless/ath/carl9170/fw.c
drivers/net/wireless/ath/carl9170/fwcmd.h
drivers/net/wireless/ath/carl9170/hw.h
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/ath/carl9170/tx.c
drivers/net/wireless/ath/carl9170/version.h
drivers/net/wireless/ath/regd.c
drivers/net/wireless/ath/regd.h
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/interrupt.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/netdev.c
drivers/net/wireless/ath/wil6210/pcie_bus.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/b43/tables_nphy.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
drivers/net/wireless/brcm80211/brcmsmac/channel.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/brcm80211/brcmsmac/scb.h
drivers/net/wireless/iwlegacy/3945-mac.c
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/4965.c
drivers/net/wireless/iwlegacy/commands.h
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlegacy/common.h
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/Makefile
drivers/net/wireless/iwlwifi/dvm/agn.h
drivers/net/wireless/iwlwifi/dvm/calib.c
drivers/net/wireless/iwlwifi/dvm/calib.h
drivers/net/wireless/iwlwifi/dvm/commands.h
drivers/net/wireless/iwlwifi/dvm/debugfs.c
drivers/net/wireless/iwlwifi/dvm/dev.h
drivers/net/wireless/iwlwifi/dvm/devices.c
drivers/net/wireless/iwlwifi/dvm/led.c
drivers/net/wireless/iwlwifi/dvm/led.h
drivers/net/wireless/iwlwifi/dvm/lib.c
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/dvm/power.c
drivers/net/wireless/iwlwifi/dvm/power.h
drivers/net/wireless/iwlwifi/dvm/rs.c
drivers/net/wireless/iwlwifi/dvm/rs.h
drivers/net/wireless/iwlwifi/dvm/rx.c
drivers/net/wireless/iwlwifi/dvm/rxon.c
drivers/net/wireless/iwlwifi/dvm/scan.c
drivers/net/wireless/iwlwifi/dvm/sta.c
drivers/net/wireless/iwlwifi/dvm/testmode.c
drivers/net/wireless/iwlwifi/dvm/tt.c
drivers/net/wireless/iwlwifi/dvm/tt.h
drivers/net/wireless/iwlwifi/dvm/tx.c
drivers/net/wireless/iwlwifi/dvm/ucode.c
drivers/net/wireless/iwlwifi/iwl-agn-hw.h
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-csr.h
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-devtrace.c
drivers/net/wireless/iwlwifi/iwl-devtrace.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-drv.h
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
drivers/net/wireless/iwlwifi/iwl-eeprom-read.h
drivers/net/wireless/iwlwifi/iwl-fh.h
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-io.h
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/iwl-notif-wait.c
drivers/net/wireless/iwlwifi/iwl-notif-wait.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-nvm-parse.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/iwl-phy-db.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-phy-db.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-test.c
drivers/net/wireless/iwlwifi/iwl-test.h
drivers/net/wireless/iwlwifi/iwl-testmode.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/Makefile [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/binding.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/d3.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/debugfs.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/led.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/mac80211.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/mvm.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/nvm.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/ops.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/power.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/quota.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/rs.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/rs.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/rx.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/scan.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/sta.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/sta.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/time-event.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/time-event.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/tx.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/utils.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/pcie/1000.c
drivers/net/wireless/iwlwifi/pcie/2000.c
drivers/net/wireless/iwlwifi/pcie/5000.c
drivers/net/wireless/iwlwifi/pcie/6000.c
drivers/net/wireless/iwlwifi/pcie/7000.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/pcie/cfg.h
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/internal.h
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/libertas/cfg.c
drivers/net/wireless/libertas/cfg.h
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/11n.h
drivers/net/wireless/mwifiex/11n_aggr.c
drivers/net/wireless/mwifiex/README
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/debugfs.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/ioctl.h
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/pcie.h
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/txrx.c
drivers/net/wireless/mwifiex/uap_cmd.c
drivers/net/wireless/mwifiex/usb.c
drivers/net/wireless/mwifiex/util.c
drivers/net/wireless/mwifiex/util.h
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/p54/p54pci.c
drivers/net/wireless/prism54/isl_ioctl.c
drivers/net/wireless/ray_cs.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800lib.h
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rtlwifi/core.c
drivers/net/wireless/rtlwifi/rc.c
drivers/net/wireless/rtlwifi/regd.c
drivers/net/wireless/rtlwifi/regd.h
drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
drivers/net/wireless/rtlwifi/rtl8192de/dm.c
drivers/net/wireless/rtlwifi/rtl8192de/trx.c
drivers/net/wireless/rtlwifi/rtl8192se/trx.c
drivers/net/wireless/rtlwifi/rtl8723ae/fw.c
drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c
drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
drivers/net/wireless/rtlwifi/rtl8723ae/phy.c
drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
drivers/net/wireless/rtlwifi/usb.c
drivers/net/wireless/rtlwifi/wifi.h
drivers/net/wireless/ti/Kconfig
drivers/net/wireless/ti/Makefile
drivers/net/wireless/ti/wilink_platform_data.c [moved from drivers/net/wireless/ti/wlcore/wl12xx_platform_data.c with 100% similarity]
drivers/net/wireless/ti/wl1251/Kconfig
drivers/net/wireless/ti/wl12xx/Makefile
drivers/net/wireless/ti/wl12xx/cmd.c
drivers/net/wireless/ti/wl12xx/cmd.h
drivers/net/wireless/ti/wl12xx/event.c [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/event.h [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl12xx/scan.c [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/scan.h [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/wl12xx.h
drivers/net/wireless/ti/wl18xx/Makefile
drivers/net/wireless/ti/wl18xx/acx.c
drivers/net/wireless/ti/wl18xx/acx.h
drivers/net/wireless/ti/wl18xx/cmd.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/cmd.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/conf.h
drivers/net/wireless/ti/wl18xx/event.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/event.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/reg.h
drivers/net/wireless/ti/wl18xx/scan.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/scan.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/tx.c
drivers/net/wireless/ti/wl18xx/wl18xx.h
drivers/net/wireless/ti/wlcore/Kconfig
drivers/net/wireless/ti/wlcore/Makefile
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/acx.h
drivers/net/wireless/ti/wlcore/boot.c
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/conf.h
drivers/net/wireless/ti/wlcore/debug.h
drivers/net/wireless/ti/wlcore/debugfs.c
drivers/net/wireless/ti/wlcore/event.c
drivers/net/wireless/ti/wlcore/event.h
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/init.c
drivers/net/wireless/ti/wlcore/io.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/ps.c
drivers/net/wireless/ti/wlcore/rx.c
drivers/net/wireless/ti/wlcore/rx.h
drivers/net/wireless/ti/wlcore/scan.c
drivers/net/wireless/ti/wlcore/scan.h
drivers/net/wireless/ti/wlcore/sdio.c
drivers/net/wireless/ti/wlcore/spi.c
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/tx.h
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/nfcwilink.c
drivers/nfc/pn533.c
drivers/nfc/pn544/Kconfig [new file with mode: 0644]
drivers/nfc/pn544/Makefile
drivers/nfc/pn544/i2c.c
drivers/nfc/pn544/pn544.c
drivers/of/Makefile
drivers/of/dma.c [new file with mode: 0644]
drivers/regulator/palmas-regulator.c
drivers/remoteproc/Kconfig
drivers/remoteproc/omap_remoteproc.c
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/rtc-palmas.c [new file with mode: 0644]
drivers/spi/spi-omap2-mcspi.c
drivers/ssb/Kconfig
drivers/ssb/Makefile
drivers/ssb/driver_chipcommon_sflash.c [new file with mode: 0644]
drivers/ssb/driver_gpio.c
drivers/ssb/driver_mipscore.c
drivers/ssb/main.c
drivers/ssb/ssb_private.h
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/omap-thermal/Makefile [deleted file]
drivers/staging/omap-thermal/omap-bandgap.c [deleted file]
drivers/staging/omap-thermal/omap-bandgap.h [deleted file]
drivers/staging/omap-thermal/omap-thermal-common.c [deleted file]
drivers/staging/omap-thermal/omap5-thermal.c [deleted file]
drivers/staging/omapdrm/Makefile
drivers/staging/omapdrm/omap_dmm_priv.h
drivers/staging/omapdrm/omap_dmm_tiler.c
drivers/staging/omapdrm/omap_drv.c
drivers/staging/omapdrm/omap_drv.h
drivers/staging/omapdrm/omap_gem.c
drivers/staging/omapdrm/sita.c [new file with mode: 0644]
drivers/staging/omapdrm/tcm-sita.c [deleted file]
drivers/staging/omapdrm/tcm.h
drivers/staging/ti-soc-thermal/Kconfig [moved from drivers/staging/omap-thermal/Kconfig with 70% similarity]
drivers/staging/ti-soc-thermal/Makefile [new file with mode: 0644]
drivers/staging/ti-soc-thermal/TODO [moved from drivers/staging/omap-thermal/TODO with 72% similarity]
drivers/staging/ti-soc-thermal/omap4-thermal-data.c [moved from drivers/staging/omap-thermal/omap4-thermal.c with 85% similarity]
drivers/staging/ti-soc-thermal/omap4xxx-bandgap.h [new file with mode: 0644]
drivers/staging/ti-soc-thermal/omap5-thermal-data.c [new file with mode: 0644]
drivers/staging/ti-soc-thermal/omap5xxx-bandgap.h [new file with mode: 0644]
drivers/staging/ti-soc-thermal/ti-bandgap.c [new file with mode: 0644]
drivers/staging/ti-soc-thermal/ti-bandgap.h [new file with mode: 0644]
drivers/staging/ti-soc-thermal/ti-thermal-common.c [new file with mode: 0644]
drivers/staging/ti-soc-thermal/ti-thermal.h [moved from drivers/staging/omap-thermal/omap-thermal.h with 71% similarity]
drivers/staging/ti-soc-thermal/ti_soc_thermal.txt [moved from drivers/staging/omap-thermal/omap_bandgap.txt with 50% similarity]
drivers/staging/tidspbridge/Kconfig
drivers/staging/tidspbridge/core/_tiomap.h
drivers/staging/tidspbridge/core/chnl_sm.c
drivers/staging/tidspbridge/core/io_sm.c
drivers/staging/tidspbridge/core/tiomap3430.c
drivers/staging/tidspbridge/core/tiomap3430_pwr.c
drivers/staging/tidspbridge/core/tiomap_io.c
drivers/staging/tidspbridge/include/dspbridge/host_os.h
drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/cpu_cooling.c
drivers/thermal/db8500_cpufreq_cooling.c
drivers/thermal/db8500_thermal.c
drivers/thermal/dove_thermal.c [new file with mode: 0644]
drivers/thermal/exynos_thermal.c
drivers/thermal/intel_powerclamp.c [new file with mode: 0644]
drivers/thermal/kirkwood_thermal.c [new file with mode: 0644]
drivers/thermal/rcar_thermal.c
drivers/thermal/spear_thermal.c
drivers/thermal/step_wise.c
drivers/thermal/thermal_sys.c
drivers/tty/serial/omap-serial.c
drivers/usb/dwc3/core.c
drivers/usb/dwc3/dwc3-exynos.c
drivers/usb/dwc3/dwc3-omap.c
drivers/usb/dwc3/dwc3-pci.c
drivers/usb/dwc3/gadget.c
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-omap.c
drivers/usb/host/ohci-omap3.c
drivers/usb/musb/Kconfig
drivers/usb/musb/Makefile
drivers/usb/musb/cppi41.c [new file with mode: 0644]
drivers/usb/musb/cppi41.h [new file with mode: 0644]
drivers/usb/musb/cppi41_dma.c [new file with mode: 0644]
drivers/usb/musb/cppi41_dma.h [new file with mode: 0644]
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_core.h
drivers/usb/musb/musb_dma.h
drivers/usb/musb/musb_dsps.c
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/musb_gadget_ep0.c
drivers/usb/musb/musb_host.c
drivers/usb/musb/omap2430.c
drivers/usb/musb/omap2430.h
drivers/usb/otg/Kconfig
drivers/usb/otg/Makefile
drivers/usb/otg/nop-usb-xceiv.c
drivers/usb/otg/otg.c
drivers/usb/otg/palmas-usb.c [new file with mode: 0644]
drivers/usb/otg/twl4030-usb.c
drivers/usb/phy/Kconfig
drivers/usb/phy/Makefile
drivers/usb/phy/omap-control-usb.c [new file with mode: 0644]
drivers/usb/phy/omap-usb2.c
drivers/usb/phy/omap-usb3.c [new file with mode: 0644]
drivers/video/omap2/displays/Kconfig
drivers/video/omap2/displays/Makefile
drivers/video/omap2/displays/panel-lg4591.c [new file with mode: 0644]
drivers/video/omap2/displays/panel-taal.c
drivers/video/omap2/displays/panel-tfp410.c
drivers/video/omap2/dss/Kconfig
drivers/video/omap2/dss/Makefile
drivers/video/omap2/dss/core.c
drivers/video/omap2/dss/dispc.c
drivers/video/omap2/dss/dispc.h
drivers/video/omap2/dss/dpi.c
drivers/video/omap2/dss/dsi.c
drivers/video/omap2/dss/dss.c
drivers/video/omap2/dss/dss.h
drivers/video/omap2/dss/dss_features.c
drivers/video/omap2/dss/dss_features.h
drivers/video/omap2/dss/hdmi.c
drivers/video/omap2/dss/hdmi_panel.c
drivers/video/omap2/dss/ti_hdmi.h
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
drivers/video/omap2/dss/ti_hdmi_5xxx_ip.c [new file with mode: 0644]
drivers/video/omap2/dss/ti_hdmi_5xxx_ip.h [new file with mode: 0644]
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/omap_wdt.c
drivers/watchdog/omap_wdt.h
drivers/watchdog/palmas_wdt.c [new file with mode: 0644]
fs/exec.c
include/asm-generic/pgtable.h
include/linux/ahci_platform.h
include/linux/amba/pl080.h [moved from arch/arm/include/asm/hardware/pl080.h with 99% similarity]
include/linux/bcma/bcma_driver_chipcommon.h
include/linux/bcma/bcma_driver_mips.h
include/linux/bcma/bcma_driver_pci.h
include/linux/dmaengine.h
include/linux/dw_dmac.h
include/linux/i2c/twl.h
include/linux/ieee80211.h
include/linux/mailbox.h [new file with mode: 0644]
include/linux/mfd/arizona/pdata.h
include/linux/mfd/davinci_voicecodec.h
include/linux/mfd/palmas.h
include/linux/of_dma.h [new file with mode: 0644]
include/linux/platform_data/cpsw.h
include/linux/platform_data/dma-ste-dma40.h
include/linux/platform_data/dwc3-omap.h
include/linux/platform_data/edma.h [moved from arch/arm/mach-davinci/include/mach/edma.h with 59% similarity]
include/linux/platform_data/emif_plat.h
include/linux/platform_data/exynos_thermal.h
include/linux/platform_data/gpio-omap.h
include/linux/platform_data/mailbox-dbx500.h [new file with mode: 0644]
include/linux/platform_data/mailbox-omap.h [new file with mode: 0644]
include/linux/platform_data/mmc-davinci.h
include/linux/platform_data/omap-abe-twl6040.h
include/linux/platform_data/serial-omap.h
include/linux/platform_data/spi-davinci.h
include/linux/platform_data/usb-omap.h
include/linux/ssb/ssb_driver_mips.h
include/linux/thermal.h
include/linux/ti_emif.h [moved from drivers/memory/emif.h with 100% similarity]
include/linux/usb.h
include/linux/usb/dwc3-omap.h [new file with mode: 0644]
include/linux/usb/musb.h
include/linux/usb/nop-usb-xceiv.h
include/linux/usb/omap_control_usb.h [new file with mode: 0644]
include/linux/usb/omap_usb.h
include/linux/usb/phy.h
include/linux/wl12xx.h
include/net/bluetooth/a2mp.h
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/cfg80211.h
include/net/mac80211.h
include/net/nfc/hci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
include/net/regulatory.h
include/sound/aess.h [new file with mode: 0644]
include/sound/cs4271.h
include/sound/pcm.h
include/sound/saif.h [deleted file]
include/sound/sh_fsi.h
include/sound/simple_card.h
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc-fw.h [new file with mode: 0644]
include/sound/soc.h
include/sound/wm2000.h
include/uapi/linux/nfc.h
include/uapi/linux/nl80211.h
include/uapi/sound/asoc.h [new file with mode: 0644]
include/video/omapdss.h
mm/mmap.c
net/bluetooth/a2mp.c
net/bluetooth/amp.c
net/bluetooth/bnep/core.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sysfs.c
net/bluetooth/l2cap_core.c
net/bluetooth/mgmt.c
net/bluetooth/sco.c
net/ipv4/tcp.c
net/mac80211/agg-rx.c
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/driver-ops.h
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_hwmp.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/offchannel.c
net/mac80211/pm.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/nfc/core.c
net/nfc/hci/command.c
net/nfc/hci/core.c
net/nfc/hci/hcp.c
net/nfc/llcp/commands.c
net/nfc/llcp/llcp.c
net/nfc/llcp/llcp.h
net/nfc/llcp/sock.c
net/nfc/nci/core.c
net/nfc/netlink.c
net/wireless/ap.c
net/wireless/chan.c
net/wireless/core.c
net/wireless/core.h
net/wireless/mesh.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/rdev-ops.h
net/wireless/reg.c
net/wireless/reg.h
net/wireless/sme.c
net/wireless/trace.h
net/wireless/util.c
sound/arm/pxa2xx-ac97-lib.c
sound/core/pcm_lib.c
sound/core/pcm_native.c
sound/soc/Makefile
sound/soc/atmel/Kconfig
sound/soc/atmel/atmel-pcm-pdc.c
sound/soc/atmel/atmel-pcm.c
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h
sound/soc/codecs/cs4271.c
sound/soc/codecs/cs42l52.c
sound/soc/codecs/tlv320dac33.c
sound/soc/codecs/twl4030.c
sound/soc/codecs/twl6040.c
sound/soc/codecs/wm2000.c
sound/soc/codecs/wm2000.h
sound/soc/codecs/wm2200.c
sound/soc/codecs/wm5100.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8804.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8983.c
sound/soc/codecs/wm8985.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/codecs/wmfw.h
sound/soc/davinci/davinci-evm.c
sound/soc/davinci/davinci-pcm.c
sound/soc/davinci/davinci-pcm.h
sound/soc/davinci/davinci-sffsdr.c
sound/soc/dwc/designware_i2s.c
sound/soc/generic/simple-card.c
sound/soc/mxs/mxs-saif.c
sound/soc/omap/Kconfig
sound/soc/omap/Makefile
sound/soc/omap/aess/Makefile [new file with mode: 0644]
sound/soc/omap/aess/abe.h [new file with mode: 0644]
sound/soc/omap/aess/abe_aess.c [new file with mode: 0644]
sound/soc/omap/aess/abe_aess.h [new file with mode: 0644]
sound/soc/omap/aess/abe_asrc.c [new file with mode: 0644]
sound/soc/omap/aess/abe_core.c [new file with mode: 0644]
sound/soc/omap/aess/abe_dbg.h [new file with mode: 0644]
sound/soc/omap/aess/abe_def.h [new file with mode: 0644]
sound/soc/omap/aess/abe_ext.h [new file with mode: 0644]
sound/soc/omap/aess/abe_gain.c [new file with mode: 0644]
sound/soc/omap/aess/abe_gain.h [new file with mode: 0644]
sound/soc/omap/aess/abe_ini.c [new file with mode: 0644]
sound/soc/omap/aess/abe_mem.h [new file with mode: 0644]
sound/soc/omap/aess/abe_port.c [new file with mode: 0644]
sound/soc/omap/aess/abe_port.h [new file with mode: 0644]
sound/soc/omap/aess/abe_seq.c [new file with mode: 0644]
sound/soc/omap/aess/abe_seq.h [new file with mode: 0644]
sound/soc/omap/aess/abe_typ.h [new file with mode: 0644]
sound/soc/omap/aess/port_mgr.c [new file with mode: 0644]
sound/soc/omap/mcbsp.c
sound/soc/omap/omap-abe-core.c [new file with mode: 0644]
sound/soc/omap/omap-abe-dbg.c [new file with mode: 0644]
sound/soc/omap/omap-abe-mixer.c [new file with mode: 0644]
sound/soc/omap/omap-abe-mmap.c [new file with mode: 0644]
sound/soc/omap/omap-abe-opp.c [new file with mode: 0644]
sound/soc/omap/omap-abe-pcm.c [new file with mode: 0644]
sound/soc/omap/omap-abe-pm.c [new file with mode: 0644]
sound/soc/omap/omap-abe-priv.h [new file with mode: 0644]
sound/soc/omap/omap-abe-twl6040.c
sound/soc/omap/omap-dmic.c
sound/soc/omap/omap-hdmi-card.c
sound/soc/omap/omap-hdmi.c
sound/soc/omap/omap-hdmi.h
sound/soc/omap/omap-mcasp.c [new file with mode: 0644]
sound/soc/omap/omap-mcasp.h [new file with mode: 0644]
sound/soc/omap/omap-mcpdm.c
sound/soc/omap/omap-twl4030.c
sound/soc/omap/omap3pandora.c
sound/soc/omap/sdp3430.c [deleted file]
sound/soc/omap/zoom2.c [deleted file]
sound/soc/pxa/palm27x.c
sound/soc/samsung/i2s.c
sound/soc/samsung/i2s.h
sound/soc/samsung/smdk_wm8580.c
sound/soc/samsung/smdk_wm8994.c
sound/soc/sh/fsi.c
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-fw.c [new file with mode: 0644]
sound/soc/soc-pcm.c
sound/soc/tegra/Kconfig
sound/soc/tegra/Makefile
sound/soc/tegra/tegra20_ac97.c [new file with mode: 0644]
sound/soc/tegra/tegra20_ac97.h [new file with mode: 0644]
sound/soc/tegra/tegra20_das.c
sound/soc/tegra/tegra30_ahub.c
sound/soc/tegra/tegra30_i2s.c
sound/soc/tegra/tegra_asoc_utils.c
sound/soc/tegra/tegra_asoc_utils.h
sound/soc/tegra/tegra_wm9712.c [new file with mode: 0644]

index 8732d4d41f8ba538ce708e6c40677b1560bdeb90..62d4f2cda602dca3f3cc6075d9086f7c89e7d7a0 100644 (file)
@@ -18,6 +18,8 @@ Optional properties:
 - ti,timer-pwm:        Indicates the timer can generate a PWM output.
 - ti,timer-secure:     Indicates the timer is reserved on a secure OMAP device
                        and therefore cannot be used by the kernel.
+- ti,timer-non-wkup    Indicates the timer is in non-wkup power domain and hence
+                       will lose register context when the power domain transitions
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/clock/ti-clock-alias.txt b/Documentation/devicetree/bindings/clock/ti-clock-alias.txt
new file mode 100644 (file)
index 0000000..87ef4c3
--- /dev/null
@@ -0,0 +1,26 @@
+* Clock alias provision for TI OMAP2+ boards
+
+This binding allows the board's device tree file to specify a clock name,
+device phandle and clock alias so that that clock can be associated
+to the device with the alias.
+
+This is required in cases where an external device is clocked by an
+OMAP generated clock and needs to be assocated to it.
+
+NOTE: The node's name should be clock_alias
+
+Required properties
+- clock-name: The clock identifier string. Should be one of the
+  clock ids defined in OMAP common clock data.
+- clock-alias: A string specifying the alias that must be created to the clock.
+- device: A phandle to the device this clock should be associated to.
+
+e.g. On the OMAP4 Panda board, the USB PHY device is clocked by the
+FREF_CLK3 (auxclk3_ck) from the OMAP. The PHY driver expexts the clock to
+be named "main_clk". This binding can be provided like so
+
+clock_alias {
+       clock-name = "auxclk3_ck";
+       clock-alias = "main_clk";
+       device = <&hsusb1_phy>;
+};
diff --git a/Documentation/devicetree/bindings/crypto/omap-aes.txt b/Documentation/devicetree/bindings/crypto/omap-aes.txt
new file mode 100644 (file)
index 0000000..6b21256
--- /dev/null
@@ -0,0 +1,37 @@
+OMAP SoC AES crypto Module
+
+Required properties:
+
+- compatible : Should contain entries for this and backward compatible
+  AES versions:
+  - "ti,omap2-aes" for OMAP2.
+  - "ti,omap3-aes" for OMAP3.
+  - "ti,omap4-aes" for OMAP4 and AM33XX.
+  Note that the OMAP2 and 3 versions are compatible (OMAP3 supports
+  more algorithms) but they are incompatible with OMAP4.
+- ti,hwmods: Name of the hwmod associated with the AES odule
+- reg : Offset and length of the register set for the module
+- interrupt-parent : the phandle for the interrupt controller that
+  services interrupts for this module.
+- interrupts : the interrupt number for the AES odule.
+
+Optional properties:
+- dmas: DMA controller phandle and DMA request ordered pairs.
+- dma-names: DMA request names. This string corresponds 1:1 with
+       the ordered pairs in dmas. The string naming is to be
+       "tx" for TX request and "rx" for RX request.
+
+Example:
+       /* AM335x */
+       aes: aes@53500000 {
+               compatible = "ti,omap4-aes";
+               ti,hwmods = "aes";
+               #address-cells = <1>;
+               #size-cells = <0>;
+               reg = <0x53500000 0xa0>;
+               interrupt-parent = <&intc>;
+               interrupts = <102>;
+               dmas = <&edma 6
+                       &edma 5>;
+               dma-names = "tx", "rx";
+       };
diff --git a/Documentation/devicetree/bindings/crypto/omap-sham.txt b/Documentation/devicetree/bindings/crypto/omap-sham.txt
new file mode 100644 (file)
index 0000000..53839cc
--- /dev/null
@@ -0,0 +1,35 @@
+OMAP SoC SHA crypto Module
+
+Required properties:
+
+- compatible : Should contain entries for this and backward compatible
+  SHAM versions:
+  - "ti,omap2-sham" for OMAP2 & OMAP3.
+  - "ti,omap4-sham" for OMAP4 and AM33XX.
+  Note that these two versions are incompatible.
+- ti,hwmods: Name of the hwmod associated with the SHAM module
+- reg : Offset and length of the register set for the module
+- interrupt-parent : the phandle for the interrupt controller that
+  services interrupts for this module.
+- interrupts : the interrupt number for the SHAM module.
+
+Optional properties:
+- dmas: DMA controller phandle and DMA request ordered pair.
+       Only one rx pair is valid per SHAM module.
+- dma-names: DMA request name. This string corresponds 1:1 with
+       the ordered pair in dmas. The string naming is to be
+       "rx" for RX request.
+
+Example:
+       /* AM335x */
+       sham: sham@53100000 {
+               compatible = "ti,omap4-sham";
+               ti,hwmods = "sham";
+               #address-cells = <1>;
+               #size-cells = <0>;
+               reg = <0x53100000 0x200>;
+               interrupt-parent = <&intc>;
+               interrupts = <109>;
+               dmas = <&edma 36>;
+               dma-names = "rx";
+       };
diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt
new file mode 100644 (file)
index 0000000..8f504e6
--- /dev/null
@@ -0,0 +1,81 @@
+* Generic DMA Controller and DMA request bindings
+
+Generic binding to provide a way for a driver using DMA Engine to retrieve the
+DMA request or channel information that goes from a hardware device to a DMA
+controller.
+
+
+* DMA controller
+
+Required property:
+- #dma-cells:          Must be at least 1. Used to provide DMA controller
+                       specific information. See DMA client binding below for
+                       more details.
+
+Optional properties:
+- dma-channels:        Number of DMA channels supported by the controller.
+- dma-requests:        Number of DMA requests signals supported by the
+                       controller.
+
+Example:
+
+       dma: dma@48000000 {
+               compatible = "ti,omap-sdma";
+               reg = <0x48000000 0x1000>;
+               interrupts = <0 12 0x4
+                             0 13 0x4
+                             0 14 0x4
+                             0 15 0x4>;
+               #dma-cells = <1>;
+               dma-channels = <32>;
+               dma-requests = <127>;
+       };
+
+
+* DMA client
+
+Client drivers should specify the DMA property using a phandle to the controller
+followed by DMA controller specific data.
+
+Required property:
+- dmas:                        List of one or more DMA specifiers, each consisting of
+                       - A phandle pointing to DMA controller node
+                       - A number of integer cells, as determined by the
+                         #dma-cells property in the node referenced by phandle
+                         containing DMA controller specific information. This
+                         typically contains a DMA request line number or a
+                         channel number, but can contain any data that is used
+                         required for configuring a channel.
+- dma-names:           Contains one identifier string for each DMA specifier in
+                       the dmas property. The specific strings that can be used
+                       are defined in the binding of the DMA client device.
+                       Multiple DMA specifiers can be used to represent
+                       alternatives and in this case the dma-names for those
+                       DMA specifiers must be identical (see examples).
+
+Examples:
+
+1. A device with one DMA read channel, one DMA write channel:
+
+       i2c1: i2c@1 {
+               ...
+               dmas = <&dma 2          /* read channel */
+                       &dma 3>;        /* write channel */
+               dma-names = "rx", "tx";
+               ...
+       };
+
+2. A single read-write channel with three alternative DMA controllers:
+
+       dmas = <&dma1 5
+               &dma2 7
+               &dma3 2>;
+       dma-names = "rx-tx", "rx-tx", "rx-tx";
+
+3. A device with three channels, one of which has two alternatives:
+
+       dmas = <&dma1 2                 /* read channel */
+               &dma1 3                 /* write channel */
+               &dma2 0                 /* error read */
+               &dma3 0>;               /* alternative error read */
+       dma-names = "rx", "tx", "error", "error";
index c0d85dbcada5ce99462f607a479f21a974e477a2..5bb3dfb6f1d87dd1896eb601fc0819ca1e53c871 100644 (file)
@@ -6,6 +6,26 @@ Required properties:
 - interrupt-parent: Should be the phandle for the interrupt controller
   that services interrupts for this device
 - interrupt: Should contain the DMAC interrupt number
+- nr_channels: Number of channels supported by hardware
+- is_private: The device channels should be marked as private and not for by the
+  general purpose DMA channel allocator. False if not passed.
+- chan_allocation_order: order of allocation of channel, 0 (default): ascending,
+  1: descending
+- chan_priority: priority of channels. 0 (default): increase from chan 0->n, 1:
+  increase from chan n->0
+- block_size: Maximum block size supported by the controller
+- nr_masters: Number of AHB masters supported by the controller
+- data_width: Maximum data width supported by hardware per AHB master
+  (0 - 8bits, 1 - 16bits, ..., 5 - 256bits)
+- slave_info:
+       - bus_id: name of this device channel, not just a device name since
+         devices may have more than one channel e.g. "foo_tx". For using the
+         dw_generic_filter(), slave drivers must pass exactly this string as
+         param to filter function.
+       - cfg_hi: Platform-specific initializer for the CFG_HI register
+       - cfg_lo: Platform-specific initializer for the CFG_LO register
+       - src_master: src master for transfers on allocated channel.
+       - dst_master: dest master for transfers on allocated channel.
 
 Example:
 
@@ -14,4 +34,28 @@ Example:
                reg = <0xfc000000 0x1000>;
                interrupt-parent = <&vic1>;
                interrupts = <12>;
+
+               nr_channels = <8>;
+               chan_allocation_order = <1>;
+               chan_priority = <1>;
+               block_size = <0xfff>;
+               nr_masters = <2>;
+               data_width = <3 3 0 0>;
+
+               slave_info {
+                       uart0-tx {
+                               bus_id = "uart0-tx";
+                               cfg_hi = <0x4000>;      /* 0x8 << 11 */
+                               cfg_lo = <0>;
+                               src_master = <0>;
+                               dst_master = <1>;
+                       };
+                       spi0-tx {
+                               bus_id = "spi0-tx";
+                               cfg_hi = <0x2000>;      /* 0x4 << 11 */
+                               cfg_lo = <0>;
+                               src_master = <0>;
+                               dst_master = <0>;
+                       };
+               };
        };
diff --git a/Documentation/devicetree/bindings/dma/ti-edma.txt b/Documentation/devicetree/bindings/dma/ti-edma.txt
new file mode 100644 (file)
index 0000000..075a60e
--- /dev/null
@@ -0,0 +1,49 @@
+TI EDMA
+
+Required properties:
+- compatible : "ti,edma3"
+- ti,hwmods: Name of the hwmods associated to the EDMA
+- ti,edma-regions: Number of regions
+- ti,edma-slots: Number of slots
+- ti,edma-queue-tc-map: List of transfer control to queue mappings
+- ti,edma-queue-priority-map: List of queue priority mappings
+- ti,edma-default-queue: Default queue value
+
+Optional properties:
+- ti,edma-reserved-channels: List of reserved channel regions
+- ti,edma-reserved-slots: List of reserved slot regions
+- ti,edma-xbar-event-map: Crossbar event to channel map
+
+Example:
+
+edma: edma@49000000 {
+       reg = <0x49000000 0x10000>;
+       interrupt-parent = <&intc>;
+       interrupts = <12 13 14>;
+       compatible = "ti,edma3";
+       ti,hwmods = "tpcc", "tptc0", "tptc1", "tptc2";
+       #dma-cells = <1>;
+       dma-channels = <64>;
+       ti,edma-regions = <4>;
+       ti,edma-slots = <256>;
+       ti,edma-reserved-channels = <0  2
+                                    14 2
+                                    26 6
+                                    48 4
+                                    56 8>;
+       ti,edma-reserved-slots = <0  2
+                                 14 2
+                                 26 6
+                                 48 4
+                                 56 8
+                                 64 127>;
+       ti,edma-queue-tc-map = <0 0
+                               1 1
+                               2 2>;
+       ti,edma-queue-priority-map = <0 0
+                                     1 1
+                                     2 2>;
+       ti,edma-default-queue = <0>;
+       ti,edma-xbar-event-map = <1 12
+                                 2 13>;
+};
diff --git a/Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt b/Documentation/devicetree/bindings/mailbox/dbx500-mailbox.txt
new file mode 100644 (file)
index 0000000..df0f594
--- /dev/null
@@ -0,0 +1,27 @@
+ST-Ericsson DBx500 PRCM Mailbox Driver
+
+Required properties:
+- compatible           : Should be
+                         "stericsson,db8500-mailbox" for db8500 and db9500
+                         "stericsson,db9540-mailbox" for db9540
+- reg                  : Physical base address and length of mailbox's
+                         registers and shared memory
+- reg-names            : Should contain the reg names "prcm-reg" and
+                         "prcmu-tcdm"
+- interrupts           : contains the IRQ line for the PRCM mailbox
+- interrupt-names      : Should contain the interrupt name "irq"
+- legacy-offset                : Memory offset in shared mem for legacy mailboxes
+
+Optional properties:
+- upap-offset          : Memory offset in shared mem for upap mailboxes
+
+Examples:
+
+mailbox {
+       compatible = "stericsson,db8500-mailbox";
+       reg = <0x80157000 0x1000>, <0x801B8000 0x2000>;
+       reg-names = "prcm-reg", "prcmu-tcdm";
+       interrupts = <0 47 0x4>;
+       interrupt-names = "irq";
+       legacy-offset = <0xdd4>;
+};
diff --git a/Documentation/devicetree/bindings/mfd/omap-usb-host.txt b/Documentation/devicetree/bindings/mfd/omap-usb-host.txt
new file mode 100644 (file)
index 0000000..b381fa6
--- /dev/null
@@ -0,0 +1,80 @@
+OMAP HS USB Host
+
+Required properties:
+
+- compatible: should be "ti,usbhs-host"
+- reg: should contain one register range i.e. start and length
+- ti,hwmods: must contain "usb_host_hs"
+
+Optional properties:
+
+- num-ports: number of USB ports. Usually this is automatically detected
+  from the IP's revision register but can be overridden by specifying
+  this property. A maximum of 3 ports are supported at the moment.
+
+- portN-mode: String specifying the port mode for port N, where N can be
+  from 1 to 3. If the port mode is not specified, that port is treated
+  as unused. When specified, it must be one of the following.
+       "ehci-phy",
+        "ehci-tll",
+        "ehci-hsic",
+        "ohci-phy-6pin-datse0",
+        "ohci-phy-6pin-dpdm",
+        "ohci-phy-3pin-datse0",
+        "ohci-phy-4pin-dpdm",
+        "ohci-tll-6pin-datse0",
+        "ohci-tll-6pin-dpdm",
+        "ohci-tll-3pin-datse0",
+        "ohci-tll-4pin-dpdm",
+        "ohci-tll-2pin-datse0",
+        "ohci-tll-2pin-dpdm",
+
+- single-ulpi-bypass: Must be present if the controller contains a single
+  ULPI bypass control bit. e.g. OMAP3 silicon <= ES2.1
+
+Required properties if child node exists:
+
+- #address-cells: Must be 1
+- #size-cells: Must be 1
+- ranges: must be present
+
+Properties for children:
+
+The OMAP HS USB Host subsystem contains EHCI and OHCI controllers.
+See Documentation/devicetree/bindings/usb/omap-ehci.txt and
+omap3-ohci.txt
+
+Example for OMAP4:
+
+usbhshost: usbhshost@4a064000 {
+       compatible = "ti,usbhs-host";
+       reg = <0x4a064000 0x800>;
+       ti,hwmods = "usb_host_hs";
+       #address-cells = <1>;
+       #size-cells = <1>;
+       ranges;
+
+       usbhsohci: ohci@4a064800 {
+               compatible = "ti,ohci-omap3", "usb-ohci";
+               reg = <0x4a064800 0x400>;
+               interrupt-parent = <&gic>;
+               interrupts = <0 76 0x4>;
+       };
+
+       usbhsehci: ehci@4a064c00 {
+               compatible = "ti,ehci-omap", "usb-ehci";
+               reg = <0x4a064c00 0x400>;
+               interrupt-parent = <&gic>;
+               interrupts = <0 77 0x4>;
+       };
+};
+
+&usbhshost {
+       port1-mode = "ehci-phy";
+       port2-mode = "ehci-tll";
+       port3-mode = "ehci-phy";
+};
+
+&usbhsehci {
+       phys = <&hsusb1_phy 0 &hsusb3_phy>;
+};
diff --git a/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt b/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt
new file mode 100644 (file)
index 0000000..62fe697
--- /dev/null
@@ -0,0 +1,17 @@
+OMAP HS USB Host TLL (Transceiver-Less Interface)
+
+Required properties:
+
+- compatible : should be "ti,usbhs-tll"
+- reg : should contain one register range i.e. start and length
+- interrupts : should contain the TLL module's interrupt
+- ti,hwmod : must contain "usb_tll_hs"
+
+Example:
+
+       usbhstll: usbhstll@4a062000 {
+               compatible = "ti,usbhs-tll";
+               reg = <0x4a062000 0x1000>;
+               interrupts = <78>;
+               ti,hwmods = "usb_tll_hs";
+         };
diff --git a/Documentation/devicetree/bindings/mfd/palmas.txt b/Documentation/devicetree/bindings/mfd/palmas.txt
new file mode 100644 (file)
index 0000000..94a0c12
--- /dev/null
@@ -0,0 +1,67 @@
+Texas Instruments Palmas family
+
+The Palmas familly are Integrated Power Management Chips.
+These chips are connected to an i2c bus.
+
+
+Required properties:
+- compatible : Must be "ti,palmas";
+  For Integrated power-management in the palmas series, twl6035, twl6037,
+  tps65913
+- interrupts : This i2c device has an IRQ line connected to the main SoC
+- interrupt-controller : Since the palmas support several interrupts internally,
+  it is considered as an interrupt controller cascaded to the SoC one.
+- #interrupt-cells = <1>;
+- interrupt-parent : The parent interrupt controller.
+
+Optional node:
+- Child nodes contain in the palmas. The palmas family is made of several
+  variants that support a different number of features.
+  The child nodes will thus depend of the capability of the variant.
+- mux_pad1 if a value is given it will be used for the pad1 mux
+- mux_pad2 if a value us given it will be used for the pad2 mux
+- power_ctrl if a value is given it will be written to the POWER_CTRL register
+
+Example:
+/*
+ * Integrated Power Management Chip Palmas
+ */
+palmas@48 {
+    compatible = "ti,palmas";
+    reg = <0x48>;
+    interrupts = <39>; /* IRQ_SYS_1N cascaded to gic */
+    interrupt-controller;
+    #interrupt-cells = <1>;
+    interrupt-parent = <&gic>;
+    #address-cells = <1>;
+    #size-cells = <0>;
+
+       ti,mux_pad1 = <0x00>;
+       ti,mux_pad2 = <0x00>;
+       ti,power_ctrl = <0x03>;
+
+       palmas_pmic {
+               compatible = "ti,palmas_pmic";
+               regulators {
+                       smps12_reg: smps12 {
+                               regulator-min-microvolt = < 600000>;
+                regulator-max-microvolt = <1500000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                ti,warm_sleep = <0>;
+                ti,roof_floor = <0>;
+                ti,mode_sleep = <0>;
+                ti,warm_reset = <0>;
+                ti,tstep = <0>;
+                ti,vsel = <0>;
+                       };
+               };
+               ti,ldo6_vibrator = <0>;
+       };
+
+    palmas_rtc {
+        compatible = "ti,palmas_rtc";
+        interrupts = <8 9>;
+        reg = <0>;
+    };
+};
index ed271fc255b23c5d3595f1a1725687cb74d077be..826cc51e425fc6fdebece10def05428b38fba5bb 100644 (file)
@@ -20,8 +20,28 @@ ti,dual-volt: boolean, supports dual voltage cards
 ti,non-removable: non-removable slot (like eMMC)
 ti,needs-special-reset: Requires a special softreset sequence
 ti,needs-special-hs-handling: HSMMC IP needs special setting for handling High Speed
+dmas: DMA controller phandle and DMA request value ordered pair
+One tx and one rx pair is required.
+dma-names: DMA request names. These strings correspond 1:1 with
+the ordered pairs in dmas. The RX request must be "rx" and the
+TX request must be "tx".
+
+Examples:
+
+[hwmod populated DMA resources]
+
+       mmc1: mmc@0x4809c000 {
+               compatible = "ti,omap4-hsmmc";
+               reg = <0x4809c000 0x400>;
+               ti,hwmods = "mmc1";
+               ti,dual-volt;
+               bus-width = <4>;
+               vmmc-supply = <&vmmc>; /* phandle to regulator node */
+               ti,non-removable;
+       };
+
+[generic DMA request binding]
 
-Example:
        mmc1: mmc@0x4809c000 {
                compatible = "ti,omap4-hsmmc";
                reg = <0x4809c000 0x400>;
@@ -30,4 +50,7 @@ Example:
                bus-width = <4>;
                vmmc-supply = <&vmmc>; /* phandle to regulator node */
                ti,non-removable;
+               dmas = <&edma 24
+                       &edma 25>;
+               dma-names = "tx", "rx";
        };
index 6ddd0286a9b7514fe00a5f150e05859da0f25e36..4f2ca6b4a182d3f9584c72b05414deacb085aebf 100644 (file)
@@ -15,15 +15,23 @@ Required properties:
 - mac_control          : Specifies Default MAC control register content
                          for the specific platform
 - slaves               : Specifies number for slaves
-- cpts_active_slave    : Specifies the slave to use for time stamping
+- active_slave         : Specifies the slave to use for time stamping,
+                         ethtool and SIOCGMIIPHY
 - cpts_clock_mult      : Numerator to convert input clock ticks into nanoseconds
 - cpts_clock_shift     : Denominator to convert input clock ticks into nanoseconds
-- phy_id               : Specifies slave phy id
-- mac-address          : Specifies slave MAC address
 
 Optional properties:
 - ti,hwmods            : Must be "cpgmac0"
 - no_bd_ram            : Must be 0 or 1
+- dual_emac            : Specifies Switch to act as Dual EMAC
+
+Slave Properties:
+Required properties:
+- phy_id               : Specifies slave phy id
+- mac-address          : Specifies slave MAC address
+
+Optional properties:
+- dual_emac_res_vlan   : Specifies VID to be used to segregate the ports
 
 Note: "ti,hwmods" field is used to fetch the base address and irq
 resources from TI, omap hwmod data base during device registration.
@@ -45,7 +53,7 @@ Examples:
                rx_descs = <64>;
                mac_control = <0x20>;
                slaves = <2>;
-               cpts_active_slave = <0>;
+               active_slave = <0>;
                cpts_clock_mult = <0x80000000>;
                cpts_clock_shift = <29>;
                cpsw_emac0: slave@0 {
@@ -71,7 +79,7 @@ Examples:
                rx_descs = <64>;
                mac_control = <0x20>;
                slaves = <2>;
-               cpts_active_slave = <0>;
+               active_slave = <0>;
                cpts_clock_mult = <0x80000000>;
                cpts_clock_shift = <29>;
                cpsw_emac0: slave@0 {
diff --git a/Documentation/devicetree/bindings/sound/ak4642.txt b/Documentation/devicetree/bindings/sound/ak4642.txt
new file mode 100644 (file)
index 0000000..623d4e7
--- /dev/null
@@ -0,0 +1,17 @@
+AK4642 I2C transmitter
+
+This device supports I2C mode only.
+
+Required properties:
+
+  - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648"
+  - reg : The chip select number on the I2C bus
+
+Example:
+
+&i2c {
+       ak4648: ak4648@0x12 {
+               compatible = "asahi-kasei,ak4642";
+               reg = <0x12>;
+       };
+};
index a850fb9c88eab2156ec89f0c76d8ca59073540f8..e2cd1d7539e527897cc34566508ed56f070a60cb 100644 (file)
@@ -20,6 +20,18 @@ Optional properties:
                !RESET pin
  - cirrus,amuteb-eq-bmutec:    When given, the Codec's AMUTEB=BMUTEC flag
                                is enabled.
+ - cirrus,enable-soft-reset:
+       The CS4271 requires its LRCLK and MCLK to be stable before its RESET
+       line is de-asserted. That also means that clocks cannot be changed
+       without putting the chip back into hardware reset, which also requires
+       a complete re-initialization of all registers.
+
+       One (undocumented) workaround is to assert and de-assert the PDN bit
+       in the MODE2 register. This workaround can be enabled with this DT
+       property.
+
+       Note that this is not needed in case the clocks are stable
+       throughout the entire runtime of the codec.
 
 Examples:
 
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt
new file mode 100644 (file)
index 0000000..be35d34
--- /dev/null
@@ -0,0 +1,51 @@
+NVIDIA Tegra audio complex
+
+Required properties:
+- compatible : "nvidia,tegra-audio-wm9712"
+- nvidia,model : The user-visible name of this sound complex.
+- nvidia,audio-routing : A list of the connections between audio components.
+  Each entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source. Valid names for sources and
+  sinks are the WM9712's pins, and the jacks on the board:
+
+  WM9712 pins:
+
+  * MONOOUT
+  * HPOUTL
+  * HPOUTR
+  * LOUT2
+  * ROUT2
+  * OUT3
+  * LINEINL
+  * LINEINR
+  * PHONE
+  * PCBEEP
+  * MIC1
+  * MIC2
+  * Mic Bias
+
+  Board connectors:
+
+  * Headphone
+  * LineIn
+  * Mic
+
+- nvidia,ac97-controller : The phandle of the Tegra AC97 controller
+
+
+Example:
+
+sound {
+       compatible = "nvidia,tegra-audio-wm9712-colibri_t20",
+                        "nvidia,tegra-audio-wm9712";
+       nvidia,model = "Toradex Colibri T20";
+
+       nvidia,audio-routing =
+               "Headphone", "HPOUTL",
+               "Headphone", "HPOUTR",
+               "LineIn", "LINEINL",
+               "LineIn", "LINEINR",
+               "Mic", "MIC1";
+
+       nvidia,ac97-controller = <&ac97>;
+};
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt
new file mode 100644 (file)
index 0000000..c145497
--- /dev/null
@@ -0,0 +1,22 @@
+NVIDIA Tegra 20 AC97 controller
+
+Required properties:
+- compatible : "nvidia,tegra20-ac97"
+- reg : Should contain AC97 controller registers location and length
+- interrupts : Should contain AC97 interrupt
+- nvidia,dma-request-selector : The Tegra DMA controller's phandle and
+  request selector for the AC97 controller
+- nvidia,codec-reset-gpio : The Tegra GPIO controller's phandle and the number
+  of the GPIO used to reset the external AC97 codec
+- nvidia,codec-sync-gpio : The Tegra GPIO controller's phandle and the number
+  of the GPIO corresponding with the AC97 DAP _FS line
+Example:
+
+ac97@70002000 {
+       compatible = "nvidia,tegra20-ac97";
+       reg = <0x70002000 0x200>;
+       interrupts = <0 81 0x04>;
+       nvidia,dma-request-selector = <&apbdma 12>;
+       nvidia,codec-reset-gpio = <&gpio 170 0>;
+       nvidia,codec-sync-gpio = <&gpio 120 0>;
+};
diff --git a/Documentation/devicetree/bindings/sound/omap-abe-core.txt b/Documentation/devicetree/bindings/sound/omap-abe-core.txt
new file mode 100644 (file)
index 0000000..26750ac
--- /dev/null
@@ -0,0 +1,37 @@
+* Texas Instruments OMAP4+ AESS
+
+Required properties:
+- compatible: "ti,omap-mcpdm"
+- reg: Register location and size as an array:
+       <MPU access base address, size>,
+       <MPU DMEM access base address, size>,
+       <MPU CMEM access base address, size>,
+       <MPU SMEM access base address, size>,
+       <MPU PMEM access base address, size>,
+       <L3 interconnect address, size>;
+       <L3 DMEM access base address, size>,
+       <L3 CMEM access base address, size>,
+       <L3 SMEM access base address, size>,
+       <L3 PMEM access base address, size>,
+- interrupts: Interrupt number for AESS
+- interrupt-parent: The parent interrupt controller
+- ti,hwmods: Name of the hwmod associated to the AESS
+
+Example:
+
+aess: aess@401f1000 {
+       compatible = "ti,omap4-aess";
+       reg = <0x401f1000 0x3ff>, /* MPU private access */
+             <0x40180000 0xffff>, /* DMEM - MPU */
+             <0x401a0000 0x1fff>, /* CMEM - MPU */
+             <0x401ce000 0x5fff>, /* SMEM - MPU */
+             <0x401e0000 0x1fff>, /* PMEM - MPU */
+             <0x4901f000 0x3ff>, /* L3 Interconnect */
+             <0x49080000 0xffff>, /* DMEM - MPU */
+             <0x490a0000 0x1fff>, /* CMEM - MPU */
+             <0x490ce000 0x5fff>, /* SMEM - MPU */
+             <0x490e0000 0x1fff>; /* PMEM - MPU */
+       interrupts = <0 99 0x4>;
+       interrupt-parent = <&gic>;
+       ti,hwmods = "aess";
+};
index 6fae51c7f766fe995ba96627cde3f1bfeb661426..1ab6bc8404d5bc850dea1fc1533f451b03787e5f 100644 (file)
@@ -6,6 +6,52 @@ Required properties:
 - ti,mcbsp: phandle for the McBSP node
 - ti,codec: phandle for the twl4030 audio node
 
+Optional properties:
+- ti,mcbsp-voice: phandle for the McBSP node connected to the voice port of twl
+- ti, jack-det-gpio: Jack detect GPIO
+- ti,audio-routing: List of connections between audio components.
+  Each entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source.
+  If the routing is not provided all possible connection will be available
+
+Available audio endpoints for the audio-routing table:
+
+Board connectors:
+ * Headset Stereophone
+ * Earpiece Spk
+ * Handsfree Spk
+ * Ext Spk
+ * Main Mic
+ * Sub Mic
+ * Headset Mic
+ * Carkit Mic
+ * Digital0 Mic
+ * Digital1 Mic
+ * Line In
+
+twl4030 pins:
+ * HSOL
+ * HSOR
+ * EARPIECE
+ * HFL
+ * HFR
+ * PREDRIVEL
+ * PREDRIVER
+ * CARKITL
+ * CARKITR
+ * MAINMIC
+ * SUBMIC
+ * HSMIC
+ * DIGIMIC0
+ * DIGIMIC1
+ * CARKITMIC
+ * AUXL
+ * AUXR
+
+ * Headset Mic Bias
+ * Mic Bias 1 /* Used for Main Mic or Digimic0 */
+ * Mic Bias 2 /* Used for Sub Mic or Digimic1 */
+
 Example:
 
 sound {
diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.txt b/Documentation/devicetree/bindings/sound/renesas,fsi.txt
new file mode 100644 (file)
index 0000000..c5be003
--- /dev/null
@@ -0,0 +1,26 @@
+Renesas FSI
+
+Required properties:
+- compatible                   : "renesas,sh_fsi2" or "renesas,sh_fsi"
+- reg                          : Should contain the register physical address and length
+- interrupts                   : Should contain FSI interrupt
+
+- fsia,spdif-connection                : FSI is connected by S/PDFI
+- fsia,stream-mode-support     : FSI supports 16bit stream mode.
+- fsia,use-internal-clock      : FSI uses internal clock when master mode.
+
+- fsib,spdif-connection                : same as fsia
+- fsib,stream-mode-support     : same as fsia
+- fsib,use-internal-clock      : same as fsia
+
+Example:
+
+sh_fsi2: sh_fsi2@0xec230000 {
+       compatible = "renesas,sh_fsi2";
+       reg = <0xec230000 0x400>;
+       interrupts = <0 146 0x4>;
+
+       fsia,spdif-connection;
+       fsia,stream-mode-support;
+       fsia,use-internal-clock;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8962.txt b/Documentation/devicetree/bindings/sound/wm8962.txt
new file mode 100644 (file)
index 0000000..dceb3b1
--- /dev/null
@@ -0,0 +1,16 @@
+WM8962 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+  - compatible : "wlf,wm8962"
+
+  - reg : the I2C address of the device.
+
+Example:
+
+codec: wm8962@1a {
+       compatible = "wlf,wm8962";
+       reg = <0x1a>;
+};
index 938809c6829bfdffd6b14af8005a60c2e7e003be..4c85c4c69584a14d39dd8597facb920b4b69cf46 100644 (file)
@@ -10,7 +10,18 @@ Required properties:
                          input. The default is D0 as input and
                          D1 as output.
 
-Example:
+Optional properties:
+- dmas: List of DMA specifiers with the controller specific format
+       as described in the generic DMA client binding. A tx and rx
+       specifier is required for each chip select.
+- dma-names: List of DMA request names. These strings correspond
+       1:1 with the DMA specifiers listed in dmas. The string naming
+       is to be "rxN" and "txN" for RX and TX requests,
+       respectively, where N equals the chip select number.
+
+Examples:
+
+[hwmod populated DMA resources]
 
 mcspi1: mcspi@1 {
     #address-cells = <1>;
@@ -20,3 +31,17 @@ mcspi1: mcspi@1 {
     ti,spi-num-cs = <4>;
 };
 
+[generic DMA request binding]
+
+mcspi1: mcspi@1 {
+    #address-cells = <1>;
+    #size-cells = <0>;
+    compatible = "ti,omap4-mcspi";
+    ti,hwmods = "mcspi1";
+    ti,spi-num-cs = <2>;
+    dmas = <&edma 42
+           &edma 43
+           &edma 44
+           &edma 45>;
+    dma-names = "tx0", "rx0", "tx1", "rx1";
+};
diff --git a/Documentation/devicetree/bindings/thermal/dove-thermal.txt b/Documentation/devicetree/bindings/thermal/dove-thermal.txt
new file mode 100644 (file)
index 0000000..6f47467
--- /dev/null
@@ -0,0 +1,18 @@
+* Dove Thermal
+
+This driver is for Dove SoCs which contain a thermal sensor.
+
+Required properties:
+- compatible : "marvell,dove-thermal"
+- reg : Address range of the thermal registers
+
+The reg properties should contain two ranges. The first is for the
+three Thermal Manager registers, while the second range contains the
+Thermal Diode Control Registers.
+
+Example:
+
+       thermal@10078 {
+               compatible = "marvell,dove-thermal";
+               reg = <0xd001c 0x0c>, <0xd005c 0x08>;
+       };
diff --git a/Documentation/devicetree/bindings/thermal/kirkwood-thermal.txt b/Documentation/devicetree/bindings/thermal/kirkwood-thermal.txt
new file mode 100644 (file)
index 0000000..8c0f5eb
--- /dev/null
@@ -0,0 +1,15 @@
+* Kirkwood Thermal
+
+This version is for Kirkwood 88F8262 & 88F6283 SoCs. Other kirkwoods
+don't contain a thermal sensor.
+
+Required properties:
+- compatible : "marvell,kirkwood-thermal"
+- reg : Address range of the thermal registers
+
+Example:
+
+       thermal@10078 {
+               compatible = "marvell,kirkwood-thermal";
+               reg = <0x10078 0x4>;
+       };
diff --git a/Documentation/devicetree/bindings/thermal/rcar-thermal.txt b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt
new file mode 100644 (file)
index 0000000..28ef498
--- /dev/null
@@ -0,0 +1,29 @@
+* Renesas R-Car Thermal
+
+Required properties:
+- compatible           : "renesas,rcar-thermal"
+- reg                  : Address range of the thermal registers.
+                         The 1st reg will be recognized as common register
+                         if it has "interrupts".
+
+Option properties:
+
+- interrupts           : use interrupt
+
+Example (non interrupt support):
+
+thermal@e61f0100 {
+       compatible = "renesas,rcar-thermal";
+       reg = <0xe61f0100 0x38>;
+};
+
+Example (interrupt support):
+
+thermal@e61f0000 {
+       compatible = "renesas,rcar-thermal";
+       reg = <0xe61f0000 0x14
+               0xe61f0100 0x38
+               0xe61f0200 0x38
+               0xe61f0300 0x38>;
+       interrupts = <0 69 4>;
+};
diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
new file mode 100644 (file)
index 0000000..7a95c65
--- /dev/null
@@ -0,0 +1,22 @@
+synopsys DWC3 CORE
+
+DWC3- USB3 CONTROLLER
+
+Required properties:
+ - compatible: must be "synopsys,dwc3"
+ - reg : Address and length of the register set for the device
+ - interrupts: Interrupts used by the dwc3 controller.
+ - usb-phy : array of phandle for the PHY device
+
+Optional properties:
+ - tx-fifo-resize: determines if the FIFO *has* to be reallocated.
+
+This is usually a subnode to DWC3 glue to which it is connected.
+
+dwc3@4a030000 {
+       compatible = "synopsys,dwc3";
+       reg = <0x4a030000 0xcfff>;
+       interrupts = <0 92 4>
+       usb-phy = <&usb2_phy>, <&usb3,phy>;
+       tx-fifo-resize;
+};
diff --git a/Documentation/devicetree/bindings/usb/omap-ehci.txt b/Documentation/devicetree/bindings/usb/omap-ehci.txt
new file mode 100644 (file)
index 0000000..b00a654
--- /dev/null
@@ -0,0 +1,34 @@
+OMAP HS USB EHCI controller
+
+This device is usually the child of the omap-usb-host
+Documentation/devicetree/bindings/mfd/omap-usb-host.txt
+
+Required properties:
+
+- compatible: should be "ti,ehci-omap"
+- reg: should contain one register range i.e. start and length
+- interrupt-parent: phandle to the interrupt controller
+- interrupts: description of the interrupt line
+
+Optional properties:
+
+- phys: list of phandles to PHY nodes.
+  This property is required if at least one of the ports are in
+  PHY mode i.e. OMAP_EHCI_PORT_MODE_PHY
+
+To specify the port mode, see
+Documentation/devicetree/bindings/mfd/omap-usb-host.txt
+
+Example for OMAP4:
+
+usbhsehci: ehci@4a064c00 {
+       compatible = "ti,ehci-omap", "usb-ehci";
+       reg = <0x4a064c00 0x400>;
+       interrupt-parent = <&gic>;
+       interrupts = <0 77 0x4>;
+};
+
+&usbhsehci {
+       phys = <&hsusb1_phy 0 &hsusb3_phy>;
+};
+
index 29a043ecda523d35e5a1becba9c4e9e748ef781e..d4769f343d6cfbdaeb5ba16880d450e339145d6b 100644 (file)
@@ -1,33 +1,94 @@
-OMAP GLUE
+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
+ - num-eps : Specifies the number of endpoints. This is also a
    MUSB configuration-specific setting. Should be set to "16"
- - ram_bits : Specifies the ram address size. Should be set to "12"
- - interface_type : This is a board specific setting to describe the type of
+ - ram-bits : Specifies the ram address size. Should be set to "12"
+ - interface-type : This is a board specific setting to describe the type of
    interface between the controller and the phy. It should be "0" or "1"
    specifying ULPI and UTMI respectively.
  - mode : Should be "3" to represent OTG. "1" signifies HOST and "2"
    represents PERIPHERAL.
  - power : Should be "50". This signifies the controller can supply upto
    100mA when operating in host mode.
+ - usb-phy : the phandle for the PHY device
+
+Optional properties:
+ - ctrl-module : phandle of the control module this glue uses to write to
+   mailbox
 
 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>;
+       num-eps = <16>;
+       ram-bits = <12>;
+       ctrl-module = <&omap_control_usb>;
 };
 
 Board specific device node entry
 &usb_otg_hs {
-       interface_type = <1>;
+       interface-type = <1>;
        mode = <3>;
        power = <50>;
 };
+
+OMAP DWC3 GLUE
+ - compatible : Should be "ti,dwc3"
+ - ti,hwmods : Should be "usb_otg_ss"
+ - reg : Address and length of the register set for the device.
+ - interrupts : The irq number of this device that is used to interrupt the
+   MPU
+ - #address-cells, #size-cells : Must be present if the device has sub-nodes
+ - utmi-mode : controls the source of UTMI/PIPE status for VBUS and OTG ID.
+   It should be set to "1" for HW mode and "2" for SW mode.
+ - ranges: the child address space are mapped 1:1 onto the parent address space
+
+Sub-nodes:
+The dwc3 core should be added as subnode to omap dwc3 glue.
+- dwc3 :
+   The binding details of dwc3 can be found in:
+   Documentation/devicetree/bindings/usb/dwc3.txt
+
+omap_dwc3 {
+       compatible = "ti,dwc3";
+       ti,hwmods = "usb_otg_ss";
+       reg = <0x4a020000 0x1ff>;
+       interrupts = <0 93 4>;
+       #address-cells = <1>;
+       #size-cells = <1>;
+       utmi-mode = <2>;
+       ranges;
+};
+
+OMAP CONTROL USB
+
+Required properties:
+ - compatible: Should be "ti,omap-control-usb"
+ - 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.
+
+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>;
+};
diff --git a/Documentation/devicetree/bindings/usb/omap3-ohci.txt b/Documentation/devicetree/bindings/usb/omap3-ohci.txt
new file mode 100644 (file)
index 0000000..ad2ace0
--- /dev/null
@@ -0,0 +1,17 @@
+OMAP HS USB OHCI controller (OMAP3 and later)
+
+Required properties:
+
+- compatible: should be "ti,ohci-omap3"
+- reg: should contain one register range i.e. start and length
+- interrupt-parent: phandle to the interrupt controller
+- interrupts: description of the interrupt line
+
+Example for OMAP4:
+
+usbhsohci: ohci@4a064800 {
+       compatible = "ti,ohci-omap3", "usb-ohci";
+       reg = <0x4a064800 0x400>;
+       interrupt-parent = <&gic>;
+       interrupts = <0 76 0x4>;
+};
index 36b9aede3f40d04b4b173ffede953b3b226135ea..fa037ad710644cc91fe30586a920bc8799363d14 100644 (file)
@@ -38,3 +38,27 @@ twl4030-usb {
        usb3v1-supply = <&vusb3v1>;
        usb_mode = <1>;
 };
+
+PALMAS USB COMPARATOR
+Required Properties:
+ - compatible : Should be "ti,palmas-usb"
+ - interrupts : Four interrupt numbers to the cpu should be specified. First
+   interrupt number is the otg interrupt number that raises ID interrupts when
+   the controller has to act as host and the second interrupt number is the
+   ID wakeup interrupt number, third interrupt is VBUS interrupt and fourth
+   interrupt is VBUS wakeup interrupt.
+-  interrupt-name : the name corresponding to each interrupt populated in
+   interrupts property.
+ - vbus-supply : phandle to the regulator device tree node.
+
+Optional Properties:
+ - ti,wakeup : To enable the wakeup comparator in probe
+ - ti,no_control_vbus: if the platform wishes its own vbus control
+
+palmas-usb {
+       compatible = "ti,palmas-usb";
+       interrupts = <20 0>, <21 0>, <22 0>, <23 0>;
+       interrupt-names = "ID", "ID_WAKEUP", "VBUS", "VBUS_WAKEUP";
+       vbus-supply = <&smps10_reg>;
+       ti,wakeup;
+};
diff --git a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt
new file mode 100644 (file)
index 0000000..d7e2726
--- /dev/null
@@ -0,0 +1,34 @@
+USB NOP PHY
+
+Required properties:
+- compatible: should be usb-nop-xceiv
+
+Optional properties:
+- clocks: phandle to the PHY clock. Use as per Documentation/devicetree
+  /bindings/clock/clock-bindings.txt
+  This property is required if clock-frequency is specified.
+
+- clock-names: Should be "main_clk"
+
+- clock-frequency: the clock frequency (in Hz) that the PHY clock must
+  be configured to.
+
+- vcc-supply: phandle to the regulator that provides RESET to the PHY.
+
+- reset-supply: phandle to the regulator that provides power to the PHY.
+
+Example:
+
+       hsusb1_phy {
+               compatible = "usb-nop-xceiv";
+               clock-frequency = <19200000>;
+               clocks = <&osc 0>;
+               clock-names = "main_clk";
+               vcc-supply = <&hsusb1_vcc_regulator>;
+               reset-supply = <&hsusb1_reset_regulator>;
+       };
+
+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.
index 80d4148cb6616fb29d9d5dc3a09fe365232f5779..61496f5cb095b39c149705e7f80c3fba379c434d 100644 (file)
@@ -4,14 +4,39 @@ OMAP USB2 PHY
 
 Required properties:
  - compatible: Should be "ti,omap-usb2"
- - reg : Address and length of the register set for the device. Also
-add the address of control module dev conf register until a driver for
-control module is added
+ - reg : Address and length of the register set for the device.
+
+Optional properties:
+ - ctrl-module : phandle of the control module used by PHY driver to power on
+   the PHY.
 
 This is usually a subnode of ocp2scp to which it is connected.
 
 usb2phy@4a0ad080 {
        compatible = "ti,omap-usb2";
-       reg = <0x4a0ad080 0x58>,
-             <0x4a002300 0x4>;
+       reg = <0x4a0ad080 0x58>;
+       ctrl-module = <&omap_control_usb>;
+};
+
+OMAP USB3 PHY
+
+Required properties:
+ - compatible: Should be "ti,omap-usb3"
+ - 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".
+
+Optional properties:
+ - ctrl-module : phandle of the control module used by PHY driver to power on
+   the PHY.
+
+This is usually a subnode of ocp2scp to which it is connected.
+
+usb3phy@4a084400 {
+       compatible = "ti,omap-usb3";
+       reg = <0x4a084400 0x80>,
+             <0x4a084800 0x64>,
+             <0x4a084c00 0x40>;
+       reg-names = "phy_rx", "phy_tx", "pll_ctrl";
+       ctrl-module = <&omap_control_usb>;
 };
index 89a339c9b079160429bb43f133049e83ebfe7f4d..0686c9e211c2828526a43549a871af11ae92f3e4 100644 (file)
@@ -17,10 +17,12 @@ HCI
 HCI registers as an nfc device with NFC Core. Requests coming from userspace are
 routed through netlink sockets to NFC Core and then to HCI. From this point,
 they are translated in a sequence of HCI commands sent to the HCI layer in the
-host controller (the chip). The sending context blocks while waiting for the
-response to arrive.
+host controller (the chip). Commands can be executed synchronously (the sending
+context blocks waiting for response) or asynchronously (the response is returned
+from HCI Rx context).
 HCI events can also be received from the host controller. They will be handled
-and a translation will be forwarded to NFC Core as needed.
+and a translation will be forwarded to NFC Core as needed. There are hooks to
+let the HCI driver handle proprietary events or override standard behavior.
 HCI uses 2 execution contexts:
 - one for executing commands : nfc_hci_msg_tx_work(). Only one command
 can be executing at any given moment.
@@ -33,6 +35,8 @@ The Session initialization is an HCI standard which must unfortunately
 support proprietary gates. This is the reason why the driver will pass a list
 of proprietary gates that must be part of the session. HCI will ensure all
 those gates have pipes connected when the hci device is set up.
+In case the chip supports pre-opened gates and pseudo-static pipes, the driver
+can pass that information to HCI core.
 
 HCI Gates and Pipes
 -------------------
@@ -46,6 +50,13 @@ without knowing the pipe connected to it.
 Driver interface
 ----------------
 
+A driver is generally written in two parts : the physical link management and
+the HCI management. This makes it easier to maintain a driver for a chip that
+can be connected using various phy (i2c, spi, ...)
+
+HCI Management
+--------------
+
 A driver would normally register itself with HCI and provide the following
 entry points:
 
@@ -53,58 +64,113 @@ struct nfc_hci_ops {
        int (*open)(struct nfc_hci_dev *hdev);
        void (*close)(struct nfc_hci_dev *hdev);
        int (*hci_ready) (struct nfc_hci_dev *hdev);
-       int (*xmit)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
-       int (*start_poll)(struct nfc_hci_dev *hdev, u32 protocols);
-       int (*target_from_gate)(struct nfc_hci_dev *hdev, u8 gate,
-                               struct nfc_target *target);
+       int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
+       int (*start_poll) (struct nfc_hci_dev *hdev,
+                          u32 im_protocols, u32 tm_protocols);
+       int (*dep_link_up)(struct nfc_hci_dev *hdev, struct nfc_target *target,
+                          u8 comm_mode, u8 *gb, size_t gb_len);
+       int (*dep_link_down)(struct nfc_hci_dev *hdev);
+       int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate,
+                                struct nfc_target *target);
        int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
                                           struct nfc_target *target);
-       int (*data_exchange) (struct nfc_hci_dev *hdev,
-                             struct nfc_target *target,
-                             struct sk_buff *skb, struct sk_buff **res_skb);
+       int (*im_transceive) (struct nfc_hci_dev *hdev,
+                             struct nfc_target *target, struct sk_buff *skb,
+                             data_exchange_cb_t cb, void *cb_context);
+       int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_hci_dev *hdev,
                              struct nfc_target *target);
+       int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
+                             struct sk_buff *skb);
 };
 
 - open() and close() shall turn the hardware on and off.
 - hci_ready() is an optional entry point that is called right after the hci
 session has been set up. The driver can use it to do additional initialization
 that must be performed using HCI commands.
-- xmit() shall simply write a frame to the chip.
+- xmit() shall simply write a frame to the physical link.
 - start_poll() is an optional entrypoint that shall set the hardware in polling
 mode. This must be implemented only if the hardware uses proprietary gates or a
 mechanism slightly different from the HCI standard.
+- dep_link_up() is called after a p2p target has been detected, to finish
+the p2p connection setup with hardware parameters that need to be passed back
+to nfc core.
+- dep_link_down() is called to bring the p2p link down.
 - target_from_gate() is an optional entrypoint to return the nfc protocols
 corresponding to a proprietary gate.
 - complete_target_discovered() is an optional entry point to let the driver
 perform additional proprietary processing necessary to auto activate the
 discovered target.
-- data_exchange() must be implemented by the driver if proprietary HCI commands
+- im_transceive() must be implemented by the driver if proprietary HCI commands
 are required to send data to the tag. Some tag types will require custom
 commands, others can be written to using the standard HCI commands. The driver
 can check the tag type and either do proprietary processing, or return 1 to ask
-for standard processing.
+for standard processing. The data exchange command itself must be sent
+asynchronously.
+- tm_send() is called to send data in the case of a p2p connection
 - check_presence() is an optional entry point that will be called regularly
 by the core to check that an activated tag is still in the field. If this is
 not implemented, the core will not be able to push tag_lost events to the user
 space
+- event_received() is called to handle an event coming from the chip. Driver
+can handle the event or return 1 to let HCI attempt standard processing.
 
 On the rx path, the driver is responsible to push incoming HCP frames to HCI
 using nfc_hci_recv_frame(). HCI will take care of re-aggregation and handling
 This must be done from a context that can sleep.
 
-SHDLC
------
+PHY Management
+--------------
+
+The physical link (i2c, ...) management is defined by the following struture:
+
+struct nfc_phy_ops {
+       int (*write)(void *dev_id, struct sk_buff *skb);
+       int (*enable)(void *dev_id);
+       void (*disable)(void *dev_id);
+};
+
+enable(): turn the phy on (power on), make it ready to transfer data
+disable(): turn the phy off
+write(): Send a data frame to the chip. Note that to enable higher
+layers such as an llc to store the frame for re-emission, this function must
+not alter the skb. It must also not return a positive result (return 0 for
+success, negative for failure).
+
+Data coming from the chip shall be sent directly to nfc_hci_recv_frame().
+
+LLC
+---
+
+Communication between the CPU and the chip often requires some link layer
+protocol. Those are isolated as modules managed by the HCI layer. There are
+currently two modules : nop (raw transfert) and shdlc.
+A new llc must implement the following functions:
+
+struct nfc_llc_ops {
+       void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+                      rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                      int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+                      llc_failure_t llc_failure);
+       void (*deinit) (struct nfc_llc *llc);
+       int (*start) (struct nfc_llc *llc);
+       int (*stop) (struct nfc_llc *llc);
+       void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb);
+       int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb);
+};
+
+- init() : allocate and init your private storage
+- deinit() : cleanup
+- start() : establish the logical connection
+- stop () : terminate the logical connection
+- rcv_from_drv() : handle data coming from the chip, going to HCI
+- xmit_from_hci() : handle data sent by HCI, going to the chip
 
-Most chips use shdlc to ensure integrity and delivery ordering of the HCP
-frames between the host controller (the chip) and hosts (entities connected
-to the chip, like the cpu). In order to simplify writing the driver, an shdlc
-layer is available for use by the driver.
-When used, the driver actually registers with shdlc, and shdlc will register
-with HCI. HCI sees shdlc as the driver and thus send its HCP frames
-through shdlc->xmit.
-SHDLC adds a new execution context (nfc_shdlc_sm_work()) to run its state
-machine and handle both its rx and tx path.
+The llc must be registered with nfc before it can be used. Do that by
+calling nfc_llc_register(const char *name, struct nfc_llc_ops *ops);
+
+Again, note that the llc does not handle the physical link. It is thus very
+easy to mix any physical link with any llc for a given chip driver.
 
 Included Drivers
 ----------------
@@ -117,10 +183,12 @@ Execution Contexts
 
 The execution contexts are the following:
 - IRQ handler (IRQH):
-fast, cannot sleep. stores incoming frames into an shdlc rx queue
+fast, cannot sleep. sends incoming frames to HCI where they are passed to
+the current llc. In case of shdlc, the frame is queued in shdlc rx queue.
 
 - SHDLC State Machine worker (SMW)
-handles shdlc rx & tx queues. Dispatches HCI cmd responses.
+Only when llc_shdlc is used: handles shdlc rx & tx queues.
+Dispatches HCI cmd responses.
 
 - HCI Tx Cmd worker (MSGTXWQ)
 Serializes execution of HCI commands. Completes execution in case of response
@@ -166,6 +234,15 @@ waiting command execution. Response processing involves invoking the completion
 callback that was provided by nfc_hci_msg_tx_work() when it sent the command.
 The completion callback will then wake the syscall context.
 
+It is also possible to execute the command asynchronously using this API:
+
+static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
+                              const u8 *param, size_t param_len,
+                              data_exchange_cb_t cb, void *cb_context)
+
+The workflow is the same, except that the API call returns immediately, and
+the callback will be called with the result from the SMW context.
+
 Workflow receiving an HCI event or command
 ------------------------------------------
 
index 2fcac9f5996e32386459c1c066c2447227d79a15..b36ca14ca2d65a0e974228622f6ea2cba45cdb54 100644 (file)
@@ -1,32 +1,15 @@
 Kernel driver for the NXP Semiconductors PN544 Near Field
 Communication chip
 
-Author: Jari Vanhala
-Contact: Matti Aaltonen (matti.j.aaltonen at nokia.com)
-
 General
 -------
 
 The PN544 is an integrated transmission module for contactless
 communication. The driver goes under drives/nfc/ and is compiled as a
-module named "pn544". It registers a misc device and creates a device
-file named "/dev/pn544".
+module named "pn544".
 
 Host Interfaces: I2C, SPI and HSU, this driver supports currently only I2C.
 
-The Interface
--------------
-
-The driver offers a sysfs interface for a hardware test and an IOCTL
-interface for selecting between two operating modes. There are read,
-write and poll functions for transferring messages. The two operating
-modes are the normal (HCI) mode and the firmware update mode.
-
-PN544 is controlled by sending messages from the userspace to the
-chip. The main function of the driver is just to pass those messages
-without caring about the message content.
-
-
 Protocols
 ---------
 
@@ -47,68 +30,3 @@ and third (LSB) bytes of the message. The maximum FW message length is
 
 For the ETSI HCI specification see
 http://www.etsi.org/WebSite/Technologies/ProtocolSpecification.aspx
-
-The Hardware Test
------------------
-
-The idea of the test is that it can performed by reading from the
-corresponding sysfs file. The test is implemented in the board file
-and it should test that PN544 can be put into the firmware update
-mode. If the test is not implemented the sysfs file does not get
-created.
-
-Example:
-> cat /sys/module/pn544/drivers/i2c\:pn544/3-002b/nfc_test
-1
-
-Normal Operation
-----------------
-
-PN544 is powered up when the device file is opened, otherwise it's
-turned off. Only one instance can use the device at a time.
-
-Userspace applications control PN544 with HCI messages. The hardware
-sends an interrupt when data is available for reading. Data is
-physically read when the read function is called by a userspace
-application. Poll() checks the read interrupt state. Configuration and
-self testing are also done from the userspace using read and write.
-
-Example platform data:
-
-static int rx71_pn544_nfc_request_resources(struct i2c_client *client)
-{
-       /* Get and setup the HW resources for the device */
-}
-
-static void rx71_pn544_nfc_free_resources(void)
-{
-       /* Release the HW resources */
-}
-
-static void rx71_pn544_nfc_enable(int fw)
-{
-       /* Turn the device on */
-}
-
-static int rx71_pn544_nfc_test(void)
-{
-       /*
-        * Put the device into the FW update mode
-        * and then back to the normal mode.
-        * Check the behavior and return one on success,
-        * zero on failure.
-        */
-}
-
-static void rx71_pn544_nfc_disable(void)
-{
-       /* turn the power off */
-}
-
-static struct pn544_nfc_platform_data rx71_nfc_data = {
-       .request_resources = rx71_pn544_nfc_request_resources,
-       .free_resources = rx71_pn544_nfc_free_resources,
-       .enable = rx71_pn544_nfc_enable,
-       .test = rx71_pn544_nfc_test,
-       .disable = rx71_pn544_nfc_disable,
-};
diff --git a/Documentation/sound/alsa/soc/firmware b/Documentation/sound/alsa/soc/firmware
new file mode 100644 (file)
index 0000000..07402a0
--- /dev/null
@@ -0,0 +1,87 @@
+ASoC Firmware Interface
+=======================
+
+ASoC now provides a simple firmware helper interface to allow drivers to load
+kcontrols, widgets, simple coefficients and DAPM graphs alongside their 
+firmware text.
+
+Modern audio DSP devices usually have a tight coupling between their firmware
+and the kcontrols, widgets and graph they implement. It makes sense to allow
+ASoC to generate the ASoC specific objects that define the driver by reading
+them from firmware file(s) rather than having them hard coded in the DSP driver. 
+This allows vendors to write a single DSP component driver for many different
+end user devices with the differentiation coming from the firmware.
+
+
+ASoC Firmware Features
+======================
+
+The ASoC firmware interface takes firmware data and reads standard headers that
+define each section in the firmware. It can then displatch the firmware section
+to either the component driver or the generic ASoC firmware core for processing
+and enumeration. The firmware sections can come in any order and can come from
+more than one firmware file, although each file will require a request to the
+kernel fimrware API in order to load the data.
+
+The ASoC firmware headers are very simple and define the section type, size,
+version, ABI as well as verifying that the section is valid. There is an 
+alsa-utils based tool that can be used to add a section header to a firmware
+file. Firmware files can be concatentaed together using tools like cat to create
+a single firmware file from numerous firmware files.
+
+Firmware sections can have the following types :- 
+
+ o kcontrols (with or without TLV data)
+ o kcontrols (with coefficient data - for simple one time coefficients)
+ o DAPM widgets with associated kcontrol(s)
+ o DAPM graph elements
+ o Firmware text
+ o Firmware configuration data
+
+The firmware header is defined in include/uapi/sound/asoc.h as :-
+
+/*
+ * File and Block Header
+ */
+struct snd_soc_fw_hdr {
+       __le32 magic;
+       __le32 abi;             /* ABI version */
+       __le32 type;
+       __le32 vendor_type;     /* optional vendor specific type info */
+       __le32 version;         /* optional vendor specific version details */
+       __le32 size;            /* data bytes, excluding this header */
+} __attribute__((packed));
+
+
+Component Driver API
+====================
+
+Component drivers can use the ASoC firmware interface by calling either :- 
+
+int snd_soc_fw_load_card(struct snd_soc_card *card,
+       struct snd_soc_fw_card_ops *ops, const struct firmware *fw,
+       u32 index);
+int snd_soc_fw_load_platform(struct snd_soc_platform *platform,
+       struct snd_soc_fw_platform_ops *ops, const struct firmware *fw,
+       u32 index);
+int snd_soc_fw_load_codec(struct snd_soc_codec *codec,
+       struct snd_soc_fw_codec_ops *ops, const struct firmware *fw,
+       u32 index);
+
+depending on the type of the component driver. The component driver passes in a
+pointer to itself and a pointer to the firmware object (requested using FW API).
+It also passes in a pointer to some some internal functions that are used to 
+initialise any firmware object or assign private data. Finally an index number
+can be passed to identify all objects created with this firmware so that
+component drivers can load and remove multiple firmware objects based on this
+number.
+
+Firmware objects can be removed with the following calls :-
+
+void snd_soc_fw_dcontrols_remove_codec(struct snd_soc_codec *codec, u32 index);
+void snd_soc_fw_dcontrols_remove_platform(struct snd_soc_platform *platform, u32 index);
+void snd_soc_fw_dcontrols_remove_card(struct snd_soc_card *soc_card, u32 index);
+
+The ASoC FW interface for component drivers is defined in include/sound/soc-fw.h
+
+
diff --git a/Documentation/thermal/exynos_thermal_emulation b/Documentation/thermal/exynos_thermal_emulation
new file mode 100644 (file)
index 0000000..b73bbfb
--- /dev/null
@@ -0,0 +1,53 @@
+EXYNOS EMULATION MODE
+========================
+
+Copyright (C) 2012 Samsung Electronics
+
+Written by Jonghwa Lee <jonghwa3.lee@samsung.com>
+
+Description
+-----------
+
+Exynos 4x12 (4212, 4412) and 5 series provide emulation mode for thermal management unit.
+Thermal emulation mode supports software debug for TMU's operation. User can set temperature
+manually with software code and TMU will read current temperature from user value not from
+sensor's value.
+
+Enabling CONFIG_EXYNOS_THERMAL_EMUL option will make this support in available.
+When it's enabled, sysfs node will be created under
+/sys/bus/platform/devices/'exynos device name'/ with name of 'emulation'.
+
+The sysfs node, 'emulation', will contain value 0 for the initial state. When you input any
+temperature you want to update to sysfs node, it automatically enable emulation mode and
+current temperature will be changed into it.
+(Exynos also supports user changable delay time which would be used to delay of
+ changing temperature. However, this node only uses same delay of real sensing time, 938us.)
+
+Exynos emulation mode requires synchronous of value changing and enabling. It means when you
+want to update the any value of delay or next temperature, then you have to enable emulation
+mode at the same time. (Or you have to keep the mode enabling.) If you don't, it fails to
+change the value to updated one and just use last succeessful value repeatedly. That's why
+this node gives users the right to change termerpature only. Just one interface makes it more
+simply to use.
+
+Disabling emulation mode only requires writing value 0 to sysfs node.
+
+
+TEMP   120 |
+           |
+       100 |
+           |
+        80 |
+           |                            +-----------
+        60 |                            |          |
+           |              +-------------|          |
+        40 |              |             |          |
+           |              |             |          |
+        20 |              |             |          +----------
+           |              |             |          |          |
+         0 |______________|_____________|__________|__________|_________
+                  A             A          A                  A     TIME
+                  |<----->|     |<----->|  |<----->|          |
+                  | 938us |     |       |  |       |          |
+emulation    :  0  50     |     70      |  20      |          0
+current temp :   sensor   50            70         20        sensor
diff --git a/Documentation/thermal/intel_powerclamp.txt b/Documentation/thermal/intel_powerclamp.txt
new file mode 100644 (file)
index 0000000..332de4a
--- /dev/null
@@ -0,0 +1,307 @@
+                        =======================
+                        INTEL POWERCLAMP DRIVER
+                        =======================
+By: Arjan van de Ven <arjan@linux.intel.com>
+    Jacob Pan <jacob.jun.pan@linux.intel.com>
+
+Contents:
+       (*) Introduction
+           - Goals and Objectives
+
+       (*) Theory of Operation
+           - Idle Injection
+           - Calibration
+
+       (*) Performance Analysis
+           - Effectiveness and Limitations
+           - Power vs Performance
+           - Scalability
+           - Calibration
+           - Comparison with Alternative Techniques
+
+       (*) Usage and Interfaces
+           - Generic Thermal Layer (sysfs)
+           - Kernel APIs (TBD)
+
+============
+INTRODUCTION
+============
+
+Consider the situation where a system’s power consumption must be
+reduced at runtime, due to power budget, thermal constraint, or noise
+level, and where active cooling is not preferred. Software managed
+passive power reduction must be performed to prevent the hardware
+actions that are designed for catastrophic scenarios.
+
+Currently, P-states, T-states (clock modulation), and CPU offlining
+are used for CPU throttling.
+
+On Intel CPUs, C-states provide effective power reduction, but so far
+they’re only used opportunistically, based on workload. With the
+development of intel_powerclamp driver, the method of synchronizing
+idle injection across all online CPU threads was introduced. The goal
+is to achieve forced and controllable C-state residency.
+
+Test/Analysis has been made in the areas of power, performance,
+scalability, and user experience. In many cases, clear advantage is
+shown over taking the CPU offline or modulating the CPU clock.
+
+
+===================
+THEORY OF OPERATION
+===================
+
+Idle Injection
+--------------
+
+On modern Intel processors (Nehalem or later), package level C-state
+residency is available in MSRs, thus also available to the kernel.
+
+These MSRs are:
+      #define MSR_PKG_C2_RESIDENCY     0x60D
+      #define MSR_PKG_C3_RESIDENCY     0x3F8
+      #define MSR_PKG_C6_RESIDENCY     0x3F9
+      #define MSR_PKG_C7_RESIDENCY     0x3FA
+
+If the kernel can also inject idle time to the system, then a
+closed-loop control system can be established that manages package
+level C-state. The intel_powerclamp driver is conceived as such a
+control system, where the target set point is a user-selected idle
+ratio (based on power reduction), and the error is the difference
+between the actual package level C-state residency ratio and the target idle
+ratio.
+
+Injection is controlled by high priority kernel threads, spawned for
+each online CPU.
+
+These kernel threads, with SCHED_FIFO class, are created to perform
+clamping actions of controlled duty ratio and duration. Each per-CPU
+thread synchronizes its idle time and duration, based on the rounding
+of jiffies, so accumulated errors can be prevented to avoid a jittery
+effect. Threads are also bound to the CPU such that they cannot be
+migrated, unless the CPU is taken offline. In this case, threads
+belong to the offlined CPUs will be terminated immediately.
+
+Running as SCHED_FIFO and relatively high priority, also allows such
+scheme to work for both preemptable and non-preemptable kernels.
+Alignment of idle time around jiffies ensures scalability for HZ
+values. This effect can be better visualized using a Perf timechart.
+The following diagram shows the behavior of kernel thread
+kidle_inject/cpu. During idle injection, it runs monitor/mwait idle
+for a given "duration", then relinquishes the CPU to other tasks,
+until the next time interval.
+
+The NOHZ schedule tick is disabled during idle time, but interrupts
+are not masked. Tests show that the extra wakeups from scheduler tick
+have a dramatic impact on the effectiveness of the powerclamp driver
+on large scale systems (Westmere system with 80 processors).
+
+CPU0
+                 ____________          ____________
+kidle_inject/0   |   sleep    |  mwait |  sleep     |
+       _________|            |________|            |_______
+                              duration
+CPU1
+                 ____________          ____________
+kidle_inject/1   |   sleep    |  mwait |  sleep     |
+       _________|            |________|            |_______
+                             ^
+                             |
+                             |
+                             roundup(jiffies, interval)
+
+Only one CPU is allowed to collect statistics and update global
+control parameters. This CPU is referred to as the controlling CPU in
+this document. The controlling CPU is elected at runtime, with a
+policy that favors BSP, taking into account the possibility of a CPU
+hot-plug.
+
+In terms of dynamics of the idle control system, package level idle
+time is considered largely as a non-causal system where its behavior
+cannot be based on the past or current input. Therefore, the
+intel_powerclamp driver attempts to enforce the desired idle time
+instantly as given input (target idle ratio). After injection,
+powerclamp moniors the actual idle for a given time window and adjust
+the next injection accordingly to avoid over/under correction.
+
+When used in a causal control system, such as a temperature control,
+it is up to the user of this driver to implement algorithms where
+past samples and outputs are included in the feedback. For example, a
+PID-based thermal controller can use the powerclamp driver to
+maintain a desired target temperature, based on integral and
+derivative gains of the past samples.
+
+
+
+Calibration
+-----------
+During scalability testing, it is observed that synchronized actions
+among CPUs become challenging as the number of cores grows. This is
+also true for the ability of a system to enter package level C-states.
+
+To make sure the intel_powerclamp driver scales well, online
+calibration is implemented. The goals for doing such a calibration
+are:
+
+a) determine the effective range of idle injection ratio
+b) determine the amount of compensation needed at each target ratio
+
+Compensation to each target ratio consists of two parts:
+
+        a) steady state error compensation
+       This is to offset the error occurring when the system can
+       enter idle without extra wakeups (such as external interrupts).
+
+       b) dynamic error compensation
+       When an excessive amount of wakeups occurs during idle, an
+       additional idle ratio can be added to quiet interrupts, by
+       slowing down CPU activities.
+
+A debugfs file is provided for the user to examine compensation
+progress and results, such as on a Westmere system.
+[jacob@nex01 ~]$ cat
+/sys/kernel/debug/intel_powerclamp/powerclamp_calib
+controlling cpu: 0
+pct confidence steady dynamic (compensation)
+0      0       0       0
+1      1       0       0
+2      1       1       0
+3      3       1       0
+4      3       1       0
+5      3       1       0
+6      3       1       0
+7      3       1       0
+8      3       1       0
+...
+30     3       2       0
+31     3       2       0
+32     3       1       0
+33     3       2       0
+34     3       1       0
+35     3       2       0
+36     3       1       0
+37     3       2       0
+38     3       1       0
+39     3       2       0
+40     3       3       0
+41     3       1       0
+42     3       2       0
+43     3       1       0
+44     3       1       0
+45     3       2       0
+46     3       3       0
+47     3       0       0
+48     3       2       0
+49     3       3       0
+
+Calibration occurs during runtime. No offline method is available.
+Steady state compensation is used only when confidence levels of all
+adjacent ratios have reached satisfactory level. A confidence level
+is accumulated based on clean data collected at runtime. Data
+collected during a period without extra interrupts is considered
+clean.
+
+To compensate for excessive amounts of wakeup during idle, additional
+idle time is injected when such a condition is detected. Currently,
+we have a simple algorithm to double the injection ratio. A possible
+enhancement might be to throttle the offending IRQ, such as delaying
+EOI for level triggered interrupts. But it is a challenge to be
+non-intrusive to the scheduler or the IRQ core code.
+
+
+CPU Online/Offline
+------------------
+Per-CPU kernel threads are started/stopped upon receiving
+notifications of CPU hotplug activities. The intel_powerclamp driver
+keeps track of clamping kernel threads, even after they are migrated
+to other CPUs, after a CPU offline event.
+
+
+=====================
+Performance Analysis
+=====================
+This section describes the general performance data collected on
+multiple systems, including Westmere (80P) and Ivy Bridge (4P, 8P).
+
+Effectiveness and Limitations
+-----------------------------
+The maximum range that idle injection is allowed is capped at 50
+percent. As mentioned earlier, since interrupts are allowed during
+forced idle time, excessive interrupts could result in less
+effectiveness. The extreme case would be doing a ping -f to generated
+flooded network interrupts without much CPU acknowledgement. In this
+case, little can be done from the idle injection threads. In most
+normal cases, such as scp a large file, applications can be throttled
+by the powerclamp driver, since slowing down the CPU also slows down
+network protocol processing, which in turn reduces interrupts.
+
+When control parameters change at runtime by the controlling CPU, it
+may take an additional period for the rest of the CPUs to catch up
+with the changes. During this time, idle injection is out of sync,
+thus not able to enter package C- states at the expected ratio. But
+this effect is minor, in that in most cases change to the target
+ratio is updated much less frequently than the idle injection
+frequency.
+
+Scalability
+-----------
+Tests also show a minor, but measurable, difference between the 4P/8P
+Ivy Bridge system and the 80P Westmere server under 50% idle ratio.
+More compensation is needed on Westmere for the same amount of
+target idle ratio. The compensation also increases as the idle ratio
+gets larger. The above reason constitutes the need for the
+calibration code.
+
+On the IVB 8P system, compared to an offline CPU, powerclamp can
+achieve up to 40% better performance per watt. (measured by a spin
+counter summed over per CPU counting threads spawned for all running
+CPUs).
+
+====================
+Usage and Interfaces
+====================
+The powerclamp driver is registered to the generic thermal layer as a
+cooling device. Currently, it’s not bound to any thermal zones.
+
+jacob@chromoly:/sys/class/thermal/cooling_device14$ grep . *
+cur_state:0
+max_state:50
+type:intel_powerclamp
+
+Example usage:
+- To inject 25% idle time
+$ sudo sh -c "echo 25 > /sys/class/thermal/cooling_device80/cur_state
+"
+
+If the system is not busy and has more than 25% idle time already,
+then the powerclamp driver will not start idle injection. Using Top
+will not show idle injection kernel threads.
+
+If the system is busy (spin test below) and has less than 25% natural
+idle time, powerclamp kernel threads will do idle injection, which
+appear running to the scheduler. But the overall system idle is still
+reflected. In this example, 24.1% idle is shown. This helps the
+system admin or user determine the cause of slowdown, when a
+powerclamp driver is in action.
+
+
+Tasks: 197 total,   1 running, 196 sleeping,   0 stopped,   0 zombie
+Cpu(s): 71.2%us,  4.7%sy,  0.0%ni, 24.1%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
+Mem:   3943228k total,  1689632k used,  2253596k free,    74960k buffers
+Swap:  4087804k total,        0k used,  4087804k free,   945336k cached
+
+  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
+ 3352 jacob     20   0  262m  644  428 S  286  0.0   0:17.16 spin
+ 3341 root     -51   0     0    0    0 D   25  0.0   0:01.62 kidle_inject/0
+ 3344 root     -51   0     0    0    0 D   25  0.0   0:01.60 kidle_inject/3
+ 3342 root     -51   0     0    0    0 D   25  0.0   0:01.61 kidle_inject/1
+ 3343 root     -51   0     0    0    0 D   25  0.0   0:01.60 kidle_inject/2
+ 2935 jacob     20   0  696m 125m  35m S    5  3.3   0:31.11 firefox
+ 1546 root      20   0  158m  20m 6640 S    3  0.5   0:26.97 Xorg
+ 2100 jacob     20   0 1223m  88m  30m S    3  2.3   0:23.68 compiz
+
+Tests have shown that by using the powerclamp driver as a cooling
+device, a PID based userspace thermal controller can manage to
+control CPU temperature effectively, when no other thermal influence
+is added. For example, a UltraBook user can compile the kernel under
+certain temperature (below most active trip points).
index 88c02334e35681b005ac20a1ad0dfe4a894bc1f7..6859661c9d31be090a78e5a2c35a9a5fdad7ca80 100644 (file)
@@ -55,6 +55,8 @@ temperature) and throttle appropriate devices.
        .get_trip_type: get the type of certain trip point.
        .get_trip_temp: get the temperature above which the certain trip point
                        will be fired.
+       .set_emul_temp: set the emulation temperature which helps in debugging
+                       different threshold temperature points.
 
 1.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz)
 
@@ -153,6 +155,7 @@ Thermal zone device sys I/F, created once it's registered:
     |---trip_point_[0-*]_temp: Trip point temperature
     |---trip_point_[0-*]_type: Trip point type
     |---trip_point_[0-*]_hyst: Hysteresis value for this trip point
+    |---emul_temp:             Emulated temperature set node
 
 Thermal cooling device sys I/F, created once it's registered:
 /sys/class/thermal/cooling_device[0-*]:
@@ -252,6 +255,16 @@ passive
        Valid values: 0 (disabled) or greater than 1000
        RW, Optional
 
+emul_temp
+       Interface to set the emulated temperature method in thermal zone
+       (sensor). After setting this temperature, the thermal zone may pass
+       this temperature to platform emulation function if registered or
+       cache it locally. This is useful in debugging different temperature
+       threshold and its associated cooling action. This is write only node
+       and writing 0 on this node should disable emulation.
+       Unit: millidegree Celsius
+       WO, Optional
+
 *****************************
 * Cooling device attributes *
 *****************************
@@ -329,8 +342,9 @@ The framework includes a simple notification mechanism, in the form of a
 netlink event. Netlink socket initialization is done during the _init_
 of the framework. Drivers which intend to use the notification mechanism
 just need to call thermal_generate_netlink_event() with two arguments viz
-(originator, event). Typically the originator will be an integer assigned
-to a thermal_zone_device when it registers itself with the framework. The
+(originator, event). The originator is a pointer to struct thermal_zone_device
+from where the event has been originated. An integer which represents the
+thermal zone device will be used in the message to identify the zone. The
 event will be one of:{THERMAL_AUX0, THERMAL_AUX1, THERMAL_CRITICAL,
 THERMAL_DEV_FAULT}. Notification can be sent when the current temperature
 crosses any of the configured thresholds.
index 67874b82a4edf318ae3718ae6137393140405586..7637d31f5f80678681436939024e221b5b72550d 100644 (file)
@@ -932,6 +932,7 @@ config ARCH_DAVINCI
        select GENERIC_IRQ_CHIP
        select HAVE_IDE
        select NEED_MACH_GPIO_H
+       select TI_PRIV_EDMA
        select USE_OF
        select ZONE_DMA
        help
index 30c443c406f3f85ef6f473b7522d426716f56ba6..c0f4e767f55c873c76a49edc778ace3836921efe 100644 (file)
@@ -293,6 +293,8 @@ zinstall uinstall install: vmlinux
 
 %.dtb: scripts
        $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $(boot)/dts/$@
+uImage.%: uImage
+       $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
 
 dtbs: scripts
        $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) dtbs
@@ -300,6 +302,7 @@ dtbs: scripts
 # We use MRPROPER_FILES and CLEAN_FILES now
 archclean:
        $(Q)$(MAKE) $(clean)=$(boot)
+       $(Q)$(MAKE) $(clean)=$(boot)/dts
 
 # My testing targets (bypasses dependencies)
 bp:;   $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/bootpImage
index abfce280f57b3f0f90d186eaed8a188d978037c7..99fd4d488200b6fa8ffe70f6a7a403693188cdf8 100644 (file)
@@ -55,6 +55,9 @@ $(obj)/zImage:        $(obj)/compressed/vmlinux FORCE
        $(call if_changed,objcopy)
        @$(kecho) '  Kernel: $@ is ready'
 
+$(obj)/zImage-dtb.%:   $(obj)/dts/%.dtb $(obj)/zImage
+       cat $(obj)/zImage $< > $@
+
 endif
 
 ifneq ($(LOADADDR),)
@@ -80,6 +83,10 @@ $(obj)/uImage:       $(obj)/zImage FORCE
        $(call if_changed,uimage)
        @$(kecho) '  Image $@ is ready'
 
+$(obj)/uImage.%:       $(obj)/zImage-dtb.% FORCE
+       $(call if_changed,uimage)
+       @echo '  Image $@ is ready'
+
 $(obj)/bootp/bootp: $(obj)/zImage initrd FORCE
        $(Q)$(MAKE) $(build)=$(obj)/bootp $@
        @:
index 5ebb44fe826a9b0b36d051272910a26775e5a16f..b4078dd927b7d16d6a6cc33937e497c33ad76ef0 100644 (file)
@@ -110,7 +110,8 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \
        omap4-panda-es.dtb \
        omap4-var-som.dtb \
        omap4-sdp.dtb \
-       omap5-evm.dtb \
+       omap5-uevm.dtb \
+       omap5-sevm.dtb \
        am335x-evm.dtb \
        am335x-evmsk.dtb \
        am335x-bone.dtb
index 11b240c5d3238e7a76699be74f0f1085de4f5c4b..bfed335e3f2b8921f6ce80ba4eba0d608a0e288d 100644 (file)
                };
 
                ldo3_reg: regulator@5 {
+                       regulator-min-microvolt = <1800000>;
+                       regulator-max-microvolt = <3300000>;
                        regulator-always-on;
                };
 
 &cpsw_emac1 {
        phy_id = <&davinci_mdio>, <1>;
 };
+
+&mmc1 {
+       status = "okay";
+       vmmc-supply = <&ldo3_reg>;
+};
+
+&mmc2 {
+       status = "okay";
+       vmmc-supply = <&ldo3_reg>;
+};
+
+&edma {
+       ti,edma-xbar-event-map = <32 12>;
+};
+
+&sham {
+       status = "okay";
+};
+
+&aes {
+       status = "okay";
+};
index d6496440fcea496b2d93a3b91b8005101ccc2efa..71e51f5bb68bed312a93f5342ffc65c8bf558b82 100644 (file)
                };
 
                vmmc_reg: regulator@12 {
+                       regulator-min-microvolt = <1800000>;
+                       regulator-max-microvolt = <3300000>;
                        regulator-always-on;
                };
        };
 &cpsw_emac1 {
        phy_id = <&davinci_mdio>, <1>;
 };
+
+&mmc1 {
+       status = "okay";
+       vmmc-supply = <&vmmc_reg>;
+};
+
+&sham {
+       status = "okay";
+};
+
+&aes {
+       status = "okay";
+};
index f5a6162a4ff22c0137fc97346c03d26cca11e8b7..39494b3bb128ab5094220f8dfff3be5739d876de 100644 (file)
                };
 
                vmmc_reg: regulator@12 {
+                       regulator-min-microvolt = <1800000>;
+                       regulator-max-microvolt = <3300000>;
                        regulator-always-on;
                };
        };
 };
+
+&cpsw_emac0 {
+       phy_id = <&davinci_mdio>, <0>;
+};
+
+&cpsw_emac1 {
+       phy_id = <&davinci_mdio>, <1>;
+};
+
+&sham {
+       status = "okay";
+};
+
+&aes {
+       status = "okay";
+};
index c2f14e875eb6274b18fc159e8e773395aca6df87..ec5526cc90b6a428fc57e4b12031f310e728dd08 100644 (file)
                        reg = <0x48200000 0x1000>;
                };
 
+               edma: edma@49000000 {
+                       compatible = "ti,edma3";
+                       ti,hwmods = "tpcc", "tptc0", "tptc1", "tptc2";
+                       reg =   <0x49000000 0x10000>,
+                               <0x44e10f90 0x10>;
+                       interrupt-parent = <&intc>;
+                       interrupts = <12 13 14>;
+                       #dma-cells = <1>;
+                       dma-channels = <64>;
+                       ti,edma-regions = <4>;
+                       ti,edma-slots = <256>;
+                       ti,edma-queue-tc-map = <0 0
+                                               1 1
+                                               2 2>;
+                       ti,edma-queue-priority-map = <0 0
+                                                     1 1
+                                                     2 2>;
+                       ti,edma-default-queue = <0>;
+               };
+
                gpio1: gpio@44e07000 {
                        compatible = "ti,omap4-gpio";
                        ti,hwmods = "gpio1";
                        status = "disabled";
                };
 
+               mmc1: mmc@48060000 {
+                       compatible = "ti,omap3-hsmmc";
+                       ti,hwmods = "mmc1";
+                       ti,dual-volt;
+                       ti,needs-special-reset;
+                       dmas = <&edma 24
+                               &edma 25>;
+                       dma-names = "tx", "rx";
+                       status = "disabled";
+               };
+
+               mmc2: mmc@481d8000 {
+                       compatible = "ti,omap3-hsmmc";
+                       ti,hwmods = "mmc2";
+                       ti,needs-special-reset;
+                       dmas = <&edma 2
+                               &edma 3>;
+                       dma-names = "tx", "rx";
+                       status = "disabled";
+               };
+
+               mmc3: mmc@47810000 {
+                       compatible = "ti,omap3-hsmmc";
+                       ti,hwmods = "mmc3";
+                       ti,needs-special-reset;
+                       status = "disabled";
+               };
+
                wdt2: wdt@44e35000 {
                        compatible = "ti,omap3-wdt";
                        ti,hwmods = "wd_timer2";
                        reg = <0x48040000 0x400>;
                        interrupts = <68>;
                        ti,hwmods = "timer2";
+                       ti,timer-non-wkup;
                };
 
                timer3: timer@48042000 {
                        interrupt = <65>;
                        ti,spi-num-cs = <2>;
                        ti,hwmods = "spi0";
+                       dmas = <&edma 16
+                               &edma 17
+                               &edma 18
+                               &edma 19>;
+                       dma-names = "tx0", "rx0", "tx1", "rx1";
                        status = "disabled";
                };
 
                        interrupt = <125>;
                        ti,spi-num-cs = <2>;
                        ti,hwmods = "spi1";
+                       dmas = <&edma 42
+                               &edma 43
+                               &edma 44
+                               &edma 45>;
+                       dma-names = "tx0", "rx0", "tx1", "rx1";
                        status = "disabled";
                };
 
                        compatible = "ti,musb-am33xx";
                        reg = <0x47400000 0x1000        /* usbss */
                               0x47401000 0x800         /* musb instance 0 */
-                              0x47401800 0x800>;       /* musb instance 1 */
+                              0x47401800 0x800         /* musb instance 1 */
+                              0x47402000 0x6000>;      /* cppi41 usbdma */
                        interrupts = <17                /* usbss */
                                      18                /* musb instance 0 */
                                      19>;              /* musb instance 1 */
                        multipoint = <1>;
                        num-eps = <16>;
                        ram-bits = <12>;
-                       port0-mode = <3>;
-                       port1-mode = <3>;
+                       port0-mode = <3>;               /* 1: host only mode */
+                       port1-mode = <3>;               /* 2: device & 3:otg */
                        power = <250>;
                        ti,hwmods = "usb_otg_hs";
                };
                        rx_descs = <64>;
                        mac_control = <0x20>;
                        slaves = <2>;
-                       cpts_active_slave = <0>;
+                       active_slave = <0>;
                        cpts_clock_mult = <0x80000000>;
                        cpts_clock_shift = <29>;
                        reg = <0x4a100000 0x800
                                mac-address = [ 00 00 00 00 00 00 ];
                        };
                };
+
+               wkup_m3: wkup_m3@44d00000 {
+                       compatible = "ti,am3353-wkup-m3";
+                       reg = <0x44d00000 0x4000        /* M3 UMEM */
+                              0x44d80000 0x2000>;      /* M3 DMEM */
+                       interrupts = <78>;
+                       ti,hwmods = "wkup_m3";
+               };
+
+               sham: sham@53100000 {
+                       compatible = "ti,omap4-sham";
+                       ti,hwmods = "sham";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <0x53100000 0x200>;
+                       interrupt-parent = <&intc>;
+                       interrupts = <109>;
+                       dmas = <&edma 36>;
+                       dma-names = "rx";
+               };
+
+               aes: aes@53500000 {
+                       compatible = "ti,omap4-aes";
+                       ti,hwmods = "aes";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       reg = <0x53500000 0xa0>;
+                       interrupt-parent = <&intc>;
+                       interrupts = <102>;
+                       dmas = <&edma 6
+                               &edma 5>;
+                       dma-names = "tx", "rx";
+               };
        };
 };
index 3705a81c1fc25eac3b03d4b90be08f6fef7387f9..1177ff63334a8ee0475f67379cd2d7822a67f03f 100644 (file)
 
        leds {
                compatible = "gpio-leds";
-               pmu_stat {
-                       label = "beagleboard::pmu_stat";
-                       gpios = <&twl_gpio 19 0>; /* LEDB */
-               };
 
                heartbeat {
                        label = "beagleboard::usr0";
                };
        };
 
+       pwmleds {
+               compatible = "pwm-leds";
+
+               pmu_stat {
+                       label = "beagleboard::pmu_stat";
+                       pwms = <&twl_pwmled 1 7812500>;
+                       max-brightness = <127>;
+               };
+       };
+
        sound {
                compatible = "ti,omap-twl4030";
                ti,model = "omap3beagle";
         */
        ti,pulldowns = <0x03a1c4>;
 };
+
+&usb_otg_hs {
+       interface-type = <0>;
+       mode = <3>;
+       power = <50>;
+};
index f624dc85d441e5000b8fa0a13abfa78a3e5ce7d2..02d23f15fd867fafe2172a1eb75e60beab51a5c7 100644 (file)
                };
        };
 
+       /* HS USB Port 2 RESET */
+       hsusb2_reset: hsusb2_reset_reg {
+               compatible = "regulator-fixed";
+               regulator-name = "hsusb2_reset";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               gpio = <&gpio5 19 0>;   /* gpio_147 */
+               startup-delay-us = <70000>;
+               enable-active-high;
+       };
+
+       /* HS USB Port 2 Power */
+       hsusb2_power: hsusb2_power_reg {
+               compatible = "regulator-fixed";
+               regulator-name = "hsusb2_vbus";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               gpio = <&twl_gpio 18 0>;        /* GPIO LEDA */
+               startup-delay-us = <70000>;
+       };
+
+       /* HS USB Host PHY on PORT 2 */
+       hsusb2_phy: hsusb2_phy {
+               compatible = "usb-nop-xceiv";
+               reset-supply = <&hsusb2_reset>;
+               vcc-supply = <&hsusb2_power>;
+       };
+};
+
+&omap3_pmx_core {
+       pinctrl-names = "default";
+       pinctrl-0 = <
+                       &hsusbb2_pins
+       >;
+
+       hsusbb2_pins: pinmux_hsusbb2_pins {
+               pinctrl-single,pins = <
+                       0x5c0 0x3  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_clk OUTPUT */
+                       0x5c2 0x3  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_stp OUTPUT */
+                       0x5c4 0x10b  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dir INPUT | PULLDOWN */
+                       0x5c6 0x10b  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_nxt INPUT | PULLDOWN */
+                       0x5c8 0x10b  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat0 INPUT | PULLDOWN */
+                       0x5cA 0x10b  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat1 INPUT | PULLDOWN */
+                       0x1a4 0x10b  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat2 INPUT | PULLDOWN */
+                       0x1a6 0x10b  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat3 INPUT | PULLDOWN */
+                       0x1a8 0x10b  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat4 INPUT | PULLDOWN */
+                       0x1aa 0x10b  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat5 INPUT | PULLDOWN */
+                       0x1ac 0x10b  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat6 INPUT | PULLDOWN */
+                       0x1ae 0x10b  /* USBB2_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat7 INPUT | PULLDOWN */
+               >;
+       };
 };
 
 &i2c1 {
 &mmc3 {
        status = "disabled";
 };
+
+&usbhshost {
+       port2-mode = "ehci-phy";
+};
+
+&usbhsehci {
+       phys = <0 &hsusb2_phy>;
+};
+
+&twl_gpio {
+       ti,use-leds;
+       /* pullups: BIT(1) */
+       ti,pullups = <0x000002>;
+       /*
+        * pulldowns:
+        * BIT(2), BIT(6), BIT(7), BIT(8), BIT(13)
+        * BIT(15), BIT(16), BIT(17)
+        */
+       ti,pulldowns = <0x03a1c4>;
+};
index e8ba1c247a39bf28622276e07a75bf6137baf463..6e6f80db689a3b0d8655aef034bc5e5ea0f1ba80 100644 (file)
@@ -59,3 +59,9 @@
 &twl_gpio {
        ti,use-leds;
 };
+
+&usb_otg_hs {
+       interface-type = <0>;
+       mode = <3>;
+       power = <50>;
+};
index 89808ce016734dd8436e75ae5dad38523ce56671..e3979b195898438166ae72d0ee45dd4b5b90bae9 100644 (file)
@@ -55,3 +55,9 @@
 &twl_gpio {
        ti,use-leds;
 };
+
+&usb_otg_hs {
+       interface-type = <0>;
+       mode = <3>;
+       power = <50>;
+};
index 1acc26148ffcf94c1ee605d1db58c531f9b01e26..aff549367724465beb82998f323a249fb05a5b7b 100644 (file)
                        ti,timer-alwon;
                        ti,timer-secure;
                };
+
+               usbhstll: usbhstll@48062000 {
+                       compatible = "ti,usbhs-tll";
+                       reg = <0x48062000 0x1000>;
+                       interrupts = <78>;
+                       ti,hwmods = "usb_tll_hs";
+               };
+
+               usbhshost: usbhshost@48064000 {
+                       compatible = "ti,usbhs-host";
+                       reg = <0x48064000 0x400>;
+                       ti,hwmods = "usb_host_hs";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+
+                       usbhsohci: ohci@48064400 {
+                               compatible = "ti,ohci-omap3", "usb-ohci";
+                               reg = <0x48064400 0x400>;
+                               interrupt-parent = <&intc>;
+                               interrupts = <76>;
+                       };
+
+                       usbhsehci: ehci@48064800 {
+                               compatible = "ti,ehci-omap", "usb-ehci";
+                               reg = <0x48064800 0x400>;
+                               interrupt-parent = <&intc>;
+                               interrupts = <77>;
+                       };
+               };
+
+               usb_otg_hs: usb_otg_hs@480ab000 {
+                       compatible = "ti,omap3-musb";
+                       reg = <0x480ab000 0x1000>;
+                       interrupts = <0 92 0x4>, <0 93 0x4>;
+                       interrupt-names = "mc", "dma";
+                       ti,hwmods = "usb_otg_hs";
+                       usb-phy = <&usb2_phy>;
+                       multipoint = <1>;
+                       num-eps = <16>;
+                       ram-bits = <12>;
+               };
        };
 };
index 73bc1a67e444480ff08ec414848981ec27fe7e39..a1d9f1d7edcb33a6e7b78a76c5d60c4249565c84 100644 (file)
                "Line Out", "AUXL",
                "Line Out", "AUXR",
                "AFML", "Line In",
-               "AFMR", "Line In";
+               "AFMR", "Line In",
+               "Headset Playback", "PDM_DL1",
+               "Handsfree Playback", "PDM_DL2",
+               "PDM_UL1", "Capture",
+               "40122000.mcbsp Playback", "BT_VX_DL",
+               "BT_VX_UL", "40122000.mcbsp Capture",
+               "40124000.mcbsp Playback", "MM_EXT_DL",
+               "MM_EXT_UL", "40124000.mcbsp Capture";
 };
 
 /* PandaboardES has external pullups on SCL & SDA */
index 4122efe31cfdf04ed6218ca970c7c661372d94e2..90ae5295e01ea63dcd7bb78c8c8a2fa364b40e44 100644 (file)
                ti,mclk-freq = <38400000>;
 
                ti,mcpdm = <&mcpdm>;
+               ti,mcbsp1 = <&mcbsp1>;
+               ti,mcbsp2 = <&mcbsp2>;
+               ti,aess = <&aess>;
 
                ti,twl6040 = <&twl6040>;
 
+
                /* Audio routing */
                ti,audio-routing =
                        "Headset Stereophone", "HSOL",
                        "HSMIC", "Headset Mic",
                        "Headset Mic", "Headset Mic Bias",
                        "AFML", "Line In",
-                       "AFMR", "Line In";
+                       "AFMR", "Line In",
+                       "Headset Playback", "PDM_DL1",
+                       "Handsfree Playback", "PDM_DL2",
+                       "PDM_UL1", "Capture",
+                       "40122000.mcbsp Playback", "BT_VX_DL",
+                       "BT_VX_UL", "40122000.mcbsp Capture",
+                       "40124000.mcbsp Playback", "MM_EXT_DL",
+                       "MM_EXT_UL", "40124000.mcbsp Capture";
+       };
+
+       sound_hdmi {
+               compatible = "ti,omap-hdmi-tpd12s015-audio";
+               ti,model = "OMAP4HDMI";
+
+               ti,hdmi_audio = <&hdmi>;
+               ti,level_shifter = <&tpd12s015>;
+       };
+
+       /* HS USB Port 1 RESET */
+       hsusb1_reset: hsusb1_reset_reg {
+               compatible = "regulator-fixed";
+               regulator-name = "hsusb1_reset";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               gpio = <&gpio2 30 0>;   /* gpio_62 */
+               startup-delay-us = <70000>;
+               enable-active-high;
+       };
+
+       /* HS USB Port 1 Power */
+       hsusb1_power: hsusb1_power_reg {
+               compatible = "regulator-fixed";
+               regulator-name = "hsusb1_vbus";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               gpio = <&gpio1 1 0>;    /* gpio_1 */
+               startup-delay-us = <70000>;
+               enable-active-high;
+       };
+
+       /* HS USB Host PHY on PORT 1 */
+       hsusb1_phy: hsusb1_phy {
+               compatible = "usb-nop-xceiv";
+               reset-supply = <&hsusb1_reset>;
+               vcc-supply = <&hsusb1_power>;
+       };
+
+       /* hsusb1_phy is clocked by FREF_CLK3 i.e. auxclk3 */
+       clock_alias {
+               clock-name = "auxclk3_ck";
+               clock-alias = "main_clk";
+               device = <&hsusb1_phy>;
        };
 };
 
                        &mcbsp1_pins
                        &dss_hdmi_pins
                        &tpd12s015_pins
+                       &hsusbb1_pins
        >;
 
        twl6040_pins: pinmux_twl6040_pins {
                        0x58 0x10b      /* hdmi_hpd.gpio_63 INPUT PULLDOWN | MODE3 */
                >;
        };
+
+       hsusbb1_pins: pinmux_hsusbb1_pins {
+               pinctrl-single,pins = <
+                       0x82 0x10C      /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_clk INPUT | PULLDOWN */
+                       0x84 0x4        /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_stp OUTPUT */
+                       0x86 0x104      /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dir INPUT | PULLDOWN */
+                       0x88 0x104      /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_nxt INPUT | PULLDOWN */
+                       0x8a 0x104      /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat0 INPUT | PULLDOWN */
+                       0x8c 0x104      /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat1 INPUT | PULLDOWN */
+                       0x8e 0x104      /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat2 INPUT | PULLDOWN */
+                       0x90 0x104      /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat3 INPUT | PULLDOWN */
+                       0x92 0x104      /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat4 INPUT | PULLDOWN */
+                       0x94 0x104      /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat5 INPUT | PULLDOWN */
+                       0x96 0x104      /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat6 INPUT | PULLDOWN */
+                       0x98 0x104      /* USBB1_ULPITLL_CLK_MUXMODE.usbb1_ulpiphy_dat7 INPUT | PULLDOWN */
+               >;
+           };
+
+       i2c1_pins: pinmux_i2c1_pins {
+               pinctrl-single,pins = <
+                       0xe2 0x118        /* i2c1_scl PULLUP | INPUTENABLE | MODE0 */
+                       0xe4 0x118        /* i2c1_sda PULLUP | INPUTENABLE | MODE0 */
+               >;
+       };
+
+       i2c2_pins: pinmux_i2c2_pins {
+               pinctrl-single,pins = <
+                       0xe6 0x118        /* i2c2_scl PULLUP | INPUTENABLE | MODE0 */
+                       0xe8 0x118        /* i2c2_sda PULLUP | INPUTENABLE | MODE0 */
+               >;
+       };
+
+       i2c3_pins: pinmux_i2c3_pins {
+               pinctrl-single,pins = <
+                       0xea 0x118        /* i2c3_scl PULLUP | INPUTENABLE | MODE0 */
+                       0xec 0x118     /* i2c3_sda PULLUP | INPUTENABLE | MODE0 */
+               >;
+       };
+
+       i2c4_pins: pinmux_i2c4_pins {
+               pinctrl-single,pins = <
+                       0xee 0x118        /* i2c4_scl PULLUP | INPUTENABLE | MODE0 */
+                       0xf0 0x118     /* i2c4_sda PULLUP | INPUTENABLE | MODE0 */
+               >;
+       };
 };
 
 &i2c1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c1_pins>;
+
        clock-frequency = <400000>;
 
        twl: twl@48 {
 
        twl6040: twl@4b {
                compatible = "ti,twl6040";
-               reg = <0x4b>;
                /* SPI = 0, IRQ# = 119, 4 = active high level-sensitive */
                interrupts = <0 119 4>; /* IRQ_SYS_2N cascaded to gic */
                interrupt-parent = <&gic>;
 };
 
 /include/ "twl6030.dtsi"
+/include/ "twl6040.dtsi"
 
 &i2c2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c2_pins>;
+
        clock-frequency = <400000>;
 };
 
 &i2c3 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c3_pins>;
+
        clock-frequency = <100000>;
 
        /*
 };
 
 &i2c4 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c4_pins>;
+
        clock-frequency = <400000>;
 };
 
        device-handle = <&elpida_ECB240ABACN>;
 };
 
-&mcbsp2 {
-       status = "disabled";
-};
-
 &mcbsp3 {
        status = "disabled";
 };
        status = "disabled";
 };
 
+&dpi {
+       dvi {
+               compatible = "ti,tfp410";
+               data-lines = <24>;
+               gpios = <&gpio1 0 0>;   /* 0, power-down */
+               i2c-bus = <&i2c3>;
+       };
+};
+
+&hdmi {
+       tpd12s015: tpd12s015 {
+               compatible = "ti,tpd12s015";
+
+               gpios = <&gpio2 28 0>,  /* 60, CT CP HPD */
+                       <&gpio2 9 0>,   /* 41, LS OE */
+                       <&gpio2 31 0>;  /* 63, HPD */
+
+               hdmi {
+                       compatible = "ti,hdmi_panel";
+               };
+
+       };
+};
+
 &twl_usb_comparator {
        usb-supply = <&vusb>;
 };
+
+&usbhshost {
+       port1-mode = "ehci-phy";
+};
+
+&usbhsehci {
+       phys = <&hsusb1_phy>;
+};
+
+&usb_otg_hs {
+       interface-type = <1>;
+       mode = <3>;
+       power = <50>;
+};
index 43e5258a9372b673d8c7a7866e30bbd5181148fa..f55f146dde891543c97be345f5a3e466191c403e 100644 (file)
                regulator-boot-on;
        };
 
+       quart_eth: fixedregulator@1 {
+               compatible = "regulator-fixed";
+               regulator-name = "QUART_ETH";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               gpio = <&gpio5 10 0>;  /* gpio line 138 */
+               enable-active-high;
+               regulator-boot-on;
+       };
+
        leds {
                compatible = "gpio-leds";
                debug0 {
                };
        };
 
+       pwmleds {
+               compatible = "pwm-leds";
+               kpad {
+                       label = "omap4::keypad";
+                       pwms = <&twl_pwm 0 7812500>;
+                       max-brightness = <127>;
+               };
+
+               charging {
+                       label = "omap4:green:chrg";
+                       pwms = <&twl_pwmled 0 7812500>;
+                       max-brightness = <255>;
+               };
+       };
+
+       backlight {
+               compatible = "pwm-backlight";
+               pwms = <&twl_pwm 1 7812500>;
+               brightness-levels = <
+                               0 10 20 30 40
+                               50 60 70 80 90
+                               100 110 120 127
+                               >;
+               default-brightness-level = <13>;
+       };
+
        sound {
                compatible = "ti,abe-twl6040";
                ti,model = "SDP4430";
 
                ti,mcpdm = <&mcpdm>;
                ti,dmic = <&dmic>;
+               ti,mcasp = <&mcasp>;
+               ti,mcbsp1 = <&mcbsp1>;
+               ti,mcbsp2 = <&mcbsp2>;
 
                ti,twl6040 = <&twl6040>;
 
+               ti,aess = <&aess>;
+
                /* Audio routing */
                ti,audio-routing =
                        "Headset Stereophone", "HSOL",
                        "AFML", "Line In",
                        "AFMR", "Line In",
                        "DMic", "Digital Mic",
-                       "Digital Mic", "Digital Mic1 Bias";
+                       "Digital Mic", "Digital Mic1 Bias",
+                       "Headset Playback", "PDM_DL1",
+                       "Handsfree Playback", "PDM_DL2",
+                       "PDM_UL1", "Capture",
+                       "40122000.mcbsp Playback", "BT_VX_DL",
+                       "BT_VX_UL", "40122000.mcbsp Capture",
+                       "40124000.mcbsp Playback", "MM_EXT_DL",
+                       "MM_EXT_UL", "40124000.mcbsp Capture",
+                       "DMIC0", "omap-dmic-abe.0 Capture",
+                       "omap-dmic-abe.0 Capture", "Digital Mic1 Bias",
+                       "Digital Mic1 Bias", "Digital Mic 0",
+                       "DMIC1", "omap-dmic-abe.1 Capture",
+                       "omap-dmic-abe.1 Capture", "Digital Mic1 Bias",
+                       "Digital Mic1 Bias", "Digital Mic 1",
+                       "DMIC2", "omap-dmic-abe.2 Capture",
+                       "omap-dmic-abe.2 Capture", "Digital Mic1 Bias",
+                       "Digital Mic1 Bias", "Digital Mic 2";
+       };
+
+       sound_hdmi {
+               compatible = "ti,omap-hdmi-tpd12s015-audio";
+               ti,model = "OMAP4HDMI";
+
+               ti,hdmi_audio = <&hdmi>;
+               ti,level_shifter = <&tpd12s015>;
        };
 };
 
                        0x58 0x10b      /* hdmi_hpd.gpio_63 INPUT PULLDOWN | MODE3 */
                >;
        };
+
+       i2c1_pins: pinmux_i2c1_pins {
+               pinctrl-single,pins = <
+                       0xe2 0x118        /* i2c1_scl PULLUP | INPUTENABLE | MODE0 */
+                       0xe4 0x118       /* i2c1_sda PULLUP | INPUTENABLE | MODE0 */
+               >;
+       };
+
+       i2c2_pins: pinmux_i2c2_pins {
+               pinctrl-single,pins = <
+                        0xe6 0x118        /* i2c2_scl PULLUP | INPUTENABLE | MODE0 */
+                        0xe8 0x118        /* i2c2_sda PULLUP | INPUTENABLE | MODE0 */
+               >;
+       };
+
+       i2c3_pins: pinmux_i2c3_pins {
+               pinctrl-single,pins = <
+                       0xea 0x118        /* i2c3_scl PULLUP | INPUTENABLE | MODE0 */
+                       0xec 0x118     /* i2c3_sda PULLUP | INPUTENABLE | MODE0 */
+               >;
+       };
+
+       i2c4_pins: pinmux_i2c4_pins {
+               pinctrl-single,pins = <
+                       0xee 0x118        /* i2c4_scl PULLUP | INPUTENABLE | MODE0 */
+                       0xf0 0x118     /* i2c4_sda PULLUP | INPUTENABLE | MODE0 */
+               >;
+       };
 };
 
 &i2c1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c1_pins>;
+
        clock-frequency = <400000>;
 
        twl: twl@48 {
                        ti,viblmotor-res = <10>;
                        ti,vibrmotor-res = <10>;
                };
+               
+               gpo {
+                       ti,nr_gpo = <3>;
+               };
        };
 };
 
 /include/ "twl6030.dtsi"
 
 &i2c2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c2_pins>;
+
        clock-frequency = <400000>;
 };
 
 &i2c3 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c3_pins>;
+
        clock-frequency = <400000>;
 
        /*
 };
 
 &i2c4 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c4_pins>;
+
        clock-frequency = <400000>;
 
        /*
                interrupt-parent = <&gpio2>;
                interrupts = <2>; /* gpio line 34 */
                vdd-supply = <&vdd_eth>;
+               enable-active-high;
        };
 };
 
 &twl_usb_comparator {
        usb-supply = <&vusb>;
 };
+
+
+&usb_otg_hs {
+       interface-type = <1>;
+       mode = <3>;
+       power = <50>;
+};
+
+&dsi1 {
+       lcd {
+               compatible = "tpo,taal";
+
+               lanes = <
+                       0       /* clk + */
+                       1       /* clk - */
+                       2       /* data1 + */
+                       3       /* data1 - */
+                       4       /* data2 + */
+                       5       /* data2 - */
+               >;
+
+               gpios = <&gpio4 6 0>;   /* 102, reset */
+       };
+};
+
+&dsi2 {
+       lcd2 {
+               compatible = "tpo,taal";
+
+               lanes = <
+                       0       /* clk + */
+                       1       /* clk - */
+                       2       /* data1 + */
+                       3       /* data1 - */
+                       4       /* data2 + */
+                       5       /* data2 - */
+               >;
+
+               gpios = <&gpio4 8 0>;   /* 104, reset */
+       };
+};
+
+&hdmi {
+       tpd12s015: tpd12s015 {
+               compatible = "ti,tpd12s015";
+
+               gpios = <&gpio2 28 0>,  /* 60, CT CP HPD */
+                       <&gpio2 9 0>,   /* 41, LS OE */
+                       <&gpio2 31 0>;  /* 63, HPD */
+
+               hdmi {
+                       compatible = "ti,hdmi_panel";
+               };
+
+       };
+};
index 739bb79e410e5bb5f558afdc85d227d9e5056885..4fc6322aa621e18d12d428c9fb6e9877e13841ae 100644 (file)
                        pinctrl-single,function-mask = <0x7fff>;
                };
 
+               dss {
+                       compatible = "ti,omap4-dss";
+                       ti,hwmods = "dss_core";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       dispc {
+                               compatible = "ti,omap4-dispc";
+                               ti,hwmods = "dss_dispc";
+                       };
+
+                       dpi: dpi {
+                               compatible = "ti,omap4-dpi";
+                               video-source = <2>;
+                       };
+
+                       dsi1: dsi@0 {
+                               compatible = "ti,omap4-dsi";
+                               ti,hwmods = "dss_dsi1";
+                               reg = <0>;
+                               vdds_dsi-supply = <&vcxio>;
+                               video-source = <0>;
+                       };
+
+                       dsi2: dsi@1 {
+                               compatible = "ti,omap4-dsi";
+                               ti,hwmods = "dss_dsi2";
+                               reg = <1>;
+                               vdds_dsi-supply = <&vcxio>;
+                               video-source = <2>;
+                       };
+
+                       hdmi: hdmi {
+                               compatible = "ti,omap4-hdmi", "simple-bus";
+                               ti,hwmods = "dss_hdmi";
+                               vdda_hdmi_dac-supply = <&vdac>;
+                               video-source = <1>;
+                       };
+               };
+
                gpio1: gpio@4a310000 {
                        compatible = "ti,omap4-gpio";
                        reg = <0x4a310000 0x200>;
                };
 
                wdt2: wdt@4a314000 {
-                       compatible = "ti,omap4-wdt", "ti,omap3-wdt";
+                       compatible = "ti,omap4-wdt";
                        reg = <0x4a314000 0x80>;
                        interrupts = <0 80 0x4>;
                        ti,hwmods = "wd_timer2";
                };
 
+               aess: aess@0x401f1000 {
+                       compatible = "ti,omap4-aess";
+                       reg = <0x401f1000 0x3ff>, /* MPU private access */
+                             <0x40180000 0xffff>, /* DMEM - MPU */
+                             <0x401a0000 0x1fff>, /* CMEM - MPU */
+                             <0x401c0000 0x5fff>, /* SMEM - MPU */
+                             <0x401e0000 0x1fff>, /* PMEM - MPU */
+                             <0x4901f000 0x3ff>, /* L3 Interconnect */
+                             <0x49080000 0xffff>, /* DMEM - MPU */
+                             <0x490a0000 0x1fff>, /* CMEM - MPU */
+                             <0x490ce000 0x5fff>, /* SMEM - MPU */
+                             <0x490e0000 0x1fff>; /* PMEM - MPU */
+                       reg-names = "mpu", "dmem", "cmem", "smem", "pmem",
+                               "dma","dmem_dma", "cmem_dma", "smem_dma", "pmem_dma";
+                       interrupts = <0 99 0x4>;
+                       ti,hwmods = "aess";
+               };
+
                mcpdm: mcpdm@40132000 {
                        compatible = "ti,omap4-mcpdm";
                        reg = <0x40132000 0x7f>, /* MPU private access */
                        ti,hwmods = "dmic";
                };
 
+               mcasp: mcasp@4012e000 {
+                       compatible = "ti,omap4-mcasp";
+                       reg = <0x40128000 0x7f>, /* MPU private access */
+                             <0x49028000 0x7f>; /* L3 Interconnect */
+                       reg-names = "mpu", "dma";
+                       interrupts = <0 109 0x4>;
+                       ti,hwmods = "mcasp";
+               };
+
                mcbsp1: mcbsp@40122000 {
                        compatible = "ti,omap4-mcbsp";
                        reg = <0x40122000 0xff>, /* MPU private access */
                        #size-cells = <1>;
                        ranges;
                        ti,hwmods = "ocp2scp_usb_phy";
+                       usb2_phy: usb2phy@4a0ad080 {
+                               compatible = "ti,omap-usb2";
+                               reg = <0x4a0ad080 0x58>;
+                               ctrl-module = <&omap_control_usb>;
+                       };
                };
 
                timer1: timer@4a318000 {
                        ti,hwmods = "timer11";
                        ti,timer-pwm;
                };
+
+               usbhstll: usbhstll@4a062000 {
+                       compatible = "ti,usbhs-tll";
+                       reg = <0x4a062000 0x1000>;
+                       interrupts = <0 78 0x4>;
+                       ti,hwmods = "usb_tll_hs";
+               };
+
+               usbhshost: usbhshost@4a064000 {
+                       compatible = "ti,usbhs-host";
+                       reg = <0x4a064000 0x800>;
+                       ti,hwmods = "usb_host_hs";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+
+                       usbhsohci: ohci@4a064800 {
+                               compatible = "ti,ohci-omap3", "usb-ohci";
+                               reg = <0x4a064800 0x400>;
+                               interrupt-parent = <&gic>;
+                               interrupts = <0 76 0x4>;
+                       };
+
+                       usbhsehci: ehci@4a064c00 {
+                               compatible = "ti,ehci-omap", "usb-ehci";
+                               reg = <0x4a064c00 0x400>;
+                               interrupt-parent = <&gic>;
+                               interrupts = <0 77 0x4>;
+                       };
+               };
+
+               omap_control_usb: omap-control-usb@4a002300 {
+                       compatible = "ti,omap-control-usb";
+                       reg = <0x4a002300 0x4>,
+                             <0x4a00233c 0x4>;
+                       reg-names = "control_dev_conf", "otghs_control";
+                       ti,type = <1>;
+               };
+
+               usb_otg_hs: usb_otg_hs@4a0ab000 {
+                       compatible = "ti,omap4-musb";
+                       reg = <0x4a0ab000 0x7ff>;
+                       interrupts = <0 92 0x4>, <0 93 0x4>;
+                       interrupt-names = "mc", "dma";
+                       ti,hwmods = "usb_otg_hs";
+                       usb-phy = <&usb2_phy>;
+                       multipoint = <1>;
+                       num-eps = <16>;
+                       ram-bits = <12>;
+                       ti,has-mailbox;
+               };
        };
 };
diff --git a/arch/arm/boot/dts/omap5-evm.dts b/arch/arm/boot/dts/omap5-evm.dts
deleted file mode 100644 (file)
index 8722c15..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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 version 2 as
- * published by the Free Software Foundation.
- */
-/dts-v1/;
-
-/include/ "omap5.dtsi"
-/include/ "samsung_k3pe0e000b.dtsi"
-
-/ {
-       model = "TI OMAP5 EVM board";
-       compatible = "ti,omap5-evm", "ti,omap5";
-
-       memory {
-               device_type = "memory";
-               reg = <0x80000000 0x80000000>; /* 2 GB */
-       };
-
-       vmmcsd_fixed: fixedregulator-mmcsd {
-               compatible = "regulator-fixed";
-               regulator-name = "vmmcsd_fixed";
-               regulator-min-microvolt = <3000000>;
-               regulator-max-microvolt = <3000000>;
-       };
-
-};
-
-&omap5_pmx_core {
-       pinctrl-names = "default";
-       pinctrl-0 = <
-                       &twl6040_pins
-                       &mcpdm_pins
-                       &dmic_pins
-                       &mcbsp1_pins
-                       &mcbsp2_pins
-       >;
-
-       twl6040_pins: pinmux_twl6040_pins {
-               pinctrl-single,pins = <
-                       0x18a 0x6       /* perslimbus2_clock.gpio5_145 OUTPUT | MODE6 */
-               >;
-       };
-
-       mcpdm_pins: pinmux_mcpdm_pins {
-               pinctrl-single,pins = <
-                       0x142 0x108     /* abe_clks.abe_clks INPUT PULLDOWN | MODE0 */
-                       0x15c 0x108     /* abemcpdm_ul_data.abemcpdm_ul_data INPUT PULLDOWN | MODE0 */
-                       0x15e 0x108     /* abemcpdm_dl_data.abemcpdm_dl_data INPUT PULLDOWN | MODE0 */
-                       0x160 0x118     /* abemcpdm_frame.abemcpdm_frame INPUT PULLUP | MODE0 */
-                       0x162 0x108     /* abemcpdm_lb_clk.abemcpdm_lb_clk INPUT PULLDOWN | MODE0 */
-               >;
-       };
-
-       dmic_pins: pinmux_dmic_pins {
-               pinctrl-single,pins = <
-                       0x144 0x100     /* abedmic_din1.abedmic_din1 INPUT | MODE0 */
-                       0x146 0x100     /* abedmic_din2.abedmic_din2 INPUT | MODE0 */
-                       0x148 0x100     /* abedmic_din3.abedmic_din3 INPUT | MODE0 */
-                       0x14a 0         /* abedmic_clk1.abedmic_clk1 OUTPUT | MODE0 */
-               >;
-       };
-
-       mcbsp1_pins: pinmux_mcbsp1_pins {
-               pinctrl-single,pins = <
-                       0x14c 0x101     /* abedmic_clk2.abemcbsp1_fsx INPUT | MODE1 */
-                       0x14e 0x9       /* abedmic_clk3.abemcbsp1_dx OUTPUT PULLDOWN | MODE1 */
-                       0x150 0x101     /* abeslimbus1_clock.abemcbsp1_clkx INPUT | MODE0 */
-                       0x152 0x109     /* abeslimbus1_data.abemcbsp1_dr INPUT PULLDOWN | MODE1 */
-               >;
-       };
-
-       mcbsp2_pins: pinmux_mcbsp2_pins {
-               pinctrl-single,pins = <
-                       0x154 0x108     /* abemcbsp2_dr.abemcbsp2_dr INPUT PULLDOWN | MODE0 */
-                       0x156 0x8       /* abemcbsp2_dx.abemcbsp2_dx OUTPUT PULLDOWN | MODE0 */
-                       0x158 0x100     /* abemcbsp2_fsx.abemcbsp2_fsx INPUT | MODE0 */
-                       0x15a 0x100     /* abemcbsp2_clkx.abemcbsp2_clkx INPUT | MODE0 */
-               >;
-       };
-};
-
-&mmc1 {
-       vmmc-supply = <&vmmcsd_fixed>;
-       bus-width = <4>;
-};
-
-&mmc2 {
-       vmmc-supply = <&vmmcsd_fixed>;
-       bus-width = <8>;
-       ti,non-removable;
-};
-
-&mmc3 {
-       bus-width = <4>;
-       ti,non-removable;
-};
-
-&mmc4 {
-       status = "disabled";
-};
-
-&mmc5 {
-       status = "disabled";
-};
-
-&i2c2 {
-       clock-frequency = <400000>;
-
-       /* Pressure Sensor */
-       bmp085@77 {
-               compatible = "bosch,bmp085";
-               reg = <0x77>;
-       };
-};
-
-&i2c4 {
-       clock-frequency = <400000>;
-
-       /* Temperature Sensor */
-       tmp102@48{
-               compatible = "ti,tmp102";
-               reg = <0x48>;
-       };
-};
-
-&keypad {
-       keypad,num-rows = <8>;
-       keypad,num-columns = <8>;
-       linux,keymap = <0x02020073      /* VOLUP */
-                       0x02030072      /* VOLDOWM */
-                       0x020400e7      /* SEND */
-                       0x02050066      /* HOME */
-                       0x0206006b      /* END */
-                       0x020700d9>;    /* SEARCH */
-       linux,input-no-autorepeat;
-};
-
-&mcbsp3 {
-       status = "disabled";
-};
-
-&emif1 {
-       cs1-used;
-       device-handle = <&samsung_K3PE0E000B>;
-};
-
-&emif2 {
-       cs1-used;
-       device-handle = <&samsung_K3PE0E000B>;
-};
diff --git a/arch/arm/boot/dts/omap5-panda.dts b/arch/arm/boot/dts/omap5-panda.dts
new file mode 100644 (file)
index 0000000..d2c98ac
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+/include/ "omap5-evm.dts"
+
+/ {
+       model = "TI OMAP5 EVM board";
+       compatible = "ti,omap5-panda", "ti,omap5";
+
+       memory {
+               device_type = "memory";
+               reg = <0x80000000 0x80000000>; /* 2 GB */
+       };
+
+       vmmcsd_fixed: fixedregulator-mmcsd {
+               compatible = "regulator-fixed";
+               regulator-name = "vmmcsd_fixed";
+               regulator-min-microvolt = <3000000>;
+               regulator-max-microvolt = <3000000>;
+       };
+
+       sound {
+               compatible = "ti,abe-twl6040";
+               ti,model = "PandaBoard5";
+
+               ti,jack-detection = <1>;
+               ti,mclk-freq = <19200000>;
+
+               ti,mcpdm = <&mcpdm>;
+               ti,mcbsp1 = <&mcbsp1>;
+               ti,mcbsp2 = <&mcbsp2>;
+               ti,aess = <&aess>;
+
+               ti,twl6040 = <&twl6040>;
+
+               /* Audio routing */
+               ti,audio-routing =
+                       "Headset Stereophone", "HSOL",
+                       "Headset Stereophone", "HSOR",
+                       "Line Out", "AUXL",
+                       "Line Out", "AUXR",
+                       "HSMIC", "Headset Mic",
+                       "Headset Mic", "Headset Mic Bias",
+                       "AFML", "Line In",
+                       "AFMR", "Line In",
+                       "Headset Playback", "PDM_DL1",
+                       "PDM_UL1", "Capture",
+                       "40122000.mcbsp Playback", "BT_VX_DL",
+                       "BT_VX_UL", "40122000.mcbsp Capture",
+                       "40124000.mcbsp Playback", "MM_EXT_DL",
+                       "MM_EXT_UL", "40124000.mcbsp Capture";
+       };
+
+       sound_hdmi {
+               compatible = "ti,omap-hdmi-tpd12s015-audio";
+               ti,model = "OMAP5HDMI";
+
+               ti,hdmi_audio = <&hdmi>;
+               ti,level_shifter = <&tpd12s015>;
+       };
+};
+
+/* card detect gpio is different for mmc1 / micro sd card slot */
+&mmc1_pins {
+               pinctrl-single,pins = <
+                       0x1a2 0x118     /* sdcard_clk.sdcard_clk INPUT PULLUP | MODE0 */
+                       0x1a4 0x118     /* sdcard_cmd.sdcard_cmd INPUT PULLUP | MODE0 */
+                       0x1a6 0x118     /* sdcard_data2.sdcard_data2 INPUT PULLUP | MODE0 */
+                       0x1a8 0x118     /* sdcard_data3.sdcard_data3 INPUT PULLUP | MODE0 */
+                       0x1aa 0x118     /* sdcard_data0.sdcard_data0 INPUT PULLUP | MODE0 */
+                       0x1ac 0x118     /* sdcard_data1.sdcard_data1 INPUT PULLUP | MODE0 */
+                       0x194 0x106     /* uart6_rts.gpio5_152 INPUT | MODE6 */
+               >;
+       };
+
+&mmc1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&mmc1_pins>;
+       vmmc-supply = <&vmmcsd_fixed>;
+       bus-width = <4>;
+       cd-gpios = <&gpio5 24 0>; /* gpio 152 */
+};
+
+&mmc2 {
+       vmmc-supply = <&vmmcsd_fixed>;
+       vmmc-aux-supply = <&ldo9_reg>;
+       bus-width = <8>;
+       ti,non-removable;
+};
+
+&mmc3 {
+       bus-width = <4>;
+       ti,non-removable;
+};
+
+&mmc4 {
+       status = "disabled";
+};
+
+&mmc5 {
+       status = "disabled";
+};
+
+&i2c1 {
+       clock-frequency = <400000>;
+
+       palmas: palmas@48 {
+               reg = <0x48>;
+               /* SPI = 0, IRQ# = 7, 4 = active high level-sensitive */
+               interrupts = <0 7 4>; /* IRQ_SYS_1N cascaded to gic */
+               interrupt-parent = <&gic>;
+       };
+
+       twl6040: twl@4b {
+               compatible = "ti,twl6040";
+
+               interrupts = <0 119 4>; /* IRQ_SYS_2N cascaded to gic */
+               interrupt-parent = <&gic>;
+               ti,audpwron-gpio = <&gpio5 13 0>;  /* gpio line 141 */
+
+               vio-supply = <&smps7_reg>;
+               v2v1-supply = <&smps9_reg>;
+               enable-active-high;
+       };
+};
+
+
+&i2c5 {
+       tca6424a: tca6424a@22 {
+               compatible = "ti,tca6424a";
+               reg = <0x22>;
+               gpio-controller;
+               #gpio-cells = <2>;
+       };
+};
+
+&omap5_pmx_core {
+       pinctrl-names = "default";
+       pinctrl-0 = <
+                       &twl6040_pins
+                       &mcpdm_pins
+                       &dmic_pins
+                       &mcbsp1_pins
+                       &mcbsp2_pins
+                       &dss_hdmi_pins
+                       &tpd12s015_pins
+                       &tca6424a_pins
+       >;
+
+       twl6040_pins: pinmux_twl6040_pins {
+               pinctrl-single,pins = <
+                       0x17e 0x6       /* mcspi1_somi.gpio5_141 OUTPUT | MODE6 */
+               >;
+       };
+
+       mcpdm_pins: pinmux_mcpdm_pins {
+               pinctrl-single,pins = <
+                       0x142 0x108     /* abe_clks.abe_clks INPUT PULLDOWN | MODE0 */
+                       0x15c 0x108     /* abemcpdm_ul_data.abemcpdm_ul_data INPUT PULLDOWN | MODE0 */
+                       0x15e 0x108     /* abemcpdm_dl_data.abemcpdm_dl_data INPUT PULLDOWN | MODE0 */
+                       0x160 0x118     /* abemcpdm_frame.abemcpdm_frame INPUT PULLUP | MODE0 */
+                       0x162 0x108     /* abemcpdm_lb_clk.abemcpdm_lb_clk INPUT PULLDOWN | MODE0 */
+               >;
+       };
+
+       dmic_pins: pinmux_dmic_pins {
+               pinctrl-single,pins = <
+                       0x144 0x100     /* abedmic_din1.abedmic_din1 INPUT | MODE0 */
+                       0x146 0x100     /* abedmic_din2.abedmic_din2 INPUT | MODE0 */
+                       0x148 0x100     /* abedmic_din3.abedmic_din3 INPUT | MODE0 */
+                       0x14a 0         /* abedmic_clk1.abedmic_clk1 OUTPUT | MODE0 */
+               >;
+       };
+
+       mcbsp1_pins: pinmux_mcbsp1_pins {
+               pinctrl-single,pins = <
+                       0x14c 0x101     /* abedmic_clk2.abemcbsp1_fsx INPUT | MODE1 */
+                       0x14e 0x9       /* abedmic_clk3.abemcbsp1_dx OUTPUT PULLDOWN | MODE1 */
+                       0x150 0x101     /* abeslimbus1_clock.abemcbsp1_clkx INPUT | MODE0 */
+                       0x152 0x109     /* abeslimbus1_data.abemcbsp1_dr INPUT PULLDOWN | MODE1 */
+               >;
+       };
+
+       mcbsp2_pins: pinmux_mcbsp2_pins {
+               pinctrl-single,pins = <
+                       0x154 0x108     /* abemcbsp2_dr.abemcbsp2_dr INPUT PULLDOWN | MODE0 */
+                       0x156 0x8       /* abemcbsp2_dx.abemcbsp2_dx OUTPUT PULLDOWN | MODE0 */
+                       0x158 0x100     /* abemcbsp2_fsx.abemcbsp2_fsx INPUT | MODE0 */
+                       0x15a 0x100     /* abemcbsp2_clkx.abemcbsp2_clkx INPUT | MODE0 */
+               >;
+       };
+
+       dss_hdmi_pins: pinmux_dss_hdmi_pins {
+               pinctrl-single,pins = <
+                       0x0fc 0x118     /* hdmi_cec.hdmi_cec INPUT PULLUP | MODE 0 */
+                       0x100 0x100     /* hdmi_scl.hdmi_scl INPUT | MODE 0 */
+                       0x102 0x100     /* hdmi_sda.hdmi_sda INPUT | MODE 0 */
+               >;
+       };
+
+       tpd12s015_pins: pinmux_tpd12s015_pins {
+               pinctrl-single,pins = <
+                       0x0fe 0x116     /* hdmi_hpd.gpio7_193 INPUT PULLDOWN | MODE6 */
+               >;
+       };
+
+       tca6424a_pins: pinmux_tca6424a_pins {
+               pinctrl-single,pins = <
+                       0x186 0x100     /* i2c5_scl.i2c5_scl INPUT | MODE0 */
+                       0x188 0x100     /* i2c5_sda.i2c5_sda INPUT | MODE0 */
+               >;
+       };
+};
+
+&hdmi {
+       tpd12s015: tpd12s015 {
+               compatible = "ti,tpd12s015";
+
+               gpios = <&tca6424a 0 0>,        /* TCA6424A P01, CT_CP_HDP */
+                       <&tca6424a 1 0>,        /* TCA6424A P00, LS_OE*/
+                       <&gpio7 1 0>;           /* 193, HPD */
+
+               hdmi-monitor {
+                       compatible = "ti,hdmi_panel";
+               };
+
+       };
+};
+
+/include/ "palmas.dtsi"
+/include/ "twl6040.dtsi"
diff --git a/arch/arm/boot/dts/omap5-sevm.dts b/arch/arm/boot/dts/omap5-sevm.dts
new file mode 100644 (file)
index 0000000..1cf708d
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+/dts-v1/;
+
+/include/ "omap5.dtsi"
+/include/ "samsung_k3pe0e000b.dtsi"
+
+/ {
+       model = "TI OMAP5 sEVM board";
+       compatible = "ti,omap5-sevm", "ti,omap5";
+
+       memory {
+               device_type = "memory";
+               reg = <0x80000000 0x7F000000>; /* 2032 MB */
+       };
+
+       vmmcsd_fixed: fixedregulator-mmcsd {
+               compatible = "regulator-fixed";
+               regulator-name = "vmmcsd_fixed";
+               regulator-min-microvolt = <3000000>;
+               regulator-max-microvolt = <3000000>;
+       };
+
+       /* HS USB Port 2 RESET */
+       hsusb2_reset: hsusb2_reset_reg {
+               compatible = "regulator-fixed";
+               regulator-name = "hsusb2_reset";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               gpio = <&gpio6 13 0>;   /* gpio6_173 HUB_NRESET */
+               startup-delay-us = <70000>;
+               enable-active-high;
+       };
+
+       /* HS USB Host PHY on PORT 2 */
+       hsusb2_phy: hsusb2_phy {
+               compatible = "usb-nop-xceiv";
+               reset-supply = <&hsusb2_reset>;
+       };
+
+       /* HS USB Port 3 RESET */
+       hsusb3_reset: hsusb3_reset_reg {
+               compatible = "regulator-fixed";
+               regulator-name = "hsusb3_reset";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               gpio = <&gpio6 12 0>;   /* gpio6_172 ETH_NRESET */
+               startup-delay-us = <70000>;
+               enable-active-high;
+       };
+
+       /* HS USB Host PHY on PORT 3 */
+       hsusb3_phy: hsusb3_phy {
+               compatible = "usb-nop-xceiv";
+               reset-supply = <&hsusb3_reset>;
+       };
+
+       sound {
+               compatible = "ti,abe-twl6040";
+               ti,model = "OMAP5-sEVM";
+
+               ti,jack-detection = <1>;
+               ti,mclk-freq = <19200000>;
+
+               ti,mcpdm = <&mcpdm>;
+               ti,dmic = <&dmic>;
+               ti,mcasp = <&mcasp>;
+               ti,mcbsp1 = <&mcbsp1>;
+               ti,mcbsp2 = <&mcbsp2>;
+               ti,aess = <&aess>;
+
+               ti,twl6040 = <&twl6040>;
+
+               /* Audio routing */
+               ti,audio-routing =
+                       "Headset Stereophone", "HSOL",
+                       "Headset Stereophone", "HSOR",
+                       "Earphone Spk", "EP",
+                       "Ext Spk", "HFL",
+                       "Ext Spk", "HFR",
+                       "Line Out", "AUXL",
+                       "Line Out", "AUXR",
+                       "HSMIC", "Headset Mic",
+                       "Headset Mic", "Headset Mic Bias",
+                       "MAINMIC", "Main Handset Mic",
+                       "Main Handset Mic", "Main Mic Bias",
+                       "SUBMIC", "Sub Handset Mic",
+                       "Sub Handset Mic", "Main Mic Bias",
+                       "AFML", "Line In",
+                       "AFMR", "Line In",
+                       "DMic", "Digital Mic",
+                       "Digital Mic", "Digital Mic1 Bias",
+                       "Headset Playback", "PDM_DL1",
+                       "Handsfree Playback", "PDM_DL2",
+                       "PDM_UL1", "Capture",
+                       "40122000.mcbsp Playback", "BT_VX_DL",
+                       "BT_VX_UL", "40122000.mcbsp Capture",
+                       "40124000.mcbsp Playback", "MM_EXT_DL",
+                       "MM_EXT_UL", "40124000.mcbsp Capture",
+                       "DMIC0", "omap-dmic-abe.0 Capture",
+                       "omap-dmic-abe.0 Capture", "Digital Mic1 Bias",
+                       "Digital Mic1 Bias", "Digital Mic 0",
+                       "DMIC1", "omap-dmic-abe.1 Capture",
+                       "omap-dmic-abe.1 Capture", "Digital Mic1 Bias",
+                       "Digital Mic1 Bias", "Digital Mic 1",
+                       "DMIC2", "omap-dmic-abe.2 Capture",
+                       "omap-dmic-abe.2 Capture", "Digital Mic1 Bias",
+                       "Digital Mic1 Bias", "Digital Mic 2";
+       };
+
+       sound_hdmi {
+               compatible = "ti,omap-hdmi-tpd12s015-audio";
+               ti,model = "OMAP5HDMI";
+
+               ti,hdmi_audio = <&hdmi>;
+               ti,level_shifter = <&tpd12s015>;
+       };
+
+};
+
+&omap5_pmx_core {
+       pinctrl-names = "default";
+       pinctrl-0 = <
+                       &twl6040_pins
+                       &mcpdm_pins
+                       &dmic_pins
+                       &mcbsp1_pins
+                       &mcbsp2_pins
+                       &usbhost_pins
+                       &lg4591_pins
+                       &dss_hdmi_pins
+                       &tpd12s015_pins
+                       &tca6424a_pins
+       >;
+
+       mmc1_pins: pinmux_mmc1_pins {
+               pinctrl-single,pins = <
+                       0x1a2 0x118     /* sdcard_clk.sdcard_clk INPUT PULLUP | MODE0 */
+                       0x1a4 0x118     /* sdcard_cmd.sdcard_cmd INPUT PULLUP | MODE0 */
+                       0x1a6 0x118     /* sdcard_data2.sdcard_data2 INPUT PULLUP | MODE0 */
+                       0x1a8 0x118     /* sdcard_data3.sdcard_data3 INPUT PULLUP | MODE0 */
+                       0x1aa 0x118     /* sdcard_data0.sdcard_data0 INPUT PULLUP | MODE0 */
+                       0x1ac 0x118     /* sdcard_data1.sdcard_data1 INPUT PULLUP | MODE0 */
+               >;
+       };
+
+       mmc2_pins: pinmux_mmc2_pins {
+               pinctrl-single,pins = <
+                       0x0 0x118       /* emmc_clk.emmc_clk INPUT PULLUP | MODE0 */
+                       0x2 0x118       /* emmc_cmd.emmc_cmd INPUT PULLUP | MODE0 */
+                       0x4 0x118       /* emmc_data0.emmc_data0 INPUT PULLUP | MODE0 */
+                       0x6 0x118       /* emmc_data1.emmc_data1 INPUT PULLUP | MODE0 */
+                       0x8 0x118       /* emmc_data2.emmc_data2 INPUT PULLUP | MODE0 */
+                       0xa 0x118       /* emmc_data3.emmc_data3 INPUT PULLUP | MODE0 */
+                       0xc 0x118       /* emmc_data4.emmc_data4 INPUT PULLUP | MODE0 */
+                       0xe 0x118       /* emmc_data5.emmc_data5 INPUT PULLUP | MODE0 */
+                       0x10 0x118      /* emmc_data6.emmc_data6 INPUT PULLUP | MODE0 */
+                       0x12 0x118      /* emmc_data7.emmc_data7 INPUT PULLUP | MODE0 */
+               >;
+       };
+
+       twl6040_pins: pinmux_twl6040_pins {
+               pinctrl-single,pins = <
+                       0x18a 0x6       /* perslimbus2_clock.gpio5_145 OUTPUT | MODE6 */
+               >;
+       };
+
+       mcpdm_pins: pinmux_mcpdm_pins {
+               pinctrl-single,pins = <
+                       0x142 0x108     /* abe_clks.abe_clks INPUT PULLDOWN | MODE0 */
+                       0x15c 0x108     /* abemcpdm_ul_data.abemcpdm_ul_data INPUT PULLDOWN | MODE0 */
+                       0x15e 0x108     /* abemcpdm_dl_data.abemcpdm_dl_data INPUT PULLDOWN | MODE0 */
+                       0x160 0x118     /* abemcpdm_frame.abemcpdm_frame INPUT PULLUP | MODE0 */
+                       0x162 0x108     /* abemcpdm_lb_clk.abemcpdm_lb_clk INPUT PULLDOWN | MODE0 */
+               >;
+       };
+
+       dmic_pins: pinmux_dmic_pins {
+               pinctrl-single,pins = <
+                       0x144 0x100     /* abedmic_din1.abedmic_din1 INPUT | MODE0 */
+                       0x146 0x100     /* abedmic_din2.abedmic_din2 INPUT | MODE0 */
+                       0x148 0x100     /* abedmic_din3.abedmic_din3 INPUT | MODE0 */
+                       0x14a 0         /* abedmic_clk1.abedmic_clk1 OUTPUT | MODE0 */
+               >;
+       };
+
+       mcbsp1_pins: pinmux_mcbsp1_pins {
+               pinctrl-single,pins = <
+                       0x14c 0x101     /* abedmic_clk2.abemcbsp1_fsx INPUT | MODE1 */
+                       0x14e 0x9       /* abedmic_clk3.abemcbsp1_dx OUTPUT PULLDOWN | MODE1 */
+                       0x150 0x101     /* abeslimbus1_clock.abemcbsp1_clkx INPUT | MODE0 */
+                       0x152 0x109     /* abeslimbus1_data.abemcbsp1_dr INPUT PULLDOWN | MODE1 */
+               >;
+       };
+
+       mcbsp2_pins: pinmux_mcbsp2_pins {
+               pinctrl-single,pins = <
+                       0x154 0x108     /* abemcbsp2_dr.abemcbsp2_dr INPUT PULLDOWN | MODE0 */
+                       0x156 0x8       /* abemcbsp2_dx.abemcbsp2_dx OUTPUT PULLDOWN | MODE0 */
+                       0x158 0x100     /* abemcbsp2_fsx.abemcbsp2_fsx INPUT | MODE0 */
+                       0x15a 0x100     /* abemcbsp2_clkx.abemcbsp2_clkx INPUT | MODE0 */
+               >;
+       };
+
+       usbhost_pins: pinmux_usbhost_pins {
+               pinctrl-single,pins = <
+                       0x84 0x100      /* usbb2_hsic_strobe INPUT | MODE 0 */
+                       0x86 0x100      /* usbb2_hsic_data INPUT | MODE 0 */
+
+                       0x19e 0x100     /* usbb3_hsic_strobe INPUT | MODE 0 */
+                       0x1a0 0x100     /* usbb3_hsic_data INPUT | MODE 0 */
+
+                       0xD4 0x6        /* gpio6_173 OUTPUT | MODE 6 HUB_NRESET */
+                       0xD6 0x6        /* gpio6_172 OUTPUT | MODE 6 ETH_NRESET */
+               >;
+       };
+
+        i2c1_pins: pinmux_i2c1_pins {
+                pinctrl-single,pins = <
+                        0x1b2 0x118        /* i2c1_scl PULLUP | INPUTENABLE | MODE0 */
+                        0x1b4 0x118        /* i2c1_sda PULLUP | INPUTENABLE | MODE0 */
+                >;
+        };
+
+       i2c2_pins: pinmux_i2c2_pins {
+               pinctrl-single,pins = <
+                       0x178 0x100        /* i2c2_scl INPUTENABLE | MODE0 */
+                       0x17a 0x100        /* i2c2_sda INPUTENABLE | MODE0 */
+               >;
+       };
+
+       i2c3_pins: pinmux_i2c3_pins {
+               pinctrl-single,pins = <
+                       0x13a 0x100        /* i2c3_scl INPUTENABLE | MODE0 */
+                       0x13c 0x100     /* i2c3_sda INPUTENABLE | MODE0 */
+               >;
+       };
+
+       i2c4_pins: pinmux_i2c4_pins {
+               pinctrl-single,pins = <
+                       0xb8 0x100        /* i2c4_scl INPUTENABLE | MODE0 */
+                       0xba 0x100     /* i2c4_sda INPUTENABLE | MODE0 */
+               >;
+       };
+
+       i2c5_pins: pinmux_i2c5_pins {
+               pinctrl-single,pins = <
+                       0x184 0x100        /* i2c5_scl INPUTENABLE | MODE0 */
+                       0x186 0x100     /* i2c5_sda INPUTENABLE | MODE0 */
+               >;
+       };
+
+       mcspi2_pins: pinmux_mcspi2_pins {
+               pinctrl-single,pins = <
+                       0xbc 0x100      /*  MCSPI2_CLK INPUTENABLE | MODE0 */
+                       0xbe 0x100      /*  MCSPI2_SIMO INPUTENABLE | MODE0 */
+                       0xc0 0x118      /*  MCSPI2_SOMI PULLUP | INPUTENABLE | MODE0*/
+                       0xc2 0x0        /*  MCSPI2_CS MODE0*/
+               >;
+       };
+
+       mcspi3_pins: pinmux_mcspi3_pins {
+               pinctrl-single,pins = <
+                       0x78 0x101      /*  MCSPI2_SOMI INPUTENABLE | MODE1 */
+                       0x7a 0x101      /*  MCSPI2_CS INPUTENABLE | MODE1 */
+                       0x7c 0x101      /*  MCSPI2_SIMO INPUTENABLE | MODE1 */
+                       0x7e 0x101      /*  MCSPI2_CLK INPUTENABLE | MODE1 */
+               >;
+       };
+
+       mcspi4_pins: pinmux_mcspi4_pins {
+               pinctrl-single,pins = <
+                       0x164 0x101     /*  MCSPI2_CLK INPUTENABLE | MODE1 */
+                       0x168 0x101     /*  MCSPI2_SIMO INPUTENABLE | MODE1 */
+                       0x16a 0x101     /*  MCSPI2_SOMI INPUTENABLE | MODE1 */
+                       0x16c 0x101     /*  MCSPI2_CS INPUTENABLE | MODE1 */
+               >;
+       };
+
+       lg4591_pins: pinmux_lg4591_pins {
+               pinctrl-single,pins = <
+                       0xf2 0x8        /* perslimbus2_clock.gpio6_183 OUTPUT PULLDOWN | MODE0 */
+               >;
+       };
+
+       dss_hdmi_pins: pinmux_dss_hdmi_pins {
+               pinctrl-single,pins = <
+                       0x0fc 0x118     /* hdmi_cec.hdmi_cec INPUT PULLUP | MODE 0 */
+                       0x100 0x118     /* hdmi_scl.hdmi_scl INPUT PULLUP | MODE 0 */
+                       0x102 0x118     /* hdmi_sda.hdmi_sda INPUT PULLUP | MODE 0 */
+               >;
+       };
+
+       tpd12s015_pins: pinmux_tpd12s015_pins {
+               pinctrl-single,pins = <
+                       0x0fe 0x116     /* hdmi_hpd.gpio7_193 INPUT PULLDOWN | MODE6 */
+               >;
+       };
+
+       tca6424a_pins: pinmux_tca6424a_pins {
+               pinctrl-single,pins = <
+                       0x186 0x100     /* i2c5_scl.i2c5_scl INPUT | MODE0 */
+                       0x188 0x100     /* i2c5_sda.i2c5_sda INPUT | MODE0 */
+               >;
+       };
+
+};
+
+&mmc1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&mmc1_pins>;
+       vmmc-supply = <&vmmcsd_fixed>;
+       vmmc-aux-supply = <&ldo9_reg>;
+       bus-width = <4>;
+       cd-gpios = <&gpio3 3 0>; /* gpio 67 */
+};
+
+&mmc2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&mmc2_pins>;
+       vmmc-supply = <&vmmcsd_fixed>;
+       vmmc-aux-supply = <&ldo9_reg>;
+       bus-width = <8>;
+       ti,non-removable;
+};
+
+&mmc3 {
+       bus-width = <4>;
+       ti,non-removable;
+};
+
+&mmc4 {
+       status = "disabled";
+};
+
+&mmc5 {
+       status = "disabled";
+};
+
+&i2c1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c1_pins>;
+
+       clock-frequency = <400000>;
+};
+
+&i2c2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c2_pins>;
+
+       clock-frequency = <400000>;
+
+       /* Pressure Sensor */
+       bmp085@77 {
+               compatible = "bosch,bmp085";
+               reg = <0x77>;
+       };
+};
+
+&i2c3 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c3_pins>;
+
+       clock-frequency = <400000>;
+};
+
+&i2c4 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c4_pins>;
+
+       clock-frequency = <400000>;
+
+       /* Temperature Sensor */
+       tmp102@48{
+               compatible = "ti,tmp102";
+               reg = <0x48>;
+       };
+};
+
+&i2c5 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c5_pins>;
+       clock-frequency = <400000>;
+
+       tca6424a: tca6424a@22 {
+               compatible = "ti,tca6424a";
+               reg = <0x22>;
+               gpio-controller;
+               #gpio-cells = <2>;
+       };
+};
+
+&keypad {
+       keypad,num-rows = <8>;
+       keypad,num-columns = <8>;
+       linux,keymap = <0x02020073      /* VOLUP */
+                       0x02030072      /* VOLDOWM */
+                       0x020400e7      /* SEND */
+                       0x02050066      /* HOME */
+                       0x0206006b      /* END */
+                       0x020700d9>;    /* SEARCH */
+       linux,input-no-autorepeat;
+};
+
+&mcbsp3 {
+       status = "disabled";
+};
+
+&emif1 {
+       cs1-used;
+       device-handle = <&samsung_K3PE0E000B>;
+};
+
+&emif2 {
+       cs1-used;
+       device-handle = <&samsung_K3PE0E000B>;
+};
+
+&usbhshost {
+       port2-mode = "ehci-hsic";
+       port3-mode = "ehci-hsic";
+};
+
+&usbhsehci {
+       phys = <0 &hsusb2_phy &hsusb3_phy>;
+};
+
+&i2c1 {
+       clock-frequency = <400000>;
+
+       palmas: palmas@48 {
+               reg = <0x48>;
+               /* SPI = 0, IRQ# = 7, 4 = active high level-sensitive */
+               interrupts = <0 7 4>; /* IRQ_SYS_1N cascaded to gic */
+               interrupt-parent = <&gic>;
+       };
+
+       twl6040: twl@4b {
+               compatible = "ti,twl6040";
+
+               interrupts = <0 119 4>; /* IRQ_SYS_2N cascaded to gic */
+               interrupt-parent = <&gic>;
+               ti,audpwron-gpio = <&gpio5 17 0>;  /* gpio line 145 */
+
+               vio-supply = <&smps7_reg>;
+               v2v1-supply = <&smps9_reg>;
+               enable-active-high;
+       };
+};
+
+/include/ "palmas.dtsi"
+
+&mcspi1 {
+
+};
+
+&mcspi2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&mcspi2_pins>;
+};
+
+&mcspi3 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&mcspi3_pins>;
+};
+
+&mcspi4 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&mcspi4_pins>;
+};
+
+/include/ "twl6040.dtsi"
+
+&dsi1 {
+       lcd {
+               compatible = "ti,lg4591";
+
+               lanes = <
+                       0       /* clk + */
+                       1       /* clk - */
+                       2       /* data1 + */
+                       3       /* data1 - */
+                       4       /* data2 + */
+                       5       /* data2 - */
+                       6       /* data3 + */
+                       7       /* data3 - */
+                       8       /* data4 + */
+                       9       /* data4 - */
+               >;
+
+               vdds_foo-supply = <&ldo2_reg>;
+
+               gpios = <&gpio6 23 0>;  /* 183, reset */
+       };
+};
+
+&hdmi {
+       tpd12s015: tpd12s015 {
+               compatible = "ti,tpd12s015";
+
+               gpios = <&tca6424a 0 0>,        /* TCA6424A P01, CT_CP_HDP */
+                       <&tca6424a 1 0>,        /* TCA6424A P00, LS_OE*/
+                       <&gpio7 1 0>;           /* 193, HPD */
+
+               hdmi-monitor {
+                       compatible = "ti,hdmi_panel";
+               };
+
+       };
+};
+
diff --git a/arch/arm/boot/dts/omap5-uevm.dts b/arch/arm/boot/dts/omap5-uevm.dts
new file mode 100644 (file)
index 0000000..8f7e385
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+/dts-v1/;
+
+/include/ "omap5.dtsi"
+/include/ "samsung_k3pe0e000b.dtsi"
+
+/ {
+       model = "TI OMAP5 uEVM board";
+       compatible = "ti,omap5-uevm", "ti,omap5";
+
+       memory {
+               device_type = "memory";
+               reg = <0x80000000 0x7F000000>; /* 2032 MB */
+       };
+
+       vmmcsd_fixed: fixedregulator-mmcsd {
+               compatible = "regulator-fixed";
+               regulator-name = "vmmcsd_fixed";
+               regulator-min-microvolt = <3000000>;
+               regulator-max-microvolt = <3000000>;
+       };
+
+       /* HS USB Port 2 RESET */
+       hsusb2_reset: hsusb2_reset_reg {
+               compatible = "regulator-fixed";
+               regulator-name = "hsusb2_reset";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               gpio = <&gpio3 16 0>;   /* gpio3_80 HUB_NRESET */
+               startup-delay-us = <70000>;
+               enable-active-high;
+       };
+
+       /* HS USB Host PHY on PORT 2 */
+       hsusb2_phy: hsusb2_phy {
+               compatible = "usb-nop-xceiv";
+               reset-supply = <&hsusb2_reset>;
+       };
+
+       /* HS USB Port 3 RESET */
+       hsusb3_reset: hsusb3_reset_reg {
+               compatible = "regulator-fixed";
+               regulator-name = "hsusb3_reset";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+               gpio = <&gpio3 15 0>;   /* gpio3_79 ETH_NRESET */
+               startup-delay-us = <70000>;
+               enable-active-high;
+       };
+
+       /* HS USB Host PHY on PORT 3 */
+       hsusb3_phy: hsusb3_phy {
+               compatible = "usb-nop-xceiv";
+               reset-supply = <&hsusb3_reset>;
+       };
+
+       /* hsusb2_phy is clocked by FREF_CLK1 i.e. auxclk1 */
+       clock_alias {
+               clock-name = "auxclk1_ck";
+               clock-alias = "main_clk";
+               device = <&hsusb2_phy>;
+               clock-frequency = <19200000>; /* 19.2 MHz */
+       };
+
+       sound {
+               compatible = "ti,abe-twl6040";
+               ti,model = "PandaBoard5";
+
+               ti,jack-detection = <1>;
+               ti,mclk-freq = <19200000>;
+
+               ti,mcpdm = <&mcpdm>;
+               ti,mcbsp1 = <&mcbsp1>;
+               ti,mcbsp2 = <&mcbsp2>;
+               ti,aess = <&aess>;
+
+               ti,twl6040 = <&twl6040>;
+
+               /* Audio routing */
+               ti,audio-routing =
+                       "Headset Stereophone", "HSOL",
+                       "Headset Stereophone", "HSOR",
+                       "Line Out", "AUXL",
+                       "Line Out", "AUXR",
+                       "HSMIC", "Headset Mic",
+                       "Headset Mic", "Headset Mic Bias",
+                       "AFML", "Line In",
+                       "AFMR", "Line In",
+                       "Headset Playback", "PDM_DL1",
+                       "PDM_UL1", "Capture",
+                       "40122000.mcbsp Playback", "BT_VX_DL",
+                       "BT_VX_UL", "40122000.mcbsp Capture",
+                       "40124000.mcbsp Playback", "MM_EXT_DL",
+                       "MM_EXT_UL", "40124000.mcbsp Capture";
+       };
+
+       sound_hdmi {
+               compatible = "ti,omap-hdmi-tpd12s015-audio";
+               ti,model = "OMAP5HDMI";
+
+               ti,hdmi_audio = <&hdmi>;
+               ti,level_shifter = <&tpd12s015>;
+       };
+};
+
+&omap5_pmx_core {
+       pinctrl-names = "default";
+       pinctrl-0 = <
+                       &twl6040_pins
+                       &mcpdm_pins
+                       &dmic_pins
+                       &mcbsp1_pins
+                       &mcbsp2_pins
+                       &usbhost_pins
+                       &dss_hdmi_pins
+                       &tpd12s015_pins
+                       &tca6424a_pins
+       >;
+
+       twl6040_pins: pinmux_twl6040_pins {
+               pinctrl-single,pins = <
+                       0x17e 0x6       /* mcspi1_somi.gpio5_141 OUTPUT | MODE6 */
+                       0x18a 0x6       /* perslimbus2_clock.gpio5_145 OUTPUT | MODE6 */
+               >;
+       };
+
+       mcpdm_pins: pinmux_mcpdm_pins {
+               pinctrl-single,pins = <
+                       0x142 0x108     /* abe_clks.abe_clks INPUT PULLDOWN | MODE0 */
+                       0x15c 0x108     /* abemcpdm_ul_data.abemcpdm_ul_data INPUT PULLDOWN | MODE0 */
+                       0x15e 0x108     /* abemcpdm_dl_data.abemcpdm_dl_data INPUT PULLDOWN | MODE0 */
+                       0x160 0x118     /* abemcpdm_frame.abemcpdm_frame INPUT PULLUP | MODE0 */
+                       0x162 0x108     /* abemcpdm_lb_clk.abemcpdm_lb_clk INPUT PULLDOWN | MODE0 */
+               >;
+       };
+
+       dmic_pins: pinmux_dmic_pins {
+               pinctrl-single,pins = <
+                       0x144 0x100     /* abedmic_din1.abedmic_din1 INPUT | MODE0 */
+                       0x146 0x100     /* abedmic_din2.abedmic_din2 INPUT | MODE0 */
+                       0x148 0x100     /* abedmic_din3.abedmic_din3 INPUT | MODE0 */
+                       0x14a 0         /* abedmic_clk1.abedmic_clk1 OUTPUT | MODE0 */
+               >;
+       };
+
+       mcbsp1_pins: pinmux_mcbsp1_pins {
+               pinctrl-single,pins = <
+                       0x14c 0x101     /* abedmic_clk2.abemcbsp1_fsx INPUT | MODE1 */
+                       0x14e 0x9       /* abedmic_clk3.abemcbsp1_dx OUTPUT PULLDOWN | MODE1 */
+                       0x150 0x101     /* abeslimbus1_clock.abemcbsp1_clkx INPUT | MODE0 */
+                       0x152 0x109     /* abeslimbus1_data.abemcbsp1_dr INPUT PULLDOWN | MODE1 */
+               >;
+       };
+
+       mcbsp2_pins: pinmux_mcbsp2_pins {
+               pinctrl-single,pins = <
+                       0x154 0x108     /* abemcbsp2_dr.abemcbsp2_dr INPUT PULLDOWN | MODE0 */
+                       0x156 0x8       /* abemcbsp2_dx.abemcbsp2_dx OUTPUT PULLDOWN | MODE0 */
+                       0x158 0x100     /* abemcbsp2_fsx.abemcbsp2_fsx INPUT | MODE0 */
+                       0x15a 0x100     /* abemcbsp2_clkx.abemcbsp2_clkx INPUT | MODE0 */
+               >;
+       };
+
+       usbhost_pins: pinmux_usbhost_pins {
+               pinctrl-single,pins = <
+                       0x84 0x100      /* usbb2_hsic_strobe INPUT | MODE 0 */
+                       0x86 0x100      /* usbb2_hsic_data INPUT | MODE 0 */
+
+                       0x19e 0x100     /* usbb3_hsic_strobe INPUT | MODE 0 */
+                       0x1a0 0x100     /* usbb3_hsic_data INPUT | MODE 0 */
+
+                       0x70 0x6        /* gpio3_80 OUTPUT | MODE 6 HUB_NRESET */
+                       0x6e 0x6        /* gpio3_79 OUTPUT | MODE 6 ETH_NRESET */
+               >;
+       };
+};
+
+&omap5_pmx_wkup {
+       pinctrl-names = "default";
+       pinctrl-0 = <
+                       &usbhost_wkup_pins
+       >;
+
+       usbhost_wkup_pins: pinmux_usbhost_wkup_pins {
+               pinctrl-single,pins = <
+                       0x1A 0x0        /* fref_clk1_out OUTPUT | MODE 7 for USB hub clk */
+               >;
+       };
+
+       dss_hdmi_pins: pinmux_dss_hdmi_pins {
+               pinctrl-single,pins = <
+                       0x0fc 0x118     /* hdmi_cec.hdmi_cec INPUT PULLUP | MODE 0 */
+                       0x100 0x100     /* hdmi_scl.hdmi_scl INPUT | MODE 0 */
+                       0x102 0x100     /* hdmi_sda.hdmi_sda INPUT | MODE 0 */
+               >;
+       };
+
+       tpd12s015_pins: pinmux_tpd12s015_pins {
+               pinctrl-single,pins = <
+                       0x0fe 0x116     /* hdmi_hpd.gpio7_193 INPUT PULLDOWN | MODE6 */
+               >;
+       };
+
+       tca6424a_pins: pinmux_tca6424a_pins {
+               pinctrl-single,pins = <
+                       0x186 0x100     /* i2c5_scl.i2c5_scl INPUT | MODE0 */
+                       0x188 0x100     /* i2c5_sda.i2c5_sda INPUT | MODE0 */
+               >;
+       };
+};
+
+&mmc1 {
+       vmmc-supply = <&vmmcsd_fixed>;
+       bus-width = <4>;
+};
+
+&mmc2 {
+       vmmc-supply = <&vmmcsd_fixed>;
+       bus-width = <8>;
+       ti,non-removable;
+};
+
+&mmc3 {
+       bus-width = <4>;
+       ti,non-removable;
+};
+
+&mmc4 {
+       status = "disabled";
+};
+
+&mmc5 {
+       status = "disabled";
+};
+
+&i2c2 {
+       clock-frequency = <400000>;
+
+       /* Pressure Sensor */
+       bmp085@77 {
+               compatible = "bosch,bmp085";
+               reg = <0x77>;
+       };
+};
+
+&i2c4 {
+       clock-frequency = <400000>;
+
+       /* Temperature Sensor */
+       tmp102@48{
+               compatible = "ti,tmp102";
+               reg = <0x48>;
+       };
+};
+
+&i2c5 {
+       tca6424a: tca6424a@22 {
+               compatible = "ti,tca6424a";
+               reg = <0x22>;
+               gpio-controller;
+               #gpio-cells = <2>;
+       };
+};
+
+&mcbsp3 {
+       status = "disabled";
+};
+
+&emif1 {
+       cs1-used;
+       device-handle = <&samsung_K3PE0E000B>;
+};
+
+&emif2 {
+       cs1-used;
+       device-handle = <&samsung_K3PE0E000B>;
+};
+
+&i2c1 {
+       clock-frequency = <400000>;
+
+       palmas: palmas@48 {
+               reg = <0x48>;
+               /* SPI = 0, IRQ# = 7, 4 = active high level-sensitive */
+               interrupts = <0 7 4>; /* IRQ_SYS_1N cascaded to gic */
+               interrupt-parent = <&gic>;
+       };
+
+       twl6040: twl@4b {
+               compatible = "ti,twl6040";
+
+               interrupts = <0 119 4>; /* IRQ_SYS_2N cascaded to gic */
+               interrupt-parent = <&gic>;
+               ti,audpwron-gpio = <&gpio5 13 0>;  /* gpio line 141 */
+
+               vio-supply = <&smps7_reg>;
+               v2v1-supply = <&smps9_reg>;
+               enable-active-high;
+       };
+};
+
+&usbhshost {
+       port2-mode = "ehci-hsic";
+       port3-mode = "ehci-hsic";
+};
+
+&usbhsehci {
+       phys = <0 &hsusb2_phy &hsusb3_phy>;
+};
+
+/include/ "palmas.dtsi"
+/include/ "twl6040.dtsi"
+
+&hdmi {
+       tpd12s015: tpd12s015 {
+               compatible = "ti,tpd12s015";
+
+               gpios = <&tca6424a 0 0>,        /* TCA6424A P01, CT_CP_HDP */
+                       <&tca6424a 1 0>,        /* TCA6424A P00, LS_OE*/
+                       <&gpio7 1 0>;           /* 193, HPD */
+
+               hdmi-monitor {
+                       compatible = "ti,hdmi_panel";
+               };
+
+       };
+};
index 790bb2a4b3434d47a500005da78ec20d385afd48..9be3b47de94f62cfd14d4f2f9a30a6dae3d5f001 100644 (file)
@@ -18,6 +18,9 @@
 /include/ "skeleton.dtsi"
 
 / {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
        compatible = "ti,omap5";
        interrupt-parent = <&gic>;
 
        cpus {
                cpu@0 {
                        compatible = "arm,cortex-a15";
-                       timer {
-                               compatible = "arm,armv7-timer";
-                               /* 14th PPI IRQ, active low level-sensitive */
-                               interrupts = <1 14 0x308>;
-                               clock-frequency = <6144000>;
-                       };
                };
                cpu@1 {
                        compatible = "arm,cortex-a15";
-                       timer {
-                               compatible = "arm,armv7-timer";
-                               /* 14th PPI IRQ, active low level-sensitive */
-                               interrupts = <1 14 0x308>;
-                               clock-frequency = <6144000>;
-                       };
                };
        };
 
+       timer {
+               compatible = "arm,armv7-timer";
+               /* PPI secure/nonsecure IRQ, active low level-sensitive */
+               interrupts = <1 13 0x308>,
+                            <1 14 0x308>,
+                            <1 11 0x308>,
+                            <1 10 0x308>;
+               clock-frequency = <6144000>;
+       };
+
+       gic: interrupt-controller@48211000 {
+               compatible = "arm,cortex-a15-gic";
+               interrupt-controller;
+               #interrupt-cells = <3>;
+               reg = <0x48211000 0x1000>,
+                     <0x48212000 0x1000>,
+                     <0x48214000 0x2000>,
+                     <0x48216000 0x2000>;
+       };
+
        /*
         * The soc node represents the soc top level view. It is uses for IPs
         * that are not memory mapped in the MPU view or for the MPU itself.
                        pinctrl-single,function-mask = <0x7fff>;
                };
 
-               gic: interrupt-controller@48211000 {
-                       compatible = "arm,cortex-a15-gic";
-                       interrupt-controller;
-                       #interrupt-cells = <3>;
-                       reg = <0x48211000 0x1000>,
-                             <0x48212000 0x1000>;
-               };
-
                gpio1: gpio@4ae10000 {
                        compatible = "ti,omap4-gpio";
                        reg = <0x4ae10000 0x200>;
                        ti,hwmods = "i2c5";
                };
 
+               mcspi1: spi@48098000 {
+                       compatible = "ti,omap4-mcspi";
+                       reg = <0x48098000 0x200>;
+                       interrupts = <0 65 0x4>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       ti,hwmods = "mcspi1";
+                       ti,spi-num-cs = <4>;
+               };
+
+               mcspi2: spi@4809a000 {
+                       compatible = "ti,omap4-mcspi";
+                       reg = <0x4809a000 0x200>;
+                       interrupts = <0 66 0x4>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       ti,hwmods = "mcspi2";
+                       ti,spi-num-cs = <2>;
+               };
+
+               mcspi3: spi@480b8000 {
+                       compatible = "ti,omap4-mcspi";
+                       reg = <0x480b8000 0x200>;
+                       interrupts = <0 91 0x4>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       ti,hwmods = "mcspi3";
+                       ti,spi-num-cs = <2>;
+               };
+
+               mcspi4: spi@480ba000 {
+                       compatible = "ti,omap4-mcspi";
+                       reg = <0x480ba000 0x200>;
+                       interrupts = <0 48 0x4>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       ti,hwmods = "mcspi4";
+                       ti,spi-num-cs = <1>;
+               };
+
                uart1: serial@4806a000 {
                        compatible = "ti,omap4-uart";
                        reg = <0x4806a000 0x100>;
 
                keypad: keypad@4ae1c000 {
                        compatible = "ti,omap4-keypad";
+                       reg = <0x4ae1c000 0x400>;
                        ti,hwmods = "kbd";
                };
 
+               aess: aess@0x401f1000 {
+                       compatible = "ti,omap4-aess";
+                       reg = <0x401f1000 0x3ff>, /* MPU private access */
+                             <0x40180000 0xffff>, /* DMEM - MPU */
+                             <0x401a0000 0x1fff>, /* CMEM - MPU */
+                             <0x401c0000 0x5fff>, /* SMEM - MPU */
+                             <0x401e0000 0x1fff>, /* PMEM - MPU */
+                             <0x4901f000 0x3ff>, /* L3 Interconnect */
+                             <0x49080000 0xffff>, /* DMEM - MPU */
+                             <0x490a0000 0x1fff>, /* CMEM - MPU */
+                             <0x490ce000 0x5fff>, /* SMEM - MPU */
+                             <0x490e0000 0x1fff>; /* PMEM - MPU */
+                       reg-names = "mpu", "dmem", "cmem", "smem", "pmem",
+                               "dma","dmem_dma", "cmem_dma", "smem_dma", "pmem_dma";
+                       interrupts = <0 99 0x4>;
+                       ti,hwmods = "aess";
+               };
+
                mcpdm: mcpdm@40132000 {
                        compatible = "ti,omap4-mcpdm";
                        reg = <0x40132000 0x7f>, /* MPU private access */
                        ti,hwmods = "dmic";
                };
 
+               mcasp: mcasp@40128000 {
+                       compatible = "ti,omap4-mcasp";
+                       reg = <0x40128000 0x7f>, /* MPU private access */
+                             <0x49028000 0x7f>; /* L3 Interconnect */
+                       reg-names = "mpu", "dma";
+                       interrupts = <0 109 0x4>;
+                       ti,hwmods = "mcasp";
+               };
+
                mcbsp1: mcbsp@40122000 {
                        compatible = "ti,omap4-mcbsp";
                        reg = <0x40122000 0xff>, /* MPU private access */
                        hw-caps-ll-interface;
                        hw-caps-temp-alert;
                };
+
+               omap_control_usb: omap-control-usb@4a002300 {
+                       compatible = "ti,omap-control-usb";
+                       reg = <0x4a002300 0x4>,
+                             <0x4a002370 0x4>;
+                       reg-names = "control_dev_conf", "phy_power_usb";
+                       ti,type = <2>;
+               };
+
+               omap_dwc3@4a020000 {
+                       compatible = "ti,dwc3";
+                       ti,hwmods = "usb_otg_ss";
+                       reg = <0x4a020000 0x1ff>;
+                       interrupts = <0 93 4>;
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       utmi-mode = <2>;
+                       ranges;
+                       dwc3@4a030000 {
+                               compatible = "synopsys,dwc3";
+                               reg = <0x4a030000 0xcfff>;
+                               interrupts = <0 92 4>;
+                               usb-phy = <&usb2_phy>, <&usb3_phy>;
+                               tx-fifo-resize;
+                       };
+               };
+
+               ocp2scp {
+                       compatible = "ti,omap-ocp2scp";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+                       ti,hwmods = "ocp2scp1";
+                       usb2_phy: usb2phy@4a084000 {
+                               compatible = "ti,omap-usb2";
+                               reg = <0x4a084000 0x7c>;
+                               ctrl-module = <&omap_control_usb>;
+                       };
+
+                       usb3_phy: usb3phy@4a084400 {
+                               compatible = "ti,omap-usb3";
+                               reg = <0x4a084400 0x80>,
+                                     <0x4a084800 0x64>,
+                                     <0x4a084c00 0x40>;
+                               reg-names = "phy_rx", "phy_tx", "pll_ctrl";
+                               ctrl-module = <&omap_control_usb>;
+                       };
+               };
+
+               usbhstll: usbhstll@4a062000 {
+                       compatible = "ti,usbhs-tll";
+                       reg = <0x4a062000 0x1000>;
+                       interrupts = <0 78 0x4>;
+                       ti,hwmods = "usb_tll_hs";
+               };
+
+               usbhshost: usbhshost@4a064000 {
+                       compatible = "ti,usbhs-host";
+                       reg = <0x4a064000 0x800>;
+                       ti,hwmods = "usb_host_hs";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+
+                       usbhsohci: ohci@4a064800 {
+                               compatible = "ti,ohci-omap3", "usb-ohci";
+                               reg = <0x4a064800 0x400>;
+                               interrupt-parent = <&gic>;
+                               interrupts = <0 76 0x4>;
+                       };
+
+                       usbhsehci: ehci@4a064c00 {
+                               compatible = "ti,ehci-omap", "usb-ehci";
+                               reg = <0x4a064c00 0x400>;
+                               interrupt-parent = <&gic>;
+                               interrupts = <0 77 0x4>;
+                       };
+               };
+
+               wdt2: wdt@4ae14000 {
+                       compatible = "ti,omap5-wdt", "ti,omap4-wdt";
+                       reg = <0x4ae14000 0x80>;
+                       interrupts = <0 80 0x4>;
+                       ti,hwmods = "wd_timer2";
+               };
+
+               dss {
+                       compatible = "ti,omap4-dss";
+                       ti,hwmods = "dss_core";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       dispc {
+                               compatible = "ti,omap4-dispc";
+                               ti,hwmods = "dss_dispc";
+                       };
+
+                       dpi: dpi {
+                               compatible = "ti,omap4-dpi";
+                               video-source = <2>;
+                       };
+
+                       dsi1: dsi@1 {
+                               compatible = "ti,omap4-dsi";
+                               ti,hwmods = "dss_dsi1_a";
+                               reg = <0>;
+                               vdds_dsi-supply = <&ldo7_reg>;
+                               video-source = <0>;
+                       };
+
+                       dsi2: dsi@2 {
+                               compatible = "ti,omap4-dsi";
+                               ti,hwmods = "dss_dsi1_c";
+                               reg = <1>;
+                               vdds_dsi-supply = <&ldo7_reg>;
+                               video-source = <2>;
+                       };
+
+                       hdmi: hdmi {
+                               compatible = "ti,omap4-hdmi", "simple-bus";
+                               ti,hwmods = "dss_hdmi";
+                               vdda_hdmi_dac-supply = <&ldo7_reg>;
+                               video-source = <1>;
+                       };
+               };
        };
 };
diff --git a/arch/arm/boot/dts/palmas.dtsi b/arch/arm/boot/dts/palmas.dtsi
new file mode 100644 (file)
index 0000000..ce8a86f
--- /dev/null
@@ -0,0 +1,324 @@
+&palmas {
+       compatible = "ti,palmas";
+       interrupt-controller;
+       #interrupt-cells = <2>;
+
+       palmas_pmic {
+               compatible = "ti,palmas-pmic";
+               ti,ldo6_vibrator = <0>;
+
+               regulators {
+                       smps123_reg: smps123 {
+                               regulator-name = "smps123";
+                               regulator-min-microvolt = < 600000>;
+                               regulator-max-microvolt = <1500000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       smps45_reg: smps45 {
+                               regulator-name = "smps45";
+                               regulator-min-microvolt = < 600000>;
+                               regulator-max-microvolt = <1310000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       smps6_reg: smps6 {
+                               regulator-name = "smps6";
+                               regulator-min-microvolt = <1200000>;
+                               regulator-max-microvolt = <1200000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       smps7_reg: smps7 {
+                               regulator-name = "smps7";
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       smps8_reg: smps8 {
+                               regulator-name = "smps8";
+                               regulator-min-microvolt = < 600000>;
+                               regulator-max-microvolt = <1310000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       smps9_reg: smps9 {
+                               regulator-name = "smps9";
+                               regulator-min-microvolt = <2100000>;
+                               regulator-max-microvolt = <2100000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0x80>;
+                       };
+
+                       smps10_reg: smps10 {
+                               regulator-name = "smps10";
+                               regulator-min-microvolt = <5000000>;
+                               regulator-max-microvolt = <5000000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       ldo1_reg: ldo1 {
+                               regulator-name = "ldo1";
+                               regulator-min-microvolt = <2800000>;
+                               regulator-max-microvolt = <2800000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       ldo2_reg: ldo2 {
+                               regulator-name = "ldo2";
+                               regulator-min-microvolt = <2900000>;
+                               regulator-max-microvolt = <2900000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       ldo3_reg: ldo3 {
+                               regulator-name = "ldo3";
+                               regulator-min-microvolt = <3000000>;
+                               regulator-max-microvolt = <3000000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       ldo4_reg: ldo4 {
+                               regulator-name = "ldo4";
+                               regulator-min-microvolt = <2200000>;
+                               regulator-max-microvolt = <2200000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       ldo5_reg: ldo5 {
+                               regulator-name = "ldo5";
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       ldo6_reg: ldo6 {
+                               regulator-name = "ldo6";
+                               regulator-min-microvolt = <1500000>;
+                               regulator-max-microvolt = <1500000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       ldo7_reg: ldo7 {
+                               regulator-name = "ldo7";
+                               regulator-min-microvolt = <1500000>;
+                               regulator-max-microvolt = <1500000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       ldo8_reg: ldo8 {
+                               regulator-name = "ldo8";
+                               regulator-min-microvolt = <1500000>;
+                               regulator-max-microvolt = <1500000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       ldo9_reg: ldo9 {
+                               regulator-name = "ldo9";
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       ldoln_reg: ldoln {
+                               regulator-name = "ldoln";
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+
+                       ldousb_reg: ldousb {
+                               regulator-name = "ldousb";
+                               regulator-min-microvolt = <3250000>;
+                               regulator-max-microvolt = <3250000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,warm_sleep = <0>;
+                               ti,roof_floor = <0>;
+                               ti,mode_sleep = <0>;
+                               ti,warm_reset = <0>;
+                               ti,tstep = <0>;
+                               ti,vsel = <0>;
+                       };
+               };
+       };
+
+       palmas_gpio {
+               compatible = "ti,palmas-gpio";
+       };
+
+       palmas_wdt {
+               compatible = "ti,palmas-wdt";
+       };
+
+       palmas_rtc {
+               compatible = "ti,palmas-rtc";
+       };
+
+       palmas_pwrbutton {
+               compatible = "ti,palmas-pwrbutton";
+       };
+
+       palmas_gpadc {
+               compatible = "ti,palmas-gpadc";
+               ti,ch3_current = <0>;
+               ti,ch0_current = <0>;
+               ti,bat_removal = <0>;
+               ti,start_polarity = <0>;
+       };
+
+       palmas_resource {
+               compatible = "ti,palmas-resource";
+               ti,regen1_mode_sleep = <0>;
+               ti,regen2_mode_sleep = <0>;
+               ti,sysen1_mode_sleep = <0>;
+               ti,sysen2_mode_sleep = <0>;
+               ti,nsleep_res = <0>;
+               ti,nsleep_smps = <0>;
+               ti,nsleep_ldo1 = <0>;
+               ti,nsleep_ldo2 = <0>;
+               ti,enable1_res = <0>;
+               ti,enable1_smps = <0>;
+               ti,enable1_ldo1 = <0>;
+               ti,enable1_ldo2 = <0>;
+               ti,enable2_res = <0>;
+               ti,enable2_smps = <0>;
+               ti,enable2_ldo1 = <0>;
+               ti,enable2_ldo2 = <0>;
+       };
+
+       palmas_clk {
+               compatible = "ti,palmas-clk";
+               clk32kg_mode_sleep = <0>;
+               clk32kgaudio_mode_sleep = <0>;
+       };
+
+       palmas_pwm {
+               compatible = "ti,palmas-pwm";
+       };
+
+       palmas_usb {
+               compatible = "ti,palmas-usb";
+               ti,wakeup;
+               vbus-supply = <&smps10_reg>;
+       };
+};
index 34da11aa679504616359ec759568d31160eb829a..b2d41b7502bd8082be6838f5fbfb9a056a41302f 100644 (file)
                        status = "disabled";
                };
 
+               dma@ea800000 {
+                       slave_info {
+                               uart1_tx {
+                                       bus_id = "uart1_tx";
+                                       cfg_hi = <0x6000>;      /* 0xC << 11 */
+                                       cfg_lo = <0>;
+                                       src_master = <0>;
+                                       dst_master = <1>;
+                               };
+                               uart1_tx {
+                                       bus_id = "uart1_tx";
+                                       cfg_hi = <0x680>;       /* 0xD << 7 */
+                                       cfg_lo = <0>;
+                                       src_master = <1>;
+                                       dst_master = <0>;
+                               };
+                       };
+               };
+
                spi1: spi@5d400000 {
                        compatible = "arm,pl022", "arm,primecell";
                        reg = <0x5d400000 0x1000>;
index b4ca60f4eb42dabf24adfb7f47e1da0471381fc9..585f64157ea44dc26945955590d9c3d661c05134 100644 (file)
                        reg = <0xea800000 0x1000>;
                        interrupts = <0 19 0x4>;
                        status = "disabled";
+
+                       nr_channels = <8>;
+                       chan_allocation_order = <1>;
+                       chan_priority = <1>;
+                       block_size = <0xfff>;
+                       nr_masters = <2>;
+                       data_width = <3 3 0 0>;
+
+                       slave_info {
+                               ssp0_tx {
+                                       bus_id = "ssp0_tx";
+                                       cfg_hi = <0x2000>;      /* 0x4 << 11 */
+                                       cfg_lo = <0>;
+                                       src_master = <0>;
+                                       dst_master = <0>;
+                               };
+                               ssp0_rx {
+                                       bus_id = "ssp0_rx";
+                                       cfg_hi = <0x280>;       /* 0x5 << 7 */
+                                       cfg_lo = <0>;
+                                       src_master = <0>;
+                                       dst_master = <0>;
+                               };
+                               cf {
+                                       bus_id = "cf";
+                                       cfg_hi = <0>;
+                                       cfg_lo = <0>;
+                                       src_master = <0>;
+                                       dst_master = <0>;
+                               };
+                       };
                };
 
                dma@eb000000 {
                        reg = <0xeb000000 0x1000>;
                        interrupts = <0 59 0x4>;
                        status = "disabled";
+
+                       nr_channels = <8>;
+                       chan_allocation_order = <1>;
+                       chan_priority = <1>;
+                       block_size = <0xfff>;
+                       nr_masters = <2>;
+                       data_width = <3 3 0 0>;
                };
 
                fsmc: flash@b0000000 {
index ed0bc9546837d8f40f3bf873e20055717c5d1451..23c0cdd6172b98da54bc8080c25ab209e2decac9 100644 (file)
@@ -67,7 +67,7 @@
                #interrupt-cells = <1>;
        };
 
-       twl4030-usb {
+       usb2_phy: twl4030-usb {
                compatible = "ti,twl4030-usb";
                interrupts = <10>, <4>;
                usb1v5-supply = <&vusb1v5>;
                usb3v1-supply = <&vusb3v1>;
                usb_mode = <1>;
        };
+
+       twl_pwm: pwm {
+               compatible = "ti,twl4030-pwm";
+               #pwm-cells = <2>;
+       };
+
+       twl_pwmled: pwmled {
+               compatible = "ti,twl4030-pwmled";
+               #pwm-cells = <2>;
+       };
 };
index 9996cfc5ee809a8854817757aded21e59b861161..2e3bd3172b2366227a78f8404f191e4d0e6e8be6 100644 (file)
                compatible = "ti,twl6030-usb";
                interrupts = <4>, <10>;
        };
+
+       twl_pwm: pwm {
+               /* provides two PWMs (id 0, 1 for PWM1 and PWM2) */
+               compatible = "ti,twl6030-pwm";
+               #pwm-cells = <2>;
+       };
+
+       twl_pwmled: pwmled {
+               /* provides one PWM (id 0 for Charging indicator LED) */
+               compatible = "ti,twl6030-pwmled";
+               #pwm-cells = <2>;
+       };
 };
diff --git a/arch/arm/boot/dts/twl6040.dtsi b/arch/arm/boot/dts/twl6040.dtsi
new file mode 100644 (file)
index 0000000..c6d5be7
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * 8 channel high-quality low power audio codec
+ * http://www.ti.com/lit/ds/symlink/twl6040.pdf
+ */
+&twl6040 {
+       reg = <0x4b>;
+
+       gpio-controller;
+       #gpio-cells = <1>;
+};
index 45ceeb0e93e05faaa40195a30d82045f7b6aabe0..9e32d0d866599e7a6ed5be84f811f9590fbe2608 100644 (file)
@@ -40,3 +40,6 @@ config SHARP_PARAM
 
 config SHARP_SCOOP
        bool
+
+config TI_PRIV_EDMA
+       bool
index e8a4e58f1b827e26ec0874c2baa53ede8105f70e..d09a39b1b5d40583106eca52d1f0bdff60b8a6da 100644 (file)
@@ -13,3 +13,4 @@ obj-$(CONFIG_SHARP_PARAM)     += sharpsl_param.o
 obj-$(CONFIG_SHARP_SCOOP)      += scoop.o
 obj-$(CONFIG_PCI_HOST_ITE8152)  += it8152.o
 obj-$(CONFIG_ARM_TIMER_SP804)  += timer-sp.o
+obj-$(CONFIG_TI_PRIV_EDMA)     += edma.o
similarity index 86%
rename from arch/arm/mach-davinci/dma.c
rename to arch/arm/common/edma.c
index 45b7c71d9cc1966bb514862364b5180cdd307060..bd2416a5748e170606c272846501142705a5b064 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/slab.h>
+#include <linux/edma.h>
+#include <linux/err.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_dma.h>
+#include <linux/of_irq.h>
+#include <linux/pm_runtime.h>
 
-#include <mach/edma.h>
+#include <linux/platform_data/edma.h>
 
 /* Offsets matching "struct edmacc_param" */
 #define PARM_OPT               0x00
@@ -494,26 +501,6 @@ static irqreturn_t dma_ccerr_handler(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-/******************************************************************************
- *
- * Transfer controller error interrupt handlers
- *
- *****************************************************************************/
-
-#define tc_errs_handled        false   /* disabled as long as they're NOPs */
-
-static irqreturn_t dma_tc0err_handler(int irq, void *data)
-{
-       dev_dbg(data, "dma_tc0err_handler\n");
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t dma_tc1err_handler(int irq, void *data)
-{
-       dev_dbg(data, "dma_tc1err_handler\n");
-       return IRQ_HANDLED;
-}
-
 static int reserve_contiguous_slots(int ctlr, unsigned int id,
                                     unsigned int num_slots,
                                     unsigned int start_slot)
@@ -1389,31 +1376,278 @@ void edma_clear_event(unsigned channel)
 EXPORT_SYMBOL(edma_clear_event);
 
 /*-----------------------------------------------------------------------*/
+static int edma_of_read_u32_to_s8_array(const struct device_node *np,
+                                        const char *propname, s8 *out_values,
+                                        size_t sz)
+{
+       int ret;
+
+       ret = of_property_read_u8_array(np, propname, out_values, sz);
+       if (ret)
+               return ret;
 
-static int __init edma_probe(struct platform_device *pdev)
+       /* Terminate it */
+       *out_values++ = -1;
+       *out_values++ = -1;
+
+       return 0;
+}
+
+static int edma_of_read_u32_to_s16_array(const struct device_node *np,
+                                        const char *propname, s16 *out_values,
+                                        size_t sz)
+{
+       int ret;
+
+       ret = of_property_read_u16_array(np, propname, out_values, sz);
+       if (ret)
+               return ret;
+
+       /* Terminate it */
+       *out_values++ = -1;
+       *out_values++ = -1;
+
+       return 0;
+}
+
+static int edma_xbar_event_map(struct device *dev,
+                              struct device_node *node,
+                              struct edma_soc_info *pdata, int len)
+{
+       int ret = 0;
+       int i;
+       struct resource res;
+       void *xbar;
+       const s16 (*xbar_chans)[2];
+       u32 shift, offset, mux;
+
+       xbar_chans = devm_kzalloc(dev,
+                                 len/sizeof(s16) + 2*sizeof(s16),
+                                 GFP_KERNEL);
+       if (!xbar_chans)
+               return -ENOMEM;
+
+       ret = of_address_to_resource(node, 1, &res);
+       if (IS_ERR_VALUE(ret))
+               return -EIO;
+
+       xbar = devm_ioremap(dev, res.start, resource_size(&res));
+       if (!xbar)
+               return -ENOMEM;
+
+       ret = edma_of_read_u32_to_s16_array(node,
+                                           "ti,edma-xbar-event-map",
+                                           (s16 *)xbar_chans,
+                                           len/sizeof(u32));
+       if (IS_ERR_VALUE(ret))
+               return -EIO;
+
+       for (i = 0; xbar_chans[i][0] != -1; i++) {
+               shift = (xbar_chans[i][1] % 4) * 8;
+               offset = xbar_chans[i][1] >> 2;
+               offset <<= 2;
+               mux = readl((void *)((u32)xbar + offset));
+               mux &= ~(0xff << shift);
+               mux |= xbar_chans[i][0] << shift;
+               writel(mux, (void *)((u32)xbar + offset));
+       }
+
+       pdata->xbar_chans = xbar_chans;
+
+       return 0;
+}
+
+static int edma_of_parse_dt(struct device *dev,
+                           struct device_node *node,
+                           struct edma_soc_info *pdata)
+{
+       int ret = 0;
+       u32 value;
+       struct property *prop;
+       size_t sz;
+       struct edma_rsv_info *rsv_info;
+       const s16 (*rsv_chans)[2], (*rsv_slots)[2];
+       const s8 (*queue_tc_map)[2], (*queue_priority_map)[2];
+
+       memset(pdata, 0, sizeof(struct edma_soc_info));
+
+       ret = of_property_read_u32(node, "dma-channels", &value);
+       if (ret < 0)
+               return ret;
+       pdata->n_channel = value;
+
+       ret = of_property_read_u32(node, "ti,edma-regions", &value);
+       if (ret < 0)
+               return ret;
+       pdata->n_region = value;
+
+       ret = of_property_read_u32(node, "ti,edma-slots", &value);
+       if (ret < 0)
+               return ret;
+       pdata->n_slot = value;
+
+       pdata->n_cc = 1;
+       pdata->n_tc = 3;
+
+       rsv_info =
+               devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL);
+       if (!rsv_info)
+               return -ENOMEM;
+       pdata->rsv = rsv_info;
+
+       /* Build the reserved channel/slots arrays */
+       prop = of_find_property(node, "ti,edma-reserved-channels", &sz);
+       if (prop) {
+               rsv_chans = devm_kzalloc(dev,
+                                        sz/sizeof(s16) + 2*sizeof(s16),
+                                        GFP_KERNEL);
+               if (!rsv_chans)
+                       return -ENOMEM;
+               pdata->rsv->rsv_chans = rsv_chans;
+
+               ret = edma_of_read_u32_to_s16_array(node,
+                                                   "ti,edma-reserved-channels",
+                                                   (s16 *)rsv_chans,
+                                                   sz/sizeof(u32));
+               if (ret < 0)
+                       return ret;
+       }
+
+       prop = of_find_property(node, "ti,edma-reserved-slots", &sz);
+       if (prop) {
+               rsv_slots = devm_kzalloc(dev,
+                                        sz/sizeof(s16) + 2*sizeof(s16),
+                                        GFP_KERNEL);
+               if (!rsv_slots)
+                       return -ENOMEM;
+               pdata->rsv->rsv_slots = rsv_slots;
+
+               ret = edma_of_read_u32_to_s16_array(node,
+                                                   "ti,edma-reserved-slots",
+                                                   (s16 *)rsv_slots,
+                                                   sz/sizeof(u32));
+               if (ret < 0)
+                       return ret;
+       }
+
+       prop = of_find_property(node, "ti,edma-queue-tc-map", &sz);
+       if (!prop)
+               return -EINVAL;
+
+       queue_tc_map = devm_kzalloc(dev,
+                                   sz/sizeof(s8) + 2*sizeof(s8),
+                                   GFP_KERNEL);
+       if (!queue_tc_map)
+               return -ENOMEM;
+       pdata->queue_tc_mapping = queue_tc_map;
+
+       ret = edma_of_read_u32_to_s8_array(node,
+                                          "ti,edma-queue-tc-map",
+                                          (s8 *)queue_tc_map,
+                                          sz/sizeof(u32));
+       if (ret < 0)
+               return ret;
+
+       prop = of_find_property(node, "ti,edma-queue-priority-map", &sz);
+       if (!prop)
+               return -EINVAL;
+
+       queue_priority_map = devm_kzalloc(dev,
+                                         sz/sizeof(s8) + 2*sizeof(s8),
+                                         GFP_KERNEL);
+       if (!queue_priority_map)
+               return -ENOMEM;
+       pdata->queue_priority_mapping = queue_priority_map;
+
+       ret = edma_of_read_u32_to_s8_array(node,
+                                          "ti,edma-queue-tc-map",
+                                          (s8 *)queue_priority_map,
+                                          sz/sizeof(u32));
+       if (ret < 0)
+               return ret;
+
+       ret = of_property_read_u32(node, "ti,edma-default-queue", &value);
+       if (ret < 0)
+               return ret;
+       pdata->default_queue = value;
+
+       prop = of_find_property(node, "ti,edma-xbar-event-map", &sz);
+       if (prop)
+               ret = edma_xbar_event_map(dev, node, pdata, sz);
+
+       return ret;
+}
+
+static struct of_dma_filter_info edma_filter_info = {
+       .filter_fn = edma_filter_fn,
+};
+
+static int edma_probe(struct platform_device *pdev)
 {
        struct edma_soc_info    **info = pdev->dev.platform_data;
+       struct edma_soc_info    *ninfo[EDMA_MAX_CC] = {NULL, NULL};
+       struct edma_soc_info    tmpinfo;
        const s8                (*queue_priority_mapping)[2];
        const s8                (*queue_tc_mapping)[2];
        int                     i, j, off, ln, found = 0;
        int                     status = -1;
        const s16               (*rsv_chans)[2];
        const s16               (*rsv_slots)[2];
+       const s16               (*xbar_chans)[2];
        int                     irq[EDMA_MAX_CC] = {0, 0};
        int                     err_irq[EDMA_MAX_CC] = {0, 0};
-       struct resource         *r[EDMA_MAX_CC] = {NULL};
+       struct resource         *r[EDMA_MAX_CC] = {NULL, NULL};
+       struct resource         res[EDMA_MAX_CC];
        resource_size_t         len[EDMA_MAX_CC];
        char                    res_name[10];
        char                    irq_name[10];
+       struct device_node      *node = pdev->dev.of_node;
+       struct device           *dev = &pdev->dev;
+       int                     ret;
+
+       if (node) {
+               /* Check if this is a second instance registered */
+               if (arch_num_cc) {
+                       dev_err(dev, "only one EDMA instance is supported via DT\n");
+                       return -ENODEV;
+               }
+               info = ninfo;
+               edma_of_parse_dt(dev, node, &tmpinfo);
+               info[0] = &tmpinfo;
+
+               dma_cap_set(DMA_SLAVE, edma_filter_info.dma_cap);
+               of_dma_controller_register(dev->of_node,
+                                          of_dma_simple_xlate,
+                                          &edma_filter_info);
+       }
 
        if (!info)
                return -ENODEV;
 
+       pm_runtime_enable(dev);
+       ret = pm_runtime_get_sync(dev);
+       if (IS_ERR_VALUE(ret)) {
+               dev_err(dev, "pm_runtime_get_sync() failed\n");
+               return ret;
+       }
+
        for (j = 0; j < EDMA_MAX_CC; j++) {
-               sprintf(res_name, "edma_cc%d", j);
-               r[j] = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+               if (!info[j]) {
+                       if (!found)
+                               return -ENODEV;
+                       break;
+               }
+               if (node) {
+                       ret = of_address_to_resource(node, j, &res[j]);
+                       if (!IS_ERR_VALUE(ret))
+                               r[j] = &res[j];
+               } else {
+                       sprintf(res_name, "edma_cc%d", j);
+                       r[j] = platform_get_resource_byname(pdev,
+                                               IORESOURCE_MEM,
                                                res_name);
-               if (!r[j] || !info[j]) {
+               }
+               if (!r[j]) {
                        if (found)
                                break;
                        else
@@ -1488,8 +1722,22 @@ static int __init edma_probe(struct platform_device *pdev)
                        }
                }
 
-               sprintf(irq_name, "edma%d", j);
-               irq[j] = platform_get_irq_byname(pdev, irq_name);
+               /* Clear the xbar mapped channels in unused list */
+               xbar_chans = info[j]->xbar_chans;
+               if (xbar_chans) {
+                       for (i = 0; xbar_chans[i][1] != -1; i++) {
+                               off = xbar_chans[i][1];
+                               clear_bits(off, 1,
+                                       edma_cc[j]->edma_unused);
+                       }
+               }
+
+               if (node)
+                       irq[j] = irq_of_parse_and_map(node, 0);
+               else {
+                       sprintf(irq_name, "edma%d", j);
+                       irq[j] = platform_get_irq_byname(pdev, irq_name);
+               }
                edma_cc[j]->irq_res_start = irq[j];
                status = request_irq(irq[j], dma_irq_handler, 0, "edma",
                                        &pdev->dev);
@@ -1499,8 +1747,12 @@ static int __init edma_probe(struct platform_device *pdev)
                        goto fail;
                }
 
-               sprintf(irq_name, "edma%d_err", j);
-               err_irq[j] = platform_get_irq_byname(pdev, irq_name);
+               if (node)
+                       err_irq[j] = irq_of_parse_and_map(node, 2);
+               else {
+                       sprintf(irq_name, "edma%d_err", j);
+                       err_irq[j] = platform_get_irq_byname(pdev, irq_name);
+               }
                edma_cc[j]->irq_res_end = err_irq[j];
                status = request_irq(err_irq[j], dma_ccerr_handler, 0,
                                        "edma_error", &pdev->dev);
@@ -1541,23 +1793,6 @@ static int __init edma_probe(struct platform_device *pdev)
                arch_num_cc++;
        }
 
-       if (tc_errs_handled) {
-               status = request_irq(IRQ_TCERRINT0, dma_tc0err_handler, 0,
-                                       "edma_tc0", &pdev->dev);
-               if (status < 0) {
-                       dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n",
-                               IRQ_TCERRINT0, status);
-                       return status;
-               }
-               status = request_irq(IRQ_TCERRINT, dma_tc1err_handler, 0,
-                                       "edma_tc1", &pdev->dev);
-               if (status < 0) {
-                       dev_dbg(&pdev->dev, "request_irq %d --> %d\n",
-                               IRQ_TCERRINT, status);
-                       return status;
-               }
-       }
-
        return 0;
 
 fail:
@@ -1578,9 +1813,17 @@ fail1:
        return status;
 }
 
+static const struct of_device_id edma_of_ids[] = {
+       { .compatible = "ti,edma3", },
+       {}
+};
 
 static struct platform_driver edma_driver = {
-       .driver.name    = "edma",
+       .driver = {
+               .name   = "edma",
+               .of_match_table = edma_of_ids,
+       },
+       .probe = edma_probe,
 };
 
 static int __init edma_init(void)
index 42eab9a2a0fd53ba4cb26eb36c1a7a7c29909587..2280d9df753e2dc402c34beb47b0891c31dd318c 100644 (file)
@@ -26,7 +26,8 @@ CONFIG_ARCH_OMAP=y
 CONFIG_ARCH_OMAP1=y
 CONFIG_OMAP_RESET_CLOCKS=y
 # CONFIG_OMAP_MUX is not set
-CONFIG_OMAP_MBOX_FWK=y
+CONFIG_MAILBOX=y
+CONFIG_OMAP1_MBOX=y
 CONFIG_OMAP_32K_TIMER=y
 CONFIG_OMAP_DM_TIMER=y
 CONFIG_ARCH_OMAP730=y
index 82ce8d738fa1c8a6f4ba730828867de22aa392c3..348d10a98a810f067d53b5028e9723abf95d08cd 100644 (file)
@@ -1,14 +1,14 @@
 CONFIG_EXPERIMENTAL=y
 CONFIG_SYSVIPC=y
 CONFIG_POSIX_MQUEUE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
 CONFIG_BSD_PROCESS_ACCT=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=16
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_EXPERT=y
-# CONFIG_SYSCTL_SYSCALL is not set
-CONFIG_KALLSYMS_EXTRA_PASS=y
 CONFIG_SLAB=y
 CONFIG_PROFILING=y
 CONFIG_OPROFILE=y
@@ -20,16 +20,15 @@ CONFIG_MODULE_FORCE_UNLOAD=y
 CONFIG_MODVERSIONS=y
 CONFIG_MODULE_SRCVERSION_ALL=y
 # CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
 CONFIG_ARCH_OMAP=y
 CONFIG_OMAP_RESET_CLOCKS=y
 CONFIG_OMAP_MUX_DEBUG=y
+CONFIG_SOC_OMAP5=y
 CONFIG_ARM_THUMBEE=y
 CONFIG_ARM_ERRATA_411920=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
 CONFIG_SMP=y
 CONFIG_NR_CPUS=2
-CONFIG_LEDS=y
 CONFIG_ZBOOT_ROM_TEXT=0x0
 CONFIG_ZBOOT_ROM_BSS=0x0
 CONFIG_CMDLINE="root=/dev/mmcblk0p2 rootwait console=ttyO2,115200"
@@ -64,9 +63,9 @@ CONFIG_MAC80211=m
 CONFIG_MAC80211_RC_PID=y
 CONFIG_MAC80211_RC_DEFAULT_PID=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-CONFIG_CONNECTOR=y
 CONFIG_DEVTMPFS=y
 CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_CONNECTOR=y
 CONFIG_MTD=y
 CONFIG_MTD_CMDLINE_PARTS=y
 CONFIG_MTD_CHAR=y
@@ -74,6 +73,7 @@ CONFIG_MTD_BLOCK=y
 CONFIG_MTD_OOPS=y
 CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_M25P80=y
 CONFIG_MTD_NAND=y
 CONFIG_MTD_NAND_OMAP2=y
 CONFIG_MTD_ONENAND=y
@@ -85,26 +85,28 @@ CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_RAM_SIZE=16384
 CONFIG_SCSI=y
 CONFIG_BLK_DEV_SD=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=m
 CONFIG_SCSI_MULTI_LUN=y
 CONFIG_SCSI_SCAN_ASYNC=y
 CONFIG_MD=y
 CONFIG_NETDEVICES=y
-CONFIG_SMSC_PHY=y
-CONFIG_NET_ETHERNET=y
-CONFIG_SMC91X=y
-CONFIG_SMSC911X=y
 CONFIG_KS8851=y
 CONFIG_KS8851_MLL=y
-CONFIG_LIBERTAS=m
-CONFIG_LIBERTAS_USB=m
-CONFIG_LIBERTAS_SDIO=m
-CONFIG_LIBERTAS_DEBUG=y
+CONFIG_SMC91X=y
+CONFIG_SMSC911X=y
+CONFIG_TI_CPSW=y
+CONFIG_SMSC_PHY=y
 CONFIG_USB_USBNET=y
 CONFIG_USB_NET_SMSC95XX=y
 CONFIG_USB_ALI_M5632=y
 CONFIG_USB_AN2720=y
 CONFIG_USB_EPSON2888=y
 CONFIG_USB_KC2190=y
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_SDIO=m
+CONFIG_LIBERTAS_DEBUG=y
 CONFIG_INPUT_JOYDEV=y
 CONFIG_INPUT_EVDEV=y
 CONFIG_KEYBOARD_GPIO=y
@@ -125,6 +127,7 @@ CONFIG_HW_RANDOM=y
 CONFIG_I2C_CHARDEV=y
 CONFIG_SPI=y
 CONFIG_SPI_OMAP24XX=y
+CONFIG_DEBUG_PINCTRL=y
 CONFIG_PINCTRL_SINGLE=y
 CONFIG_DEBUG_GPIO=y
 CONFIG_GPIO_SYSFS=y
@@ -134,16 +137,23 @@ CONFIG_POWER_SUPPLY=y
 CONFIG_WATCHDOG=y
 CONFIG_OMAP_WATCHDOG=y
 CONFIG_TWL4030_WATCHDOG=y
+CONFIG_PALMAS_WATCHDOG=y
 CONFIG_MFD_TPS65217=y
 CONFIG_REGULATOR_TWL4030=y
+CONFIG_MFD_PALMAS=y
+CONFIG_MFD_PALMAS_GPADC=y
+CONFIG_MFD_PALMAS_PWM=y
+CONFIG_MFD_PALMAS_RESOURCE=y
+CONFIG_REGULATOR_PALMAS=y
+CONFIG_MFD_TPS65910=y
 CONFIG_REGULATOR_TPS65023=y
 CONFIG_REGULATOR_TPS6507X=y
 CONFIG_REGULATOR_TPS65217=y
+CONFIG_REGULATOR_TPS65910=y
 CONFIG_FB=y
 CONFIG_FIRMWARE_EDID=y
 CONFIG_FB_MODE_HELPERS=y
 CONFIG_FB_TILEBLITTING=y
-CONFIG_FB_OMAP_LCD_VGA=y
 CONFIG_OMAP2_DSS=m
 CONFIG_OMAP2_DSS_RFBI=y
 CONFIG_OMAP2_DSS_SDI=y
@@ -158,7 +168,6 @@ CONFIG_PANEL_ACX565AKM=m
 CONFIG_BACKLIGHT_LCD_SUPPORT=y
 CONFIG_LCD_CLASS_DEVICE=y
 CONFIG_LCD_PLATFORM=y
-CONFIG_DISPLAY_SUPPORT=y
 CONFIG_FRAMEBUFFER_CONSOLE=y
 CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
 CONFIG_FONTS=y
@@ -179,18 +188,37 @@ CONFIG_SND_OMAP_SOC_OMAP3_PANDORA=m
 CONFIG_USB=y
 CONFIG_USB_DEBUG=y
 CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
-CONFIG_USB_DEVICEFS=y
 CONFIG_USB_SUSPEND=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_DWC3=m
+CONFIG_USB_DWC3_DEBUG=y
+CONFIG_USB_DWC3_VERBOSE=y
+CONFIG_USB_MUSB_HDRC=m
+CONFIG_USB_MUSB_OMAP2PLUS=m
+CONFIG_USB_MUSB_AM35X=m
+CONFIG_USB_MUSB_DSPS=m
+CONFIG_MUSB_PIO_ONLY=y
 CONFIG_USB_MON=y
 CONFIG_USB_WDM=y
 CONFIG_USB_STORAGE=y
-CONFIG_USB_LIBUSUAL=y
 CONFIG_USB_TEST=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DEBUG=y
 CONFIG_USB_GADGET_DEBUG_FILES=y
 CONFIG_USB_GADGET_DEBUG_FS=y
 CONFIG_USB_ZERO=m
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_USB_G_MULTI=m
+CONFIG_USB_G_MULTI_CDC=y
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_EEM=y
+CONFIG_OMAP_USB2=m
+CONFIG_OMAP_USB3=m
+CONFIG_OMAP_CONTROL_USB=y
+CONFIG_TWL4030_USB=m
+CONFIG_TWL6030_USB=m
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_PALMAS_USB=m
 CONFIG_MMC=y
 CONFIG_MMC_UNSAFE_RESUME=y
 CONFIG_SDIO_UART=y
@@ -199,7 +227,9 @@ CONFIG_MMC_OMAP_HS=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_TWL92330=y
 CONFIG_RTC_DRV_TWL4030=y
+CONFIG_RTC_DRV_PALMAS=y
 CONFIG_DMADEVICES=y
+CONFIG_TI_EDMA=y
 CONFIG_DMA_OMAP=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
@@ -219,23 +249,18 @@ CONFIG_JFFS2_RUBIN=y
 CONFIG_UBIFS_FS=y
 CONFIG_CRAMFS=y
 CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
 CONFIG_NFS_V3_ACL=y
 CONFIG_NFS_V4=y
 CONFIG_ROOT_NFS=y
-CONFIG_PARTITION_ADVANCED=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 CONFIG_PRINTK_TIME=y
 CONFIG_MAGIC_SYSRQ=y
-CONFIG_DEBUG_KERNEL=y
 CONFIG_SCHEDSTATS=y
 CONFIG_TIMER_STATS=y
 CONFIG_PROVE_LOCKING=y
-CONFIG_DEBUG_SPINLOCK_SLEEP=y
 # CONFIG_DEBUG_BUGVERBOSE is not set
 CONFIG_DEBUG_INFO=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
 CONFIG_SECURITY=y
 CONFIG_CRYPTO_MICHAEL_MIC=y
 # CONFIG_CRYPTO_ANSI_CPRNG is not set
@@ -244,7 +269,3 @@ CONFIG_CRC_T10DIF=y
 CONFIG_CRC_ITU_T=y
 CONFIG_CRC7=y
 CONFIG_LIBCRC32C=y
-CONFIG_SOC_OMAP5=y
-CONFIG_TI_DAVINCI_MDIO=y
-CONFIG_TI_DAVINCI_CPDMA=y
-CONFIG_TI_CPSW=y
index c09474942f3ee80c83fe9aa2f3289818b6928d6f..26e9ce4a136773240f72b48161a5206ff4605db3 100644 (file)
@@ -60,6 +60,15 @@ extern void __pgd_error(const char *file, int line, pgd_t);
  */
 #define FIRST_USER_ADDRESS     PAGE_SIZE
 
+/*
+ * Use TASK_SIZE as the ceiling argument for free_pgtables() and
+ * free_pgd_range() to avoid freeing the modules pmd when LPAE is enabled (pmd
+ * page shared between user and kernel).
+ */
+#ifdef CONFIG_ARM_LPAE
+#define USER_PGTABLES_CEILING  TASK_SIZE
+#endif
+
 /*
  * The pgprot_* and protection_map entries will be fixed up in runtime
  * to include the cachable and bufferable bits based on memory policy,
index 58af91c2a2f79de57df7bb09a25b773f3618c279..650a7cfbd52cfef555381d7b866a564de54071f8 100644 (file)
@@ -296,6 +296,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
 {
        struct mm_struct *mm = &init_mm;
        unsigned int cpu;
+       static bool booted;
 
        /*
         * The identity mapping is uncached (strongly ordered), so
@@ -328,7 +329,9 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
 
        notify_cpu_starting(cpu);
 
-       calibrate_delay();
+       if (!booted)
+               calibrate_delay();
+       booted = true;
 
        smp_store_cpu_info(cpu);
 
index 00f79e59985bccaf54bd3b1c8f2e292f49014a47..6315162ceeed6c70d7c273a4c14e33966f57c5e2 100644 (file)
@@ -31,7 +31,7 @@ int notrace unwind_frame(struct stackframe *frame)
        high = ALIGN(low, THREAD_SIZE);
 
        /* check current frame pointer is within bounds */
-       if (fp < (low + 12) || fp + 4 >= high)
+       if (fp < (low + 12) || fp >= high - 4)
                return -EINVAL;
 
        /* restore the registers from the stack frame */
index fb5c1aa98a63ed1d4c0ec1fb9ccff1be6172948e..493a36bc9c06d2fbfe73d7f6a8d04aa220fc6c7e 100644 (file)
@@ -5,7 +5,7 @@
 
 # Common objects
 obj-y                  := time.o clock.o serial.o psc.o \
-                          dma.o usb.o common.o sram.o aemif.o
+                          usb.o common.o sram.o aemif.o
 
 obj-$(CONFIG_DAVINCI_MUX)              += mux.o
 
index be3099733b1fb2ab6eb857998cb4a2838a49ff70..86f55bacba60babc1dec88795572a4d7dcf65d7b 100644 (file)
 #include <linux/input.h>
 #include <linux/input/matrix_keypad.h>
 #include <linux/spi/spi.h>
+#include <linux/platform_data/edma.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach-types.h>
 
 #include <mach/irqs.h>
-#include <mach/edma.h>
 #include <mach/mux.h>
 #include <mach/cp_intc.h>
 #include <mach/tnetv107x.h>
index 12d544befcfa36554d70931fcf0048e01ab212bd..d26a6bcac2e14ff0253476d357d4f7e19e523949 100644 (file)
@@ -23,9 +23,9 @@
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/platform_data/davinci_asp.h>
+#include <linux/platform_data/edma.h>
 #include <linux/platform_data/keyscan-davinci.h>
 #include <mach/hardware.h>
-#include <mach/edma.h>
 
 #include <media/davinci/vpfe_capture.h>
 #include <media/davinci/vpif_types.h>
index 773ab07a71a0536351ab2ba00338f769f159237e..ba37760d3583d6eeab0a4a152394722cee6f0f4d 100644 (file)
 #include <linux/dma-mapping.h>
 #include <linux/clk.h>
 #include <linux/slab.h>
+#include <linux/platform_data/edma.h>
 
 #include <mach/common.h>
 #include <mach/irqs.h>
-#include <mach/edma.h>
 #include <mach/tnetv107x.h>
 
 #include "clock.h"
index 4c48a36ee567d6a98b2eb5d0a56ef9b51f2121b0..ca0c7b37c308af1e68cca01699e746ab6ab68e77 100644 (file)
 #include <mach/irqs.h>
 #include <mach/cputype.h>
 #include <mach/mux.h>
-#include <mach/edma.h>
 #include <linux/platform_data/mmc-davinci.h>
 #include <mach/time.h>
+#include <linux/platform_data/edma.h>
+
 
 #include "davinci.h"
 #include "clock.h"
@@ -34,6 +35,9 @@
 #define DM365_MMCSD0_BASE           0x01D11000
 #define DM365_MMCSD1_BASE           0x01D00000
 
+#define DAVINCI_DMA_MMCRXEVT   26
+#define DAVINCI_DMA_MMCTXEVT   27
+
 void __iomem  *davinci_sysmod_base;
 
 void davinci_map_sysmod(void)
index b49c3b77d55e5ac3e9884762a2fcdab28c03453b..53998d8c2be5664a7d5614a8f45a71a19ee94055 100644 (file)
@@ -19,7 +19,6 @@
 #include <asm/mach/map.h>
 
 #include <mach/cputype.h>
-#include <mach/edma.h>
 #include <mach/psc.h>
 #include <mach/mux.h>
 #include <mach/irqs.h>
@@ -28,6 +27,7 @@
 #include <mach/common.h>
 #include <linux/platform_data/spi-davinci.h>
 #include <mach/gpio-davinci.h>
+#include <linux/platform_data/edma.h>
 
 #include "davinci.h"
 #include "clock.h"
index 6c3980540be0525a9e3d7a5c699ed2221d378a35..9b41d330020461ed686807e0304f2b90de9624f0 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/spi/spi.h>
+#include <linux/platform_data/edma.h>
 
 #include <asm/mach/map.h>
 
 #include <mach/cputype.h>
-#include <mach/edma.h>
 #include <mach/psc.h>
 #include <mach/mux.h>
 #include <mach/irqs.h>
index 11c79a3362ef9e3ec1aa7abc67280ddbada9591f..a08910e86397c60fa9f088a815d96c311c4dd493 100644 (file)
 #include <linux/clk.h>
 #include <linux/serial_8250.h>
 #include <linux/platform_device.h>
+#include <linux/platform_data/edma.h>
 
 #include <asm/mach/map.h>
 
 #include <mach/cputype.h>
-#include <mach/edma.h>
 #include <mach/irqs.h>
 #include <mach/psc.h>
 #include <mach/mux.h>
index ac7b431c4c8eb93bd21feee28cc9fe8dd251d1ea..6d52a321a8cf133b1a0eccbd82cf84bf55cc6de8 100644 (file)
 #include <linux/clk.h>
 #include <linux/serial_8250.h>
 #include <linux/platform_device.h>
+#include <linux/platform_data/edma.h>
 
 #include <asm/mach/map.h>
 
 #include <mach/cputype.h>
-#include <mach/edma.h>
 #include <mach/irqs.h>
 #include <mach/psc.h>
 #include <mach/mux.h>
index 700d311c6854f0abc3054d4a732fbf8fc522a418..9d77f9b2857331b5cee5a4dcfaded4e92eece38e 100644 (file)
@@ -20,8 +20,8 @@
 #include <linux/videodev2.h>
 
 #include <mach/serial.h>
-#include <mach/edma.h>
 #include <mach/pm.h>
+#include <linux/platform_data/edma.h>
 #include <linux/platform_data/i2c-davinci.h>
 #include <linux/platform_data/mmc-davinci.h>
 #include <linux/platform_data/usb-davinci.h>
index 222d58c0ae76951efa9651e064182cf138ed0dbd..3889b6cd211e704b325573468246b28a45cf299d 100644 (file)
@@ -19,10 +19,6 @@ obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o
 # Power Management
 obj-$(CONFIG_PM) += pm.o sleep.o
 
-# DSP
-obj-$(CONFIG_OMAP_MBOX_FWK)    += mailbox_mach.o
-mailbox_mach-objs              := mailbox.o
-
 i2c-omap-$(CONFIG_I2C_OMAP)            := i2c.o
 obj-y                                  += $(i2c-omap-m) $(i2c-omap-y)
 
index 41b581fd0213e6a636c921d531463f6979e4af17..3ceda910e4b98ac1c369fed8c79c4e0d043b428f 100644 (file)
@@ -24,6 +24,9 @@ config ARCH_OMAP2PLUS_TYPICAL
 config SOC_HAS_OMAP2_SDRC
        bool "OMAP2 SDRAM Controller support"
 
+config ARCH_HAS_BANDGAP
+       bool
+
 config SOC_HAS_REALTIME_COUNTER
        bool "Real time free running counter"
        depends on SOC_OMAP5
@@ -58,6 +61,7 @@ config ARCH_OMAP4
        default y
        depends on ARCH_OMAP2PLUS
        select ARCH_HAS_OPP
+       select ARCH_HAS_BANDGAP
        select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
        select ARM_CPU_SUSPEND if PM
        select ARM_ERRATA_720789
@@ -76,12 +80,16 @@ config ARCH_OMAP4
 
 config SOC_OMAP5
        bool "TI OMAP5"
+       select ARCH_HAS_BANDGAP
        select ARM_ARCH_TIMER
        select ARM_CPU_SUSPEND if PM
        select ARM_GIC
        select CPU_V7
        select HAVE_SMP
        select COMMON_CLK
+       select USB_ARCH_HAS_EHCI if USB_SUPPORT
+       select USB_ARCH_HAS_XHCI if USB_SUPPORT
+       select ARCH_NEEDS_CPU_IDLE_COUPLED
 
 comment "OMAP Core Type"
        depends on ARCH_OMAP2
@@ -114,9 +122,11 @@ config SOC_AM33XX
        bool "AM33XX support"
        default y
        select ARM_CPU_SUSPEND if PM
+       select COMMON_CLK
        select CPU_V7
+       select MAILBOX if PM
        select MULTI_IRQ_HANDLER
-       select COMMON_CLK
+       select OMAP2PLUS_MBOX if PM
 
 config OMAP_PACKAGE_ZAF
        bool
@@ -397,7 +407,7 @@ config OMAP3_SDRC_AC_TIMING
 
 config OMAP4_ERRATA_I688
        bool "OMAP4 errata: Async Bridge Corruption"
-       depends on ARCH_OMAP4
+       depends on ARCH_OMAP4 || SOC_OMAP5
        select ARCH_HAS_BARRIERS
        help
          If a data is stalled inside asynchronous bridge because of back
@@ -416,6 +426,20 @@ config OMAP4_ERRATA_I688
          In MPU case, L3 T2ASYNC FIFO and DDR T2ASYNC FIFO needs to be drained.
          IO barrier ensure that there is no synchronisation loss on initiators
          operating on both interconnect port simultaneously.
+
+config OMAP4_HS_SECURE_SRAM_SIZE
+       int "Secure SRAM Resized size for OMAP4 HS devices in KB"
+       depends on ARCH_OMAP4
+       default 52
+       help
+         resized secure SRAM size for OMAP4
+
+config OMAP5_HS_SECURE_SRAM_SIZE
+       int "Secure SRAM Resized size for OMAP5 HS devices in KB"
+       depends on ARCH_OMAP5
+       default 120
+       help
+         resized secure SRAM size for OMAP5
 endmenu
 
 endif
index 947cafe65aefda11f9e00a43c8758807d1229f96..8c264aef5adef1f1945a0c26607f497d84030554 100644 (file)
@@ -8,7 +8,7 @@ obj-y := id.o io.o control.o mux.o devices.o fb.o serial.o gpmc.o timer.o pm.o \
         omap_device.o sram.o
 
 omap-2-3-common                                = irq.o
-hwmod-common                           = omap_hwmod.o \
+hwmod-common                           = omap_hwmod.o omap_hwmod_reset.o \
                                          omap_hwmod_common_data.o
 clock-common                           = clock.o clock_common_data.o \
                                          clkt_dpll.o clkt_clksel.o
@@ -32,14 +32,14 @@ obj-$(CONFIG_SOC_HAS_OMAP2_SDRC)    += sdrc.o
 obj-$(CONFIG_SMP)                      += omap-smp.o omap-headsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)              += omap-hotplug.o
 omap-4-5-common                                =  omap4-common.o omap-wakeupgen.o \
-                                          sleep44xx.o
+                                          sleep_omap4plus.o
 obj-$(CONFIG_ARCH_OMAP4)               += $(omap-4-5-common)
 obj-$(CONFIG_SOC_OMAP5)                        += $(omap-4-5-common)
 
 plus_sec := $(call as-instr,.arch_extension sec,+sec)
 AFLAGS_omap-headsmp.o                  :=-Wa,-march=armv7-a$(plus_sec)
 AFLAGS_omap-smc.o                      :=-Wa,-march=armv7-a$(plus_sec)
-AFLAGS_sleep44xx.o                     :=-Wa,-march=armv7-a$(plus_sec)
+AFLAGS_sleep_omap4plus.o               :=-Wa,-march=armv7-a$(plus_sec)
 
 # Functions loaded to SRAM
 obj-$(CONFIG_SOC_OMAP2420)             += sram242x.o
@@ -53,6 +53,7 @@ AFLAGS_sram34xx.o                     :=-Wa,-march=armv7-a
 # Restart code (OMAP4/5 currently in omap4-common.c)
 obj-$(CONFIG_SOC_OMAP2420)             += omap2-restart.o
 obj-$(CONFIG_SOC_OMAP2430)             += omap2-restart.o
+obj-$(CONFIG_SOC_AM33XX)               += am33xx-restart.o
 obj-$(CONFIG_ARCH_OMAP3)               += omap3-restart.o
 
 # Pin multiplexing
@@ -76,11 +77,13 @@ endif
 obj-$(CONFIG_OMAP_PM_NOOP)             += omap-pm-noop.o
 
 ifeq ($(CONFIG_PM),y)
+omap4plus-common-pm                    =  omap-mpuss-lowpower.o pm_omap4plus.o
 obj-$(CONFIG_ARCH_OMAP2)               += pm24xx.o
 obj-$(CONFIG_ARCH_OMAP2)               += sleep24xx.o
 obj-$(CONFIG_ARCH_OMAP3)               += pm34xx.o sleep34xx.o
-obj-$(CONFIG_ARCH_OMAP4)               += pm44xx.o omap-mpuss-lowpower.o
-obj-$(CONFIG_SOC_OMAP5)                        += omap-mpuss-lowpower.o
+obj-$(CONFIG_ARCH_OMAP4)               += $(omap4plus-common-pm)
+obj-$(CONFIG_SOC_OMAP5)                        += $(omap4plus-common-pm)
+obj-$(CONFIG_SOC_AM33XX)               += pm33xx.o sleep33xx.o
 obj-$(CONFIG_PM_DEBUG)                 += pm-debug.o
 
 obj-$(CONFIG_POWER_AVS_OMAP)           += sr_device.o
@@ -88,6 +91,7 @@ obj-$(CONFIG_POWER_AVS_OMAP_CLASS3)    += smartreflex-class3.o
 
 AFLAGS_sleep24xx.o                     :=-Wa,-march=armv6
 AFLAGS_sleep34xx.o                     :=-Wa,-march=armv7-a$(plus_sec)
+AFLAGS_sleep33xx.o                     :=-Wa,-march=armv7-a$(plus_sec)
 
 ifeq ($(CONFIG_PM_VERBOSE),y)
 CFLAGS_pm_bus.o                                += -DDEBUG
@@ -96,8 +100,10 @@ endif
 endif
 
 ifeq ($(CONFIG_CPU_IDLE),y)
+omap4plus-common-idle                  = cpuidle_omap4plus.o
 obj-$(CONFIG_ARCH_OMAP3)                += cpuidle34xx.o
-obj-$(CONFIG_ARCH_OMAP4)                += cpuidle44xx.o
+obj-$(CONFIG_ARCH_OMAP4)               += $(omap4plus-common-idle)
+obj-$(CONFIG_SOC_OMAP5)                        += $(omap4plus-common-idle)
 endif
 
 # PRCM
@@ -123,6 +129,7 @@ obj-$(CONFIG_ARCH_OMAP4)            += voltagedomains44xx_data.o
 obj-$(CONFIG_SOC_AM33XX)               += $(voltagedomain-common)
 obj-$(CONFIG_SOC_AM33XX)                += voltagedomains33xx_data.o
 obj-$(CONFIG_SOC_OMAP5)                        += $(voltagedomain-common)
+obj-$(CONFIG_SOC_OMAP5)                += voltagedomains54xx_data.o
 
 # OMAP powerdomain framework
 powerdomain-common                     += powerdomain.o powerdomain-common.o
@@ -137,6 +144,7 @@ obj-$(CONFIG_ARCH_OMAP4)            += powerdomains44xx_data.o
 obj-$(CONFIG_SOC_AM33XX)               += $(powerdomain-common)
 obj-$(CONFIG_SOC_AM33XX)               += powerdomains33xx_data.o
 obj-$(CONFIG_SOC_OMAP5)                        += $(powerdomain-common)
+obj-$(CONFIG_SOC_OMAP5)                        += powerdomains54xx_data.o
 
 # PRCM clockdomain control
 clockdomain-common                     += clockdomain.o
@@ -152,6 +160,7 @@ obj-$(CONFIG_ARCH_OMAP4)            += clockdomains44xx_data.o
 obj-$(CONFIG_SOC_AM33XX)               += $(clockdomain-common)
 obj-$(CONFIG_SOC_AM33XX)               += clockdomains33xx_data.o
 obj-$(CONFIG_SOC_OMAP5)                        += $(clockdomain-common)
+obj-$(CONFIG_SOC_OMAP5)                        += clockdomains54xx_data.o
 
 # Clock framework
 obj-$(CONFIG_ARCH_OMAP2)               += $(clock-common) clock2xxx.o
@@ -171,7 +180,7 @@ obj-$(CONFIG_ARCH_OMAP4)            += $(clock-common) cclock44xx_data.o
 obj-$(CONFIG_ARCH_OMAP4)               += dpll3xxx.o dpll44xx.o
 obj-$(CONFIG_SOC_AM33XX)               += $(clock-common) dpll3xxx.o
 obj-$(CONFIG_SOC_AM33XX)               += cclock33xx_data.o
-obj-$(CONFIG_SOC_OMAP5)                        += $(clock-common)
+obj-$(CONFIG_SOC_OMAP5)                        += $(clock-common) cclock54xx_data.o
 obj-$(CONFIG_SOC_OMAP5)                        += dpll3xxx.o dpll44xx.o
 
 # OMAP2 clock rate set data (old "OPP" data)
@@ -194,14 +203,12 @@ obj-$(CONFIG_ARCH_OMAP3)          += omap_hwmod_2xxx_3xxx_interconnect_data.o
 obj-$(CONFIG_ARCH_OMAP3)               += omap_hwmod_3xxx_data.o
 obj-$(CONFIG_SOC_AM33XX)               += omap_hwmod_33xx_data.o
 obj-$(CONFIG_ARCH_OMAP4)               += omap_hwmod_44xx_data.o
+obj-$(CONFIG_SOC_OMAP5)                        += omap_hwmod_54xx_data.o
 
 # EMU peripherals
 obj-$(CONFIG_OMAP3_EMU)                        += emu.o
 obj-$(CONFIG_HW_PERF_EVENTS)           += pmu.o
 
-obj-$(CONFIG_OMAP_MBOX_FWK)            += mailbox_mach.o
-mailbox_mach-objs                      := mailbox.o
-
 iommu-$(CONFIG_OMAP_IOMMU)             := omap-iommu.o
 obj-y                                  += $(iommu-m) $(iommu-y)
 
@@ -296,3 +303,7 @@ emac-$(CONFIG_TI_DAVINCI_EMAC)              := am35xx-emac.o
 obj-y                                  += $(emac-m) $(emac-y)
 
 obj-y                                  += common-board-devices.o twl-common.o dss-common.o
+
+# Serial ATA support
+s-ata-$(CONFIG_SATA_AHCI_PLATFORM)     := sata.o
+obj-$(CONFIG_SOC_OMAP5)                += $(s-ata-m) $(s-ata-y)
diff --git a/arch/arm/mach-omap2/am33xx-restart.c b/arch/arm/mach-omap2/am33xx-restart.c
new file mode 100644 (file)
index 0000000..88e4fa8
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * am33xx-restart.c - Code common to all AM33xx machines.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+
+#include "common.h"
+#include "prm-regbits-33xx.h"
+#include "prm33xx.h"
+
+/**
+ * am3xx_restart - trigger a software restart of the SoC
+ * @mode: the "reboot mode", see arch/arm/kernel/{setup,process}.c
+ * @cmd: passed from the userspace program rebooting the system (if provided)
+ *
+ * Resets the SoC.  For @cmd, see the 'reboot' syscall in
+ * kernel/sys.c.  No return value.
+ */
+void am33xx_restart(char mode, const char *cmd)
+{
+       /* TODO: Handle mode and cmd if necessary */
+
+       am33xx_prm_rmw_reg_bits(AM33XX_GLOBAL_WARM_SW_RST_MASK,
+                               AM33XX_GLOBAL_WARM_SW_RST_MASK,
+                               AM33XX_PRM_DEVICE_MOD,
+                               AM33XX_PRM_RSTCTRL_OFFSET);
+
+       /* OCP barrier */
+       (void)am33xx_prm_read_reg(AM33XX_PRM_DEVICE_MOD,
+                                 AM33XX_PRM_RSTCTRL_OFFSET);
+}
index 4815ea6f8f5dfc11f34af1c8e848bd0ed8d866c0..1337f2c51f9e7a0fd71837afc66547d63b12066b 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
+#include <linux/usb/phy.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -263,6 +264,7 @@ static void __init omap_2430sdp_init(void)
        omap_hsmmc_init(mmc);
 
        omap_mux_init_signal("usb0hs_stp", OMAP_PULL_ENA | OMAP_PULL_UP);
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
 
        board_smc91x_init();
index bb73afc9ac17cbc83e915145b7887b0e8203ff90..7e2d3babe3448f9699afcf641ed1203e4b17699a 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/spi/spi.h>
 #include <linux/i2c/twl.h>
 #include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/usb/phy.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
 #include <linux/mmc/host.h>
@@ -424,16 +426,86 @@ static void enable_board_wakeup_source(void)
                OMAP_WAKEUP_EN | OMAP_PIN_INPUT_PULLUP);
 }
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+/* PHY device on HS USB Port 1 i.e. nop_usb_xceiv.1 */
+static struct platform_device hsusb1_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 1,
+};
+
+/* Regulator for HS USB Port 1 PHY reset */
+static struct regulator_consumer_supply hsusb1_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.1"),
+};
+
+static struct regulator_init_data hsusb1_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb1_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb1_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb1_reset_config = {
+       .supply_name = "hsusb1_reset",
+       .microvolts = 3300000,
+       .gpio = 57,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb1_reset_data,
+};
+
+static struct platform_device hsusb1_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb1_reset_config,
+       },
+};
+
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = 61,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 
        .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
        .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
-
-       .phy_reset  = true,
-       .reset_gpio_port[0]  = 57,
-       .reset_gpio_port[1]  = 61,
-       .reset_gpio_port[2]  = -EINVAL
 };
 
 #ifdef CONFIG_OMAP_MUX
@@ -564,6 +636,13 @@ static struct flash_partitions sdp_flash_partitions[] = {
        },
 };
 
+static struct platform_device *sdp3430_devices[] __initdata = {
+       &hsusb1_phy_device,
+       &hsusb1_reset_device,
+       &hsusb2_phy_device,
+       &hsusb2_reset_device,
+};
+
 static void __init omap_3430sdp_init(void)
 {
        int gpio_pendown;
@@ -579,11 +658,20 @@ static void __init omap_3430sdp_init(void)
        omap_ads7846_init(1, gpio_pendown, 310, NULL);
        omap_serial_init();
        omap_sdrc_init(hyb18m512160af6_sdrc_params, NULL);
+
+       platform_add_devices(sdp3430_devices, ARRAY_SIZE(sdp3430_devices));
+
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
        board_smc91x_init();
        board_flash_init(sdp_flash_partitions, chip_sel_3430, 0);
        sdp3430_display_init();
        enable_board_wakeup_source();
+
+       /* PHY on HSUSB Port 1 i.e. index 0 */
+       usb_bind_phy("ehci-omap.0", 0, "nop_usb_xceiv.1");
+       /* PHY on HSUSB Port 2 i.e. index 1 */
+       usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
        usbhs_init(&usbhs_bdata);
 }
 
index 050aaa7712544fc9db648dbd48ad2e8e237e484b..43683ea0ee51943bde248c3296a3df63f6320f1d 100644 (file)
@@ -12,6 +12,9 @@
 #include <linux/input.h>
 #include <linux/gpio.h>
 #include <linux/mtd/nand.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/usb/phy.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -53,16 +56,86 @@ static void enable_board_wakeup_source(void)
                OMAP_WAKEUP_EN | OMAP_PIN_INPUT_PULLUP);
 }
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+/* PHY device on HS USB Port 1 i.e. nop_usb_xceiv.1 */
+static struct platform_device hsusb1_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 1,
+};
+
+/* Regulator for HS USB Port 1 PHY reset */
+static struct regulator_consumer_supply hsusb1_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.1"),
+};
+
+static struct regulator_init_data hsusb1_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb1_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb1_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb1_reset_config = {
+       .supply_name = "hsusb1_reset",
+       .microvolts = 3300000,
+       .gpio = 126,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb1_reset_data,
+};
+
+static struct platform_device hsusb1_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb1_reset_config,
+       },
+};
+
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = 61,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 
        .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
        .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
-
-       .phy_reset  = true,
-       .reset_gpio_port[0]  = 126,
-       .reset_gpio_port[1]  = 61,
-       .reset_gpio_port[2]  = -EINVAL
 };
 
 #ifdef CONFIG_OMAP_MUX
@@ -189,6 +262,13 @@ static struct flash_partitions sdp_flash_partitions[] = {
        },
 };
 
+static struct platform_device *sdp3630_devices[] __initdata = {
+       &hsusb1_phy_device,
+       &hsusb1_reset_device,
+       &hsusb2_phy_device,
+       &hsusb2_reset_device,
+};
+
 static void __init omap_sdp_init(void)
 {
        omap3_mux_init(board_mux, OMAP_PACKAGE_CBP);
@@ -199,6 +279,14 @@ static void __init omap_sdp_init(void)
        board_smc91x_init();
        board_flash_init(sdp_flash_partitions, chip_sel_sdp, NAND_BUSWIDTH_16);
        enable_board_wakeup_source();
+
+       platform_add_devices(sdp3630_devices, ARRAY_SIZE(sdp3630_devices));
+
+       /* PHY on HSUSB Port 1 i.e. index 0 */
+       usb_bind_phy("ehci-omap.0", 0, "nop_usb_xceiv.1");
+       /* PHY on HSUSB Port 2 i.e. index 1 */
+       usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
+
        usbhs_init(&usbhs_bdata);
 }
 
index 1cc6696594fd5493258b2df98874f6eb307a3bbf..cc1531c906d303dd2abd9fba2ae2a0bd70a67298 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/leds_pwm.h>
 #include <linux/platform_data/omap4-keypad.h>
 #include <linux/usb/musb.h>
+#include <linux/usb/phy.h>
 
 #include <asm/hardware/gic.h>
 #include <asm/mach-types.h>
@@ -56,6 +57,9 @@
 #define GPIO_WIFI_PMENA                54
 #define GPIO_WIFI_IRQ          53
 
+#define FIXED_REG_VBAT_ID      0
+#define FIXED_REG_VWLAN_ID     1
+
 static const int sdp4430_keymap[] = {
        KEY(0, 0, KEY_E),
        KEY(0, 1, KEY_R),
@@ -365,7 +369,7 @@ static struct fixed_voltage_config sdp4430_vbat_pdata = {
 
 static struct platform_device sdp4430_vbat = {
        .name           = "reg-fixed-voltage",
-       .id             = -1,
+       .id             = FIXED_REG_VBAT_ID,
        .dev = {
                .platform_data = &sdp4430_vbat_pdata,
        },
@@ -376,9 +380,9 @@ static struct platform_device sdp4430_dmic_codec = {
        .id     = -1,
 };
 
-static struct platform_device sdp4430_hdmi_audio_codec = {
-       .name   = "hdmi-audio-codec",
-       .id     = -1,
+static struct platform_device sdp4430_spdif_dit_codec = {
+       .name           = "spdif-dit",
+       .id             = -1,
 };
 
 static struct omap_abe_twl6040_data sdp4430_abe_audio_data = {
@@ -389,6 +393,7 @@ static struct omap_abe_twl6040_data sdp4430_abe_audio_data = {
        .has_aux        = ABE_TWL6040_LEFT | ABE_TWL6040_RIGHT,
        .has_vibra      = ABE_TWL6040_LEFT | ABE_TWL6040_RIGHT,
 
+       .has_abe        = 1,
        .has_dmic       = 1,
        .has_hsmic      = 1,
        .has_mainmic    = 1,
@@ -414,8 +419,8 @@ static struct platform_device *sdp4430_devices[] __initdata = {
        &sdp4430_leds_pwm,
        &sdp4430_vbat,
        &sdp4430_dmic_codec,
+       &sdp4430_spdif_dit_codec,
        &sdp4430_abe_audio,
-       &sdp4430_hdmi_audio_codec,
 };
 
 static struct omap_musb_board_data musb_board_data = {
@@ -481,7 +486,7 @@ static struct fixed_voltage_config sdp4430_vwlan = {
 
 static struct platform_device omap_vwlan_device = {
        .name           = "reg-fixed-voltage",
-       .id             = 1,
+       .id             = FIXED_REG_VWLAN_ID,
        .dev = {
                .platform_data = &sdp4430_vwlan,
        },
@@ -532,9 +537,14 @@ static struct twl6040_vibra_data twl6040_vibra = {
        .vddvibr_uV = 0,        /* fixed volt supply - VBAT */
 };
 
+static struct twl6040_gpo_data twl6040_gpo = {
+       .gpio_base = -1,
+};
+
 static struct twl6040_platform_data twl6040_data = {
        .codec          = &twl6040_codec,
        .vibra          = &twl6040_vibra,
+       .gpo            = &twl6040_gpo,
        .audpwron_gpio  = 127,
 };
 
@@ -696,6 +706,7 @@ static void __init omap_4430sdp_init(void)
        omap4_sdp4430_wifi_init();
        omap4_twl6030_hsmmc_init(mmc);
 
+       usb_bind_phy("musb-hdrc.0.auto", 0, "omap-usb2.1.auto");
        usb_musb_init(&musb_board_data);
 
        status = omap_ethernet_init();
index 51b96a1206d198dcdeef25ac5f76c0c06e9cb080..1d25af67463e60ab4d6adeed05fc0989a3aaedce 100644 (file)
@@ -20,6 +20,9 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/gpio.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/usb/phy.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -40,15 +43,84 @@ static struct omap_board_mux board_mux[] __initdata = {
 };
 #endif
 
-static struct usbhs_omap_board_data usbhs_bdata __initdata = {
+/* PHY device on HS USB Port 1 i.e. nop_usb_xceiv.1 */
+static struct platform_device hsusb1_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 1,
+};
+
+/* Regulator for HS USB Port 1 PHY reset */
+static struct regulator_consumer_supply hsusb1_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.1"),
+};
+
+static struct regulator_init_data hsusb1_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb1_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb1_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb1_reset_config = {
+       .supply_name = "hsusb1_reset",
+       .microvolts = 3300000,
+       .gpio = GPIO_USB_NRESET,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb1_reset_data,
+};
+
+static struct platform_device hsusb1_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb1_reset_config,
+       },
+};
+
+/* Regulator for HS USB Port 1 supply */
+static struct regulator_consumer_supply hsusb1_power_supplies[] = {
+/* Link PHY device to power supply so it gets enabled in the PHY driver */
+       REGULATOR_SUPPLY("vcc", "nop_usb_xceiv.1"),
+};
+
+static struct regulator_init_data hsusb1_power_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb1_power_supplies,
+       .num_consumer_supplies  = ARRAY_SIZE(hsusb1_power_supplies),
+};
+
+static struct fixed_voltage_config hsusb1_power_config = {
+       .supply_name = "hsusb1_vbus",
+       .microvolts = 5000000,
+       .gpio = GPIO_USB_POWER,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,
+       .init_data = &hsusb1_power_data,
+};
+
+static struct platform_device hsusb1_power_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb1_power_config,
+       },
+};
+
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
        .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
+};
 
-       .phy_reset  = true,
-       .reset_gpio_port[0]  = GPIO_USB_NRESET,
-       .reset_gpio_port[1]  = -EINVAL,
-       .reset_gpio_port[2]  = -EINVAL
+static struct platform_device *am3517_crane_devices[] __initdata = {
+       &hsusb1_phy_device,
+       &hsusb1_reset_device,
+       &hsusb1_power_device,
 };
 
 static void __init am3517_crane_init(void)
@@ -72,12 +144,11 @@ static void __init am3517_crane_init(void)
                return;
        }
 
-       ret = gpio_request_one(GPIO_USB_POWER, GPIOF_OUT_INIT_HIGH,
-                              "usb_ehci_enable");
-       if (ret < 0) {
-               pr_err("Can not request GPIO %d\n", GPIO_USB_POWER);
-               return;
-       }
+       platform_add_devices(am3517_crane_devices,
+                               ARRAY_SIZE(am3517_crane_devices));
+
+       /* PHY on HSUSB Port 1 i.e. index 0 */
+       usb_bind_phy("ehci-omap.0", 0, "nop_usb_xceiv.1");
 
        usbhs_init(&usbhs_bdata);
        am35xx_emac_init(AM35XX_DEFAULT_MDIO_FREQUENCY, 1);
index f81a303b87ff1afbba44e9ddcfdea4dcb2363ffb..a06feb0a375bdeadd25440bbc23040e487ee00e9 100644 (file)
@@ -25,6 +25,9 @@
 #include <linux/can/platform/ti_hecc.h>
 #include <linux/davinci_emac.h>
 #include <linux/mmc/host.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/usb/phy.h>
 #include <linux/usb/musb.h>
 #include <linux/platform_data/gpio-omap.h>
 
@@ -274,7 +277,51 @@ static __init void am3517_evm_mcbsp1_init(void)
        omap_ctrl_writel(devconf0, OMAP2_CONTROL_DEVCONF0);
 }
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+/* PHY device on HS USB Port 1 i.e. nop_usb_xceiv.1 */
+static struct platform_device hsusb1_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 1,
+};
+
+/* Regulator for HS USB Port 1 PHY reset */
+static struct regulator_consumer_supply hsusb1_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.1"),
+};
+
+static struct regulator_init_data hsusb1_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb1_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb1_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb1_reset_config = {
+       .supply_name = "hsusb1_reset",
+       .microvolts = 3300000,
+       .gpio = 57,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb1_reset_data,
+};
+
+static struct platform_device hsusb1_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb1_reset_config,
+       },
+};
+
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
        .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
 #if defined(CONFIG_PANEL_SHARP_LQ043T1DG01) || \
                defined(CONFIG_PANEL_SHARP_LQ043T1DG01_MODULE)
@@ -282,12 +329,6 @@ static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
 #else
        .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
 #endif
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
-
-       .phy_reset  = true,
-       .reset_gpio_port[0]  = 57,
-       .reset_gpio_port[1]  = -EINVAL,
-       .reset_gpio_port[2]  = -EINVAL
 };
 
 #ifdef CONFIG_OMAP_MUX
@@ -349,6 +390,11 @@ static struct omap2_hsmmc_info mmc[] = {
        {}      /* Terminator */
 };
 
+static struct platform_device *am3517evm_devices[] __initdata = {
+       &hsusb1_phy_device,
+       &hsusb1_reset_device,
+       &hsusb2_phy_device,
+};
 
 static void __init am3517_evm_init(void)
 {
@@ -361,6 +407,14 @@ static void __init am3517_evm_init(void)
 
        /* Configure GPIO for EHCI port */
        omap_mux_init_gpio(57, OMAP_PIN_OUTPUT);
+
+       platform_add_devices(am3517evm_devices, ARRAY_SIZE(am3517evm_devices));
+
+       /* PHY on HSUSB Port 1 i.e. index 0 */
+       usb_bind_phy("ehci-omap.0", 0, "nop_usb_xceiv.1");
+       /* PHY on HSUSB Port 2 i.e. index 1 */
+       usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
+
        usbhs_init(&usbhs_bdata);
        am3517_evm_hecc_init(&am3517_evm_hecc_pdata);
        /* DSS */
index b3102c2f4a3cbbede79af9aeb10beecf808a60a5..b4fd3e9d971d9521c3840ff44455e7eba5fede14 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/i2c/twl.h>
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/machine.h>
+#include <linux/usb/phy.h>
 #include <linux/mmc/host.h>
 
 #include <linux/spi/spi.h>
@@ -418,15 +419,92 @@ static struct omap2_hsmmc_info mmc[] = {
        {}      /* Terminator */
 };
 
-static struct usbhs_omap_board_data usbhs_bdata __initdata = {
+/* PHY device on HS USB Port 1 i.e. nop_usb_xceiv.1 */
+static struct platform_device hsusb1_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 1,
+};
+
+/* Regulator for HS USB Port 1 PHY reset */
+static struct regulator_consumer_supply hsusb1_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.1"),
+};
+
+static struct regulator_init_data hsusb1_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb1_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb1_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb1_reset_config = {
+       .supply_name = "hsusb1_reset",
+       .microvolts = 3300000,
+       .gpio = OMAP_MAX_GPIO_LINES + 6,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb1_reset_data,
+};
+
+static struct platform_device hsusb1_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb1_reset_config,
+       },
+};
+
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = OMAP_MAX_GPIO_LINES + 7,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
        .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
        .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
+};
 
-       .phy_reset  = true,
-       .reset_gpio_port[0]  = OMAP_MAX_GPIO_LINES + 6,
-       .reset_gpio_port[1]  = OMAP_MAX_GPIO_LINES + 7,
-       .reset_gpio_port[2]  = -EINVAL
+static struct platform_device *usbhs_devices[] = {
+       &hsusb1_phy_device,
+       &hsusb1_reset_device,
+       &hsusb2_phy_device,
+       &hsusb2_reset_device,
 };
 
 static void  __init cm_t35_init_usbh(void)
@@ -443,6 +521,13 @@ static void  __init cm_t35_init_usbh(void)
                msleep(1);
        }
 
+       platform_add_devices(usbhs_devices, ARRAY_SIZE(usbhs_devices));
+
+       /* PHY on HSUSB Port 1 i.e. index 0 */
+       usb_bind_phy("ehci-omap.0", 0, "nop_usb_xceiv.1");
+       /* PHY on HSUSB Port 2 i.e. index 1 */
+       usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
+
        usbhs_init(&usbhs_bdata);
 }
 
@@ -724,6 +809,7 @@ static void __init cm_t3x_common_init(void)
        cm_t35_init_display();
        omap_twl4030_audio_init("cm-t3x");
 
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
        cm_t35_init_usbh();
        cm_t35_init_camera();
index ebbc2adb499e9300d6213f8f4968aa6306c4a87e..2ba97c5acffbd15e898427cf8271be17f47145b0 100644 (file)
@@ -33,6 +33,9 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/can/platform/ti_hecc.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/usb/phy.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -166,15 +169,92 @@ static inline void cm_t3517_init_rtc(void) {}
 #define HSUSB2_RESET_GPIO      (147)
 #define USB_HUB_RESET_GPIO     (152)
 
-static struct usbhs_omap_board_data cm_t3517_ehci_pdata __initdata = {
+/* PHY device on HS USB Port 1 i.e. nop_usb_xceiv.1 */
+static struct platform_device hsusb1_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 1,
+};
+
+/* Regulator for HS USB Port 1 PHY reset */
+static struct regulator_consumer_supply hsusb1_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.1"),
+};
+
+static struct regulator_init_data hsusb1_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb1_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb1_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb1_reset_config = {
+       .supply_name = "hsusb1_reset",
+       .microvolts = 3300000,
+       .gpio = HSUSB1_RESET_GPIO,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb1_reset_data,
+};
+
+static struct platform_device hsusb1_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb1_reset_config,
+       },
+};
+
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = HSUSB2_RESET_GPIO,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
+static struct usbhs_omap_platform_data cm_t3517_ehci_pdata __initdata = {
        .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
        .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
+};
 
-       .phy_reset  = true,
-       .reset_gpio_port[0]  = HSUSB1_RESET_GPIO,
-       .reset_gpio_port[1]  = HSUSB2_RESET_GPIO,
-       .reset_gpio_port[2]  = -EINVAL,
+static struct platform_device *usbhs_devices[] = {
+       &hsusb1_phy_device,
+       &hsusb1_reset_device,
+       &hsusb2_phy_device,
+       &hsusb2_reset_device,
 };
 
 static int __init cm_t3517_init_usbh(void)
@@ -191,6 +271,13 @@ static int __init cm_t3517_init_usbh(void)
                msleep(1);
        }
 
+       platform_add_devices(usbhs_devices, ARRAY_SIZE(usbhs_devices));
+
+       /* PHY on HSUSB Port 1 i.e. index 0 */
+       usb_bind_phy("ehci-omap.0", 0, "nop_usb_xceiv.1");
+       /* PHY on HSUSB Port 2 i.e. index 1 */
+       usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
+
        usbhs_init(&cm_t3517_ehci_pdata);
 
        return 0;
index 12865af25d3a865b351e8dff3b8cb5fc20649ca5..99fdd4440acf0e2fa8c2ec7188765f05800d944d 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <linux/regulator/machine.h>
 #include <linux/i2c/twl.h>
+#include <linux/usb/phy.h>
 #include "id.h"
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -429,22 +430,21 @@ static void __init omap_dm9000_init(void)
        eth_addr[5] = (odi.id_0 & 0x000000ff);
 }
 
+/* PHY device on HS USB Port 1 i.e. nop_usb_xceiv.1 */
+static struct platform_device hsusb1_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 1,
+};
+
 static struct platform_device *devkit8000_devices[] __initdata = {
        &leds_gpio,
        &keys_gpio,
        &omap_dm9000_dev,
+       &hsusb1_phy_device,
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
-
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
        .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
-
-       .phy_reset  = true,
-       .reset_gpio_port[0]  = -EINVAL,
-       .reset_gpio_port[1]  = -EINVAL,
-       .reset_gpio_port[2]  = -EINVAL
 };
 
 #ifdef CONFIG_OMAP_MUX
@@ -622,7 +622,12 @@ static void __init devkit8000_init(void)
 
        omap_ads7846_init(2, OMAP3_DEVKIT_TS_GPIO, 0, NULL);
 
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
+
+       /* PHY on HSUSB Port 1 i.e. index 0 */
+       usb_bind_phy("ehci-omap.0", 0, "nop_usb_xceiv.1");
+
        usbhs_init(&usbhs_bdata);
        board_nand_init(devkit8000_nand_partitions,
                        ARRAY_SIZE(devkit8000_nand_partitions), NAND_CS,
index 53cb380b7877a3fb6e354d97db1586523e2f78a3..7baede11cd2cb27977407ba70408d797422d2204 100644 (file)
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/irqdomain.h>
+#include <linux/clk.h>
+#include <linux/string.h>
+#include <linux/slab.h>
 
 #include <asm/hardware/gic.h>
 #include <asm/mach/arch.h>
 
+#include <plat/sata.h>
+
 #include "common.h"
 #include "common-board-devices.h"
 #include "dss-common.h"
@@ -36,20 +41,80 @@ static struct of_device_id omap_dt_match_table[] __initdata = {
        { }
 };
 
+static int __init omap_create_clk_alias(struct device_node *np)
+{
+       int ret = 0;
+       const char *s, *alias;
+       char *clk_id;
+       struct device_node *dev_np;
+       struct platform_device *pdev;
+
+       of_property_read_string(np, "clock-name", &s);
+       if (!s) {
+               pr_err("%s: couldn't find clock-name property in node %s\n",
+                               __func__, np->name);
+               return -ENODEV;
+       }
+
+       clk_id = kstrdup(s, GFP_KERNEL);
+       if (!clk_id)
+               return -ENOMEM;
+
+       dev_np = of_parse_phandle(np, "device", 0);
+       if (!dev_np) {
+               pr_err("%s: couldn't find device phandle for \'%s\'\n",
+                               __func__, clk_id);
+               ret = -ENODEV;
+               goto exit;
+       }
+
+       pdev = of_find_device_by_node(dev_np);
+       if (!pdev) {
+               pr_err("%s: couldn't find device for clock \'%s\'\n",
+                               __func__, clk_id);
+               ret = -ENODEV;
+               goto exit;
+       }
+
+       ret = of_property_read_string(np, "clock-alias", &alias);
+       if (ret) {
+               pr_err("%s: couldn't find alias for clock \'%s\'\n",
+                               __func__, clk_id);
+               ret = -ENODEV;
+               goto exit;
+       }
+
+       ret = clk_add_alias(alias, dev_name(&pdev->dev), clk_id, NULL);
+       if (ret) {
+               pr_err("%s: couldn't add alias \'%s\' to clock \'%s\'\n",
+                               __func__, alias, clk_id);
+               ret = -ENODEV;
+               goto exit;
+       }
+
+exit:
+       kfree(clk_id);
+       return ret;
+}
+
 static void __init omap_generic_init(void)
 {
+       struct device_node *np;
+
        omap_sdrc_init(NULL, NULL);
 
        of_platform_populate(NULL, omap_dt_match_table, NULL, NULL);
 
-       /*
-        * HACK: call display setup code for selected boards to enable omapdss.
-        * This will be removed when omapdss supports DT.
-        */
-       if (of_machine_is_compatible("ti,omap4-panda"))
-               omap4_panda_display_init_of();
-       else if (of_machine_is_compatible("ti,omap4-sdp"))
-               omap_4430sdp_display_init_of();
+       omapdss_init_of();
+
+       /* create clock aliases based on 'clock_alias' nodes */
+       for_each_node_by_name(np, "clock_alias") {
+               omap_create_clk_alias(np);
+               of_node_put(np);
+       }
+
+       if (of_machine_is_compatible("ti,omap5"))
+               omap_sata_init();
 }
 
 #ifdef CONFIG_SOC_OMAP2420
@@ -136,11 +201,13 @@ DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)")
        .reserve        = omap_reserve,
        .map_io         = am33xx_map_io,
        .init_early     = am33xx_init_early,
+       .init_late      = am33xx_init_late,
        .init_irq       = omap_intc_of_init,
        .handle_irq     = omap3_intc_handle_irq,
        .init_machine   = omap_generic_init,
        .timer          = &omap3_am33xx_timer,
        .dt_compat      = am33xx_boards_compat,
+       .restart        = am33xx_restart,
 MACHINE_END
 #endif
 
@@ -179,6 +246,7 @@ DT_MACHINE_START(OMAP5_DT, "Generic OMAP5 (Flattened Device Tree)")
        .init_irq       = omap_gic_of_init,
        .handle_irq     = gic_handle_irq,
        .init_machine   = omap_generic_init,
+       .init_late      = omap5_init_late,
        .timer          = &omap5_timer,
        .dt_compat      = omap5_boards_compat,
        .restart        = omap44xx_restart,
index 0f24cb84ba5a524ff0256462d84be6cc2d6d9b0e..51a7c98515bb8cb7486a1176d626b6f20880f75b 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <linux/regulator/machine.h>
 #include <linux/regulator/fixed.h>
+#include <linux/usb/phy.h>
 #include <linux/i2c/twl.h>
 #include <linux/mmc/host.h>
 
@@ -526,26 +527,98 @@ static void __init igep_i2c_init(void)
        omap3_pmic_init("twl4030", &igep_twldata);
 }
 
-static const struct usbhs_omap_board_data igep2_usbhs_bdata __initconst = {
-       .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
+/* PHY device on HS USB Port 1 i.e. nop_usb_xceiv.1 */
+static struct platform_device hsusb1_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 1,
+};
 
-       .phy_reset = true,
-       .reset_gpio_port[0] = IGEP2_GPIO_USBH_NRESET,
-       .reset_gpio_port[1] = -EINVAL,
-       .reset_gpio_port[2] = -EINVAL,
+/* Regulator for HS USB Port 1 PHY reset */
+static struct regulator_consumer_supply hsusb1_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.1"),
 };
 
-static const struct usbhs_omap_board_data igep3_usbhs_bdata __initconst = {
-       .port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
-       .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
+static struct regulator_init_data hsusb1_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb1_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb1_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb1_reset_config = {
+       .supply_name = "hsusb1_reset",
+       .microvolts = 3300000,
+       .gpio = IGEP2_GPIO_USBH_NRESET,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb1_reset_data,
+};
+
+static struct platform_device hsusb1_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb1_reset_config,
+       },
+};
+
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
 
-       .phy_reset = true,
-       .reset_gpio_port[0] = -EINVAL,
-       .reset_gpio_port[1] = IGEP3_GPIO_USBH_NRESET,
-       .reset_gpio_port[2] = -EINVAL,
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = IGEP3_GPIO_USBH_NRESET,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
+static struct platform_device *igep2_devices[] __initdata = {
+       &hsusb1_phy_device,
+       &hsusb1_reset_device,
+};
+
+static struct platform_device *igep3_devices[] __initdata = {
+       &hsusb2_phy_device,
+       &hsusb2_reset_device,
+};
+
+static struct usbhs_omap_platform_data igep2_usbhs_bdata __initdata = {
+       .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
+};
+
+static struct usbhs_omap_platform_data igep3_usbhs_bdata __initdata = {
+       .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
 };
 
 #ifdef CONFIG_OMAP_MUX
@@ -625,6 +698,7 @@ static void __init igep_init(void)
        omap_serial_init();
        omap_sdrc_init(m65kxxxxam_sdrc_params,
                                  m65kxxxxam_sdrc_params);
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
 
        igep_flash_init();
@@ -640,8 +714,19 @@ static void __init igep_init(void)
        if (machine_is_igep0020()) {
                omap_display_init(&igep2_dss_data);
                igep2_init_smsc911x();
+
+               platform_add_devices(igep2_devices, ARRAY_SIZE(igep2_devices));
+
+               /* PHY on HSUSB Port 1 i.e. index 0 */
+               usb_bind_phy("ehci-omap.0", 0, "nop_usb_xceiv.1");
+
                usbhs_init(&igep2_usbhs_bdata);
        } else {
+               platform_add_devices(igep3_devices, ARRAY_SIZE(igep3_devices));
+
+               /* PHY on HSUSB Port 2 i.e. index 1 */
+               usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
+
                usbhs_init(&igep3_usbhs_bdata);
        }
 }
index 0869f4f3d3e11264fce04e29b02c5e046f61193f..3b5510a433f0a7476d2a2cbdd2049bd6a2e4da9d 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/io.h>
 #include <linux/smsc911x.h>
 #include <linux/mmc/host.h>
+#include <linux/usb/phy.h>
 #include <linux/platform_data/spi-omap2-mcspi.h>
 
 #include <asm/mach-types.h>
@@ -418,6 +419,7 @@ static void __init omap_ldp_init(void)
        omap_ads7846_init(1, 54, 310, NULL);
        omap_serial_init();
        omap_sdrc_init(NULL, NULL);
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
        board_nand_init(ldp_nand_partitions, ARRAY_SIZE(ldp_nand_partitions),
                        ZOOM_NAND_CS, 0, nand_default_timings);
index 22c483d5dfa829891fd8ec9ece81f35c94cf152b..116d2833c09861cad446f27a4855bb8d06763f19 100644 (file)
@@ -32,7 +32,9 @@
 #include <linux/mmc/host.h>
 
 #include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
 #include <linux/i2c/twl.h>
+#include <linux/usb/phy.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -248,6 +250,76 @@ static struct regulator_consumer_supply beagle_vsim_supply[] = {
 
 static struct gpio_led gpio_leds[];
 
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = 147,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
+/* Regulator for HS USB Port 2 supply */
+static struct regulator_consumer_supply hsusb2_power_supplies[] = {
+/* Link PHY device to power supply so it gets enabled in the PHY driver */
+       REGULATOR_SUPPLY("vcc", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_power_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_power_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_power_supplies),
+};
+
+static struct fixed_voltage_config hsusb2_power_config = {
+       .supply_name = "hsusb2_vbus",
+       .microvolts = 5000000,
+       .gpio = -1,             /* set at runtime in beagle_twl_gpio_setup */
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 0,       /* updated in omap3_beagle_init_rev() */
+       .enabled_at_boot = 0,
+       .init_data = &hsusb2_power_data,
+};
+
+static struct platform_device hsusb2_power_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_power_config,
+       },
+};
+
 static int beagle_twl_gpio_setup(struct device *dev,
                unsigned gpio, unsigned ngpio)
 {
@@ -289,8 +361,12 @@ static int beagle_twl_gpio_setup(struct device *dev,
        }
        dvi_panel.power_down_gpio = beagle_config.dvi_pd_gpio;
 
-       gpio_request_one(gpio + TWL4030_GPIO_MAX, beagle_config.usb_pwr_level,
-                       "nEN_USB_PWR");
+       /* TWL4030_GPIO_MAX controls HS USB Port 2 power */
+       hsusb2_power_config.gpio = gpio + TWL4030_GPIO_MAX;
+       hsusb2_power_config.enable_high = beagle_config.usb_pwr_level;
+
+       platform_device_register(&hsusb2_power_device);
+       platform_device_register(&hsusb2_phy_device);
 
        /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */
        gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1;
@@ -428,18 +504,11 @@ static struct platform_device *omap3_beagle_devices[] __initdata = {
        &leds_gpio,
        &keys_gpio,
        &madc_hwmon,
+       &hsusb2_reset_device,
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
-
-       .port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
        .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
-
-       .phy_reset  = true,
-       .reset_gpio_port[0]  = -EINVAL,
-       .reset_gpio_port[1]  = 147,
-       .reset_gpio_port[2]  = -EINVAL
 };
 
 #ifdef CONFIG_OMAP_MUX
@@ -519,8 +588,13 @@ static void __init omap3_beagle_init(void)
        omap_sdrc_init(mt46h32m32lf6_sdrc_params,
                                  mt46h32m32lf6_sdrc_params);
 
+       usb_bind_phy("musb-hdrc.1.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
+
+       /* PHY on HSUSB Port 2 i.e. index 1 */
+       usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
        usbhs_init(&usbhs_bdata);
+
        board_nand_init(omap3beagle_nand_partitions,
                        ARRAY_SIZE(omap3beagle_nand_partitions), NAND_CS,
                        NAND_BUSWIDTH_16, NULL);
index 3985f35aee06a6cffbc05d2930b56c456231ee97..29ed1290862070298a01c84ba98732262a101813 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/usb/otg.h>
 #include <linux/usb/musb.h>
 #include <linux/usb/nop-usb-xceiv.h>
+#include <linux/usb/phy.h>
 #include <linux/smsc911x.h>
 
 #include <linux/wl12xx.h>
@@ -309,7 +310,7 @@ static struct omap2_hsmmc_info mmc[] = {
                .gpio_wp        = 63,
                .deferred       = true,
        },
-#ifdef CONFIG_WL12XX_PLATFORM_DATA
+#ifdef CONFIG_WILINK_PLATFORM_DATA
        {
                .name           = "wl1271",
                .mmc            = 2,
@@ -450,7 +451,7 @@ static struct regulator_init_data omap3evm_vio = {
        .consumer_supplies      = omap3evm_vio_supply,
 };
 
-#ifdef CONFIG_WL12XX_PLATFORM_DATA
+#ifdef CONFIG_WILINK_PLATFORM_DATA
 
 #define OMAP3EVM_WLAN_PMENA_GPIO       (150)
 #define OMAP3EVM_WLAN_IRQ_GPIO         (149)
@@ -538,17 +539,51 @@ static int __init omap3_evm_i2c_init(void)
        return 0;
 }
 
-static struct usbhs_omap_board_data usbhs_bdata __initdata = {
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
 
-       .port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
-       .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
 
-       .phy_reset  = true,
-       /* PHY reset GPIO will be runtime programmed based on EVM version */
-       .reset_gpio_port[0]  = -EINVAL,
-       .reset_gpio_port[1]  = -EINVAL,
-       .reset_gpio_port[2]  = -EINVAL
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = -1,             /* set at runtime */
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
+static struct platform_device *omap3evm_devices[] __initdata = {
+       &hsusb2_phy_device,
+       &hsusb2_reset_device,
+};
+
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
+       .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
 };
 
 #ifdef CONFIG_OMAP_MUX
@@ -563,7 +598,7 @@ static struct omap_board_mux omap35x_board_mux[] __initdata = {
                                OMAP_PIN_OFF_NONE),
        OMAP3_MUX(GPMC_WAIT2, OMAP_MUX_MODE4 | OMAP_PIN_INPUT_PULLUP |
                                OMAP_PIN_OFF_NONE),
-#ifdef CONFIG_WL12XX_PLATFORM_DATA
+#ifdef CONFIG_WILINK_PLATFORM_DATA
        /* WLAN IRQ - GPIO 149 */
        OMAP3_MUX(UART1_RTS, OMAP_MUX_MODE4 | OMAP_PIN_INPUT),
 
@@ -601,7 +636,7 @@ static struct omap_board_mux omap36x_board_mux[] __initdata = {
        OMAP3_MUX(SYS_BOOT4, OMAP_MUX_MODE3 | OMAP_PIN_OFF_NONE),
        OMAP3_MUX(SYS_BOOT5, OMAP_MUX_MODE3 | OMAP_PIN_OFF_NONE),
        OMAP3_MUX(SYS_BOOT6, OMAP_MUX_MODE3 | OMAP_PIN_OFF_NONE),
-#ifdef CONFIG_WL12XX_PLATFORM_DATA
+#ifdef CONFIG_WILINK_PLATFORM_DATA
        /* WLAN IRQ - GPIO 149 */
        OMAP3_MUX(UART1_RTS, OMAP_MUX_MODE4 | OMAP_PIN_INPUT),
 
@@ -637,7 +672,7 @@ static struct gpio omap3_evm_ehci_gpios[] __initdata = {
 
 static void __init omap3_evm_wl12xx_init(void)
 {
-#ifdef CONFIG_WL12XX_PLATFORM_DATA
+#ifdef CONFIG_WILINK_PLATFORM_DATA
        int ret;
 
        /* WL12xx WLAN Init */
@@ -724,7 +759,7 @@ static void __init omap3_evm_init(void)
 
                /* setup EHCI phy reset config */
                omap_mux_init_gpio(21, OMAP_PIN_INPUT_PULLUP);
-               usbhs_bdata.reset_gpio_port[1] = 21;
+               hsusb2_reset_config.gpio = 21;
 
                /* EVM REV >= E can supply 500mA with EXTVBUS programming */
                musb_board_data.power = 500;
@@ -732,9 +767,16 @@ static void __init omap3_evm_init(void)
        } else {
                /* setup EHCI phy reset on MDC */
                omap_mux_init_gpio(135, OMAP_PIN_OUTPUT);
-               usbhs_bdata.reset_gpio_port[1] = 135;
+               hsusb2_reset_config.gpio = 135;
        }
+
+       platform_add_devices(omap3evm_devices, ARRAY_SIZE(omap3evm_devices));
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(&musb_board_data);
+
+       /* PHY on HSUSB Port 2 i.e. index 1 */
+       usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
+
        usbhs_init(&usbhs_bdata);
        board_nand_init(omap3evm_nand_partitions,
                        ARRAY_SIZE(omap3evm_nand_partitions), NAND_CS,
index 2a065ba6eb58b9c3da2c265d5616e19904263716..9409eb897e2fe050e641c7f57581bbcb08eec3f7 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <linux/i2c/twl.h>
 #include <linux/mmc/host.h>
+#include <linux/usb/phy.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -215,6 +216,7 @@ static void __init omap3logic_init(void)
        board_mmc_init();
        board_smsc911x_init();
 
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
 
        /* Ensure SDRC pins are mux'd for self-refresh */
index a53a6683c1b8b103f88cb3e5e1019a04a6b751a4..f57f258341aedb3b83b1fc3fa803d32a95d4c6c1 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <linux/spi/spi.h>
 #include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
 #include <linux/i2c/twl.h>
 #include <linux/wl12xx.h>
 #include <linux/mtd/partitions.h>
@@ -35,6 +36,7 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
 #include <linux/regulator/fixed.h>
+#include <linux/usb/phy.h>
 #include <linux/platform_data/spi-omap2-mcspi.h>
 
 #include <asm/mach-types.h>
@@ -560,23 +562,55 @@ fail:
        printk(KERN_ERR "wl1251 board initialisation failed\n");
 }
 
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = 16,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
 static struct platform_device *omap3pandora_devices[] __initdata = {
        &pandora_leds_gpio,
        &pandora_keys_gpio,
        &pandora_vwlan_device,
        &pandora_backlight,
+       &hsusb2_phy_device,
+       &hsusb2_reset_device,
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
-
-       .port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
        .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
-
-       .phy_reset  = true,
-       .reset_gpio_port[0]  = -EINVAL,
-       .reset_gpio_port[1]  = 16,
-       .reset_gpio_port[2]  = -EINVAL
 };
 
 #ifdef CONFIG_OMAP_MUX
@@ -601,6 +635,11 @@ static void __init omap3pandora_init(void)
                        ARRAY_SIZE(omap3pandora_spi_board_info));
        omap_ads7846_init(1, OMAP3_PANDORA_TS_GPIO, 0, NULL);
        usbhs_init(&usbhs_bdata);
+
+       /* PHY on HSUSB Port 2 i.e. index 1 */
+       usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
+
        usb_musb_init(NULL);
        gpmc_nand_init(&pandora_nand_data, NULL);
 
index 53a6cbcf9747bdd88acbbf9fdd155db05a5dc817..1762e4cd6f2f0697971781c5ac4158e469ab0158 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/machine.h>
+#include <linux/usb/phy.h>
 #include <linux/i2c/twl.h>
 #include <linux/mmc/host.h>
 #include <linux/input/matrix_keypad.h>
@@ -357,19 +358,52 @@ static int __init omap3_stalker_i2c_init(void)
 
 #define OMAP3_STALKER_TS_GPIO  175
 
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = 21,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
 static struct platform_device *omap3_stalker_devices[] __initdata = {
        &keys_gpio,
+       &hsusb2_phy_device,
+       &hsusb2_reset_device,
 };
 
-static struct usbhs_omap_board_data usbhs_bdata __initconst = {
-       .port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
        .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
-
-       .phy_reset = true,
-       .reset_gpio_port[0] = -EINVAL,
-       .reset_gpio_port[1] = 21,
-       .reset_gpio_port[2] = -EINVAL,
 };
 
 #ifdef CONFIG_OMAP_MUX
@@ -404,7 +438,12 @@ static void __init omap3_stalker_init(void)
 
        omap_serial_init();
        omap_sdrc_init(mt46h32m32lf6_sdrc_params, NULL);
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
+
+       /* PHY on HSUSB Port 2 i.e. index 1 */
+       usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
+
        usbhs_init(&usbhs_bdata);
        omap_ads7846_init(1, OMAP3_STALKER_TS_GPIO, 310, NULL);
 
index 263cb9cfbf3783e1c95d540bd807d652276725f3..ec39e3a76346ded578a7c52b1cefeb78f4136bba 100644 (file)
@@ -35,6 +35,8 @@
 #include <linux/spi/ads7846.h>
 
 #include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/usb/phy.h>
 #include <linux/i2c/twl.h>
 
 #include <asm/mach-types.h>
@@ -304,21 +306,61 @@ static struct omap_board_mux board_mux[] __initdata = {
 };
 #endif
 
+/* PHY device on HS USB Port 1 i.e. nop_usb_xceiv.1 */
+static struct platform_device hsusb1_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 1,
+};
+
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = 147,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
 static struct platform_device *omap3_touchbook_devices[] __initdata = {
        &leds_gpio,
        &keys_gpio,
+       &hsusb1_phy_device,
+       &hsusb2_phy_device,
+       &hsusb2_reset_device,
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
-
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
        .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
        .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
-
-       .phy_reset  = true,
-       .reset_gpio_port[0]  = -EINVAL,
-       .reset_gpio_port[1]  = 147,
-       .reset_gpio_port[2]  = -EINVAL
 };
 
 static void omap3_touchbook_poweroff(void)
@@ -365,7 +407,14 @@ static void __init omap3_touchbook_init(void)
 
        /* Touchscreen and accelerometer */
        omap_ads7846_init(4, OMAP3_TS_GPIO, 310, &ads7846_pdata);
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
+
+       /* PHY on HSUSB Port 1 i.e. index 0 */
+       usb_bind_phy("ehci-omap.0", 0, "nop_usb_xceiv.1");
+       /* PHY on HSUSB Port 2 i.e. index 1 */
+       usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
+
        usbhs_init(&usbhs_bdata);
        board_nand_init(omap3touchbook_nand_partitions,
                        ARRAY_SIZE(omap3touchbook_nand_partitions), NAND_CS,
index 769c1feee1c469049290f9101745cc28ef01742f..4f6f130749ace2886038f0151ae8a8cc8d09657b 100644 (file)
@@ -30,6 +30,8 @@
 #include <linux/regulator/fixed.h>
 #include <linux/ti_wilink_st.h>
 #include <linux/usb/musb.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/nop-usb-xceiv.h>
 #include <linux/wl12xx.h>
 #include <linux/platform_data/omap-abe-twl6040.h>
 
@@ -106,6 +108,7 @@ static struct omap_abe_twl6040_data panda_abe_audio_data = {
        .has_aux        = ABE_TWL6040_LEFT | ABE_TWL6040_RIGHT,
        /* PandaBoard: FM RX, PandaBoardES: audio in */
        .has_afm        = ABE_TWL6040_LEFT | ABE_TWL6040_RIGHT,
+       .has_abe        = 1,
        /* No jack detection. */
        .jack_detection = 0,
        /* MCLK input is 38.4MHz */
@@ -121,9 +124,9 @@ static struct platform_device panda_abe_audio = {
        },
 };
 
-static struct platform_device panda_hdmi_audio_codec = {
-       .name   = "hdmi-audio-codec",
-       .id     = -1,
+static struct platform_device panda_spdif_dit_codec = {
+       .name           = "spdif-dit",
+       .id             = -1,
 };
 
 static struct platform_device btwilink_device = {
@@ -131,59 +134,112 @@ static struct platform_device btwilink_device = {
        .id     = -1,
 };
 
+/* PHY device on HS USB Port 1 i.e. nop_usb_xceiv.1 */
+static struct nop_usb_xceiv_platform_data hsusb1_phy_data = {
+       /* FREF_CLK3 provides the 19.2 MHz reference clock to the PHY */
+       .clk_rate = 19200000,
+};
+
+static struct platform_device hsusb1_phy_device = {
+       .name   = "nop_usb_xceiv",
+       .id     = 1,
+       .dev    = {
+               .platform_data = &hsusb1_phy_data,
+       },
+};
+
+/* Regulator for USB HUB/PHY reset */
+static struct regulator_consumer_supply hsusb1_reset_supplies[] = {
+/* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.1"),
+};
+
+static struct regulator_init_data hsusb1_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies      = hsusb1_reset_supplies,
+       .num_consumer_supplies  = ARRAY_SIZE(hsusb1_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb1_reset_config = {
+       .supply_name    = "hsusb1_reset",
+       .microvolts = 3300000,
+       .gpio = GPIO_HUB_NRESET,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb1_reset_data,
+};
+
+static struct platform_device hsusb1_reset_device = {
+       .name   = "reg-fixed-voltage",
+       .id     = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb1_reset_config,
+       },
+};
+
+/* Regulator for USB HUB supply */
+static struct regulator_consumer_supply hsusb1_power_supplies[] = {
+/* Link PHY device to USB HUB supply so it gets enabled in the PHY driver */
+       REGULATOR_SUPPLY("vcc", "nop_usb_xceiv.1"),
+};
+
+static struct regulator_init_data hsusb1_power_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies      = hsusb1_power_supplies,
+       .num_consumer_supplies  = ARRAY_SIZE(hsusb1_power_supplies),
+};
+
+static struct fixed_voltage_config hsusb1_power_config = {
+       .supply_name    = "hsusb1_vbus",
+       .microvolts = 3300000,
+       .gpio = GPIO_HUB_POWER,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,
+       .init_data = &hsusb1_power_data,
+};
+
+static struct platform_device hsusb1_power_device = {
+       .name   = "reg-fixed-voltage",
+       .id     = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb1_power_config,
+       },
+};
+
 static struct platform_device *panda_devices[] __initdata = {
        &leds_gpio,
        &wl1271_device,
        &panda_abe_audio,
-       &panda_hdmi_audio_codec,
+       &panda_spdif_dit_codec,
        &btwilink_device,
+       &hsusb1_phy_device,
+       &hsusb1_power_device,
+       &hsusb1_reset_device,
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
        .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
-       .phy_reset  = false,
-       .reset_gpio_port[0]  = -EINVAL,
-       .reset_gpio_port[1]  = -EINVAL,
-       .reset_gpio_port[2]  = -EINVAL
-};
-
-static struct gpio panda_ehci_gpios[] __initdata = {
-       { GPIO_HUB_POWER,       GPIOF_OUT_INIT_LOW,  "hub_power"  },
-       { GPIO_HUB_NRESET,      GPIOF_OUT_INIT_LOW,  "hub_nreset" },
 };
 
 static void __init omap4_ehci_init(void)
 {
        int ret;
-       struct clk *phy_ref_clk;
 
        /* FREF_CLK3 provides the 19.2 MHz reference clock to the PHY */
-       phy_ref_clk = clk_get(NULL, "auxclk3_ck");
-       if (IS_ERR(phy_ref_clk)) {
-               pr_err("Cannot request auxclk3\n");
-               return;
-       }
-       clk_set_rate(phy_ref_clk, 19200000);
-       clk_prepare_enable(phy_ref_clk);
-
-       /* disable the power to the usb hub prior to init and reset phy+hub */
-       ret = gpio_request_array(panda_ehci_gpios,
-                                ARRAY_SIZE(panda_ehci_gpios));
-       if (ret) {
-               pr_err("Unable to initialize EHCI power/reset\n");
-               return;
-       }
+       ret = clk_add_alias("main_clk", "nop_usb_xceiv.1", "auxclk3_ck", NULL);
+       if (ret)
+               pr_err("Failed to add main_clk alias to auxclk3_ck\n");
 
-       gpio_export(GPIO_HUB_POWER, 0);
-       gpio_export(GPIO_HUB_NRESET, 0);
-       gpio_set_value(GPIO_HUB_NRESET, 1);
+       /* PHY on HS USB Port 1 i.e. index 0 */
+       usb_bind_phy("ehci-omap.0", 0, "nop_usb_xceiv.1");
 
        usbhs_init(&usbhs_bdata);
-
-       /* enable power to hub */
-       gpio_set_value(GPIO_HUB_POWER, 1);
 }
 
 static struct omap_musb_board_data musb_board_data = {
@@ -447,6 +503,7 @@ static void __init omap4_panda_init(void)
        omap_sdrc_init(NULL, NULL);
        omap4_twl6030_hsmmc_init(mmc);
        omap4_ehci_init();
+       usb_bind_phy("musb-hdrc.2.auto", 0, "omap-usb2.3.auto");
        usb_musb_init(&musb_board_data);
        omap4_panda_display_init();
 }
index c8fde3e5644138088721040a465b55de4094313c..2594cef890240653d7ddc6872ac1ed3c372d277c 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/regulator/machine.h>
 #include <linux/regulator/fixed.h>
 #include <linux/spi/spi.h>
+#include <linux/usb/phy.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -457,14 +458,51 @@ static int __init overo_spi_init(void)
        return 0;
 }
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
-       .port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = OVERO_GPIO_USBH_NRESET,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
+static struct platform_device *overo_devices[] __initdata = {
+       &hsusb2_phy_device,
+       &hsusb2_reset_device,
+};
+
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
        .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
-       .phy_reset  = true,
-       .reset_gpio_port[0]  = -EINVAL,
-       .reset_gpio_port[1]  = OVERO_GPIO_USBH_NRESET,
-       .reset_gpio_port[2]  = -EINVAL
 };
 
 #ifdef CONFIG_OMAP_MUX
@@ -499,7 +537,13 @@ static void __init overo_init(void)
                                  mt46h32m32lf6_sdrc_params);
        board_nand_init(overo_nand_partitions,
                        ARRAY_SIZE(overo_nand_partitions), NAND_CS, 0, NULL);
+       platform_add_devices(overo_devices, ARRAY_SIZE(overo_devices));
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
+
+       /* PHY on HSUSB Port 2 i.e. index 1 */
+       usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
+
        usbhs_init(&usbhs_bdata);
        overo_spi_init();
        overo_init_smsc911x();
index 0c777b75e484dcc3cc291a4dd5cab058178f8941..f8a272c253f56b0a5083d97cc079f9df7c1602f9 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/regulator/machine.h>
 #include <linux/regulator/consumer.h>
 #include <linux/platform_data/mtd-onenand-omap2.h>
+#include <linux/usb/phy.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach-types.h>
@@ -134,6 +135,7 @@ static void __init rm680_init(void)
        sdrc_params = nokia_get_sdram_timings();
        omap_sdrc_init(sdrc_params, sdrc_params);
 
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
        rm680_peripherals_init();
 }
index 1c7c834a5b5f5fb819dc56b6c8bc01edfc62f4d8..8cef477d6b006ac81e890e68c9965abecb2d8a97 100644 (file)
@@ -49,13 +49,13 @@ static void zoom_panel_disable_lcd(struct omap_dss_device *dssdev)
 {
 }
 
-/*
- * PWMA/B register offsets (TWL4030_MODULE_PWMA)
- */
+/* Register offsets in TWL4030_MODULE_INTBR */
 #define TWL_INTBR_PMBR1        0xD
 #define TWL_INTBR_GPBR1        0xC
-#define TWL_LED_PWMON  0x0
-#define TWL_LED_PWMOFF 0x1
+
+/* Register offsets in TWL_MODULE_PWM */
+#define TWL_LED_PWMON  0x3
+#define TWL_LED_PWMOFF 0x4
 
 static int zoom_set_bl_intensity(struct omap_dss_device *dssdev, int level)
 {
@@ -93,8 +93,8 @@ static int zoom_set_bl_intensity(struct omap_dss_device *dssdev, int level)
        }
 
        c = ((50 * (100 - level)) / 100) + 1;
-       twl_i2c_write_u8(TWL4030_MODULE_PWM1, 0x7F, TWL_LED_PWMOFF);
-       twl_i2c_write_u8(TWL4030_MODULE_PWM1, c, TWL_LED_PWMON);
+       twl_i2c_write_u8(TWL_MODULE_PWM, 0x7F, TWL_LED_PWMOFF);
+       twl_i2c_write_u8(TWL_MODULE_PWM, c, TWL_LED_PWMON);
 #else
        pr_warn("Backlight not enabled\n");
 #endif
index 26e07addc9d72b1c5540c1020043d171fe00a211..dc5498b1b3a753d9bf79a9e2b51f52df92feeb6b 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/wl12xx.h>
 #include <linux/mmc/host.h>
 #include <linux/platform_data/gpio-omap.h>
+#include <linux/usb/phy.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -298,6 +299,7 @@ void __init zoom_peripherals_init(void)
        omap_hsmmc_init(mmc);
        omap_i2c_init();
        platform_device_register(&omap_vwlan_device);
+       usb_bind_phy("musb-hdrc.0.auto", 0, "twl4030_usb");
        usb_musb_init(NULL);
        enable_board_wakeup_source();
        omap_serial_init();
index d7fa31e672387043aedeaaa872e499d1219daa27..d258632ea6e2a6e7c9375252c131fe3518454125 100644 (file)
@@ -17,6 +17,9 @@
 #include <linux/gpio.h>
 #include <linux/i2c/twl.h>
 #include <linux/mtd/nand.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/usb/phy.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -92,14 +95,51 @@ static struct mtd_partition zoom_nand_partitions[] = {
        },
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
-       .port_mode[0]           = OMAP_USBHS_PORT_MODE_UNUSED,
+/* PHY device on HS USB Port 2 i.e. nop_usb_xceiv.2 */
+static struct platform_device hsusb2_phy_device = {
+       .name = "nop_usb_xceiv",
+       .id = 2,
+};
+
+/* Regulator for HS USB Port 2 PHY reset */
+static struct regulator_consumer_supply hsusb2_reset_supplies[] = {
+       /* Link PHY device to reset supply so it gets used in the PHY driver */
+       REGULATOR_SUPPLY("reset", "nop_usb_xceiv.2"),
+};
+
+static struct regulator_init_data hsusb2_reset_data = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .consumer_supplies = hsusb2_reset_supplies,
+       .num_consumer_supplies = ARRAY_SIZE(hsusb2_reset_supplies),
+};
+
+static struct fixed_voltage_config hsusb2_reset_config = {
+       .supply_name = "hsusb2_reset",
+       .microvolts = 3300000,
+       .gpio = ZOOM3_EHCI_RESET_GPIO,
+       .startup_delay = 70000, /* 70msec */
+       .enable_high = 1,
+       .enabled_at_boot = 0,   /* keep in RESET */
+       .init_data = &hsusb2_reset_data,
+};
+
+static struct platform_device hsusb2_reset_device = {
+       .name = "reg-fixed-voltage",
+       .id = PLATFORM_DEVID_AUTO,
+       .dev = {
+               .platform_data = &hsusb2_reset_config,
+       },
+};
+
+static struct platform_device *zoom3_devices[] __initdata = {
+       &hsusb2_phy_device,
+       &hsusb2_reset_device,
+};
+
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
        .port_mode[1]           = OMAP_EHCI_PORT_MODE_PHY,
-       .port_mode[2]           = OMAP_USBHS_PORT_MODE_UNUSED,
-       .phy_reset              = true,
-       .reset_gpio_port[0]     = -EINVAL,
-       .reset_gpio_port[1]     = ZOOM3_EHCI_RESET_GPIO,
-       .reset_gpio_port[2]     = -EINVAL,
 };
 
 static void __init omap_zoom_init(void)
@@ -109,6 +149,10 @@ static void __init omap_zoom_init(void)
        } else if (machine_is_omap_zoom3()) {
                omap3_mux_init(board_mux, OMAP_PACKAGE_CBP);
                omap_mux_init_gpio(ZOOM3_EHCI_RESET_GPIO, OMAP_PIN_OUTPUT);
+
+               platform_add_devices(zoom3_devices, ARRAY_SIZE(zoom3_devices));
+               /* PHY on HSUSB Port 2 i.e. index 1 */
+               usb_bind_phy("ehci-omap.0", 1, "nop_usb_xceiv.2");
                usbhs_init(&usbhs_bdata);
        }
 
index eb3dab68d5362b4b6340de4fccca2b5fd8fff825..89f6ed81ca14e53a097e617c1d0b67ded8e1624c 100644 (file)
@@ -1992,9 +1992,11 @@ static struct omap_clk omap2430_clks[] = {
        CLK(NULL,       "sdrc_ick",     &sdrc_ick,      CK_243X),
        CLK(NULL,       "des_ick",      &des_ick,       CK_243X),
        CLK("omap-sham",        "ick",  &sha_ick,       CK_243X),
+       CLK(NULL,       "sha_ick",      &sha_ick,       CK_243X),
        CLK("omap_rng", "ick",          &rng_ick,       CK_243X),
        CLK(NULL,       "rng_ick",      &rng_ick,       CK_243X),
-       CLK("omap-aes", "ick",  &aes_ick,       CK_243X),
+       CLK("omap-aes", "ick",          &aes_ick,       CK_243X),
+       CLK(NULL,       "aes_ick",      &aes_ick,       CK_243X),
        CLK(NULL,       "pka_ick",      &pka_ick,       CK_243X),
        CLK(NULL,       "usb_fck",      &usb_fck,       CK_243X),
        CLK("musb-omap2430",    "ick",  &usbhs_ick,     CK_243X),
index ea64ad60675999e98927574432dc816db82b3392..e674e0106ebf0cc953a0e69176a8e2abd73efb21 100644 (file)
@@ -412,6 +412,14 @@ static struct clk smartreflex1_fck;
 DEFINE_STRUCT_CLK_HW_OMAP(smartreflex1_fck, NULL);
 DEFINE_STRUCT_CLK(smartreflex1_fck, dpll_core_ck_parents, clk_ops_null);
 
+static struct clk sha0_fck;
+DEFINE_STRUCT_CLK_HW_OMAP(sha0_fck, NULL);
+DEFINE_STRUCT_CLK(sha0_fck, dpll_core_ck_parents, clk_ops_null);
+
+static struct clk aes0_fck;
+DEFINE_STRUCT_CLK_HW_OMAP(aes0_fck, NULL);
+DEFINE_STRUCT_CLK(aes0_fck, dpll_core_ck_parents, clk_ops_null);
+
 /*
  * Modules clock nodes
  *
@@ -422,15 +430,11 @@ DEFINE_STRUCT_CLK(smartreflex1_fck, dpll_core_ck_parents, clk_ops_null);
  *  - Driver code is not yet migrated to use hwmod/runtime pm
  *  - Modules outside kernel access (to disable them by default)
  *
- *     - debugss
  *     - mmu (gfx domain)
  *     - cefuse
  *     - usbotg_fck (its additional clock and not really a modulemode)
  *     - ieee5000
  */
-DEFINE_CLK_GATE(debugss_ick, "dpll_core_m4_ck", &dpll_core_m4_ck, 0x0,
-               AM33XX_CM_WKUP_DEBUGSS_CLKCTRL, AM33XX_MODULEMODE_SWCTRL_SHIFT,
-               0x0, NULL);
 
 DEFINE_CLK_GATE(mmu_fck, "dpll_core_m4_ck", &dpll_core_m4_ck, 0x0,
                AM33XX_CM_GFX_MMUDATA_CLKCTRL, AM33XX_MODULEMODE_SWCTRL_SHIFT,
@@ -832,6 +836,42 @@ static struct clk_hw_omap wdt1_fck_hw = {
 
 DEFINE_STRUCT_CLK(wdt1_fck, wdt_ck_parents, gpio_fck_ops);
 
+/*
+ * debugss optional clocks
+ */
+DEFINE_CLK_GATE(dbg_sysclk_ck, "sys_clkin_ck", &sys_clkin_ck,
+               0x0, AM33XX_CM_WKUP_DEBUGSS_CLKCTRL,
+               AM33XX_OPTFCLKEN_DBGSYSCLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(dbg_clka_ck, "dpll_core_m4_ck", &dpll_core_m4_ck,
+               0x0, AM33XX_CM_WKUP_DEBUGSS_CLKCTRL,
+               AM33XX_OPTCLK_DEBUG_CLKA_SHIFT, 0x0, NULL);
+
+static const char *stm_pmd_clock_mux_ck_parents[] = {
+       "dbg_sysclk_ck", "dbg_clka_ck",
+};
+
+DEFINE_CLK_MUX(stm_pmd_clock_mux_ck, stm_pmd_clock_mux_ck_parents, NULL, 0x0,
+              AM33XX_CM_WKUP_DEBUGSS_CLKCTRL, AM33XX_STM_PMD_CLKSEL_SHIFT,
+              AM33XX_STM_PMD_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(trace_pmd_clk_mux_ck, stm_pmd_clock_mux_ck_parents, NULL, 0x0,
+              AM33XX_CM_WKUP_DEBUGSS_CLKCTRL,
+              AM33XX_TRC_PMD_CLKSEL_SHIFT,
+              AM33XX_TRC_PMD_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_DIVIDER(stm_clk_div_ck, "stm_pmd_clock_mux_ck",
+                  &stm_pmd_clock_mux_ck, 0x0, AM33XX_CM_WKUP_DEBUGSS_CLKCTRL,
+                  AM33XX_STM_PMD_CLKDIVSEL_SHIFT,
+                  AM33XX_STM_PMD_CLKDIVSEL_WIDTH, CLK_DIVIDER_POWER_OF_TWO,
+                  NULL);
+
+DEFINE_CLK_DIVIDER(trace_clk_div_ck, "trace_pmd_clk_mux_ck",
+                  &trace_pmd_clk_mux_ck, 0x0, AM33XX_CM_WKUP_DEBUGSS_CLKCTRL,
+                  AM33XX_TRC_PMD_CLKDIVSEL_SHIFT,
+                  AM33XX_TRC_PMD_CLKDIVSEL_WIDTH, CLK_DIVIDER_POWER_OF_TWO,
+                  NULL);
+
 /*
  * clkdev
  */
@@ -869,13 +909,14 @@ static struct omap_clk am33xx_clks[] = {
        CLK("481cc000.d_can",   NULL,           &dcan0_fck,     CK_AM33XX),
        CLK(NULL,       "dcan1_fck",            &dcan1_fck,     CK_AM33XX),
        CLK("481d0000.d_can",   NULL,           &dcan1_fck,     CK_AM33XX),
-       CLK(NULL,       "debugss_ick",          &debugss_ick,   CK_AM33XX),
        CLK(NULL,       "pruss_ocp_gclk",       &pruss_ocp_gclk,        CK_AM33XX),
        CLK(NULL,       "mcasp0_fck",           &mcasp0_fck,    CK_AM33XX),
        CLK(NULL,       "mcasp1_fck",           &mcasp1_fck,    CK_AM33XX),
        CLK(NULL,       "mmu_fck",              &mmu_fck,       CK_AM33XX),
        CLK(NULL,       "smartreflex0_fck",     &smartreflex0_fck,      CK_AM33XX),
        CLK(NULL,       "smartreflex1_fck",     &smartreflex1_fck,      CK_AM33XX),
+       CLK(NULL,       "sha0_fck",             &sha0_fck,      CK_AM33XX),
+       CLK(NULL,       "aes0_fck",             &aes0_fck,      CK_AM33XX),
        CLK(NULL,       "timer1_fck",           &timer1_fck,    CK_AM33XX),
        CLK(NULL,       "timer2_fck",           &timer2_fck,    CK_AM33XX),
        CLK(NULL,       "timer3_fck",           &timer3_fck,    CK_AM33XX),
@@ -910,6 +951,12 @@ static struct omap_clk am33xx_clks[] = {
        CLK(NULL,       "clkout2_div_ck",       &clkout2_div_ck,        CK_AM33XX),
        CLK(NULL,       "timer_32k_ck",         &clkdiv32k_ick, CK_AM33XX),
        CLK(NULL,       "timer_sys_ck",         &sys_clkin_ck,  CK_AM33XX),
+       CLK(NULL,       "dbg_sysclk_ck",        &dbg_sysclk_ck, CK_AM33XX),
+       CLK(NULL,       "dbg_clka_ck",          &dbg_clka_ck,   CK_AM33XX),
+       CLK(NULL,       "stm_pmd_clock_mux_ck", &stm_pmd_clock_mux_ck,  CK_AM33XX),
+       CLK(NULL,       "trace_pmd_clk_mux_ck", &trace_pmd_clk_mux_ck,  CK_AM33XX),
+       CLK(NULL,       "stm_clk_div_ck",       &stm_clk_div_ck,        CK_AM33XX),
+       CLK(NULL,       "trace_clk_div_ck",     &trace_clk_div_ck,      CK_AM33XX),
 };
 
 
@@ -956,6 +1003,14 @@ int __init am33xx_clk_init(void)
 
        clk_set_parent(&timer3_fck, &sys_clkin_ck);
        clk_set_parent(&timer6_fck, &sys_clkin_ck);
+       /*
+        * The On-Chip 32K RC Osc clock is not an accurate clock-source as per
+        * the design/spec, so as a result, for example, timer which supposed
+        * to get expired @60Sec, but will expire somewhere ~@40Sec, which is
+        * not expected by any use-case, so change WDT1 clock source to PRCM
+        * 32KHz clock.
+        */
+       clk_set_parent(&wdt1_fck, &clkdiv32k_ick);
 
        return 0;
 }
index 6ef87580c33f6772a1fed5445b1bb2344fb96a6d..6cbd8011a1f81e2478d603ac0e61158260a71426 100644 (file)
@@ -3334,8 +3334,10 @@ static struct omap_clk omap3xxx_clks[] = {
        CLK("omap_hsmmc.2",     "ick",  &mmchs3_ick,    CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
        CLK(NULL,       "mmchs3_ick",   &mmchs3_ick,    CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
        CLK(NULL,       "icr_ick",      &icr_ick,       CK_34XX | CK_36XX),
-       CLK("omap-aes", "ick",  &aes2_ick,      CK_34XX | CK_36XX),
+       CLK("omap-aes", "ick",          &aes2_ick,      CK_34XX | CK_36XX),
+       CLK(NULL,       "aes2_ick",     &aes2_ick,      CK_34XX | CK_36XX),
        CLK("omap-sham",        "ick",  &sha12_ick,     CK_34XX | CK_36XX),
+       CLK(NULL,       "sha12_ick",    &sha12_ick,     CK_34XX | CK_36XX),
        CLK(NULL,       "des2_ick",     &des2_ick,      CK_34XX | CK_36XX),
        CLK("omap_hsmmc.1",     "ick",  &mmchs2_ick,    CK_3XXX),
        CLK("omap_hsmmc.0",     "ick",  &mmchs1_ick,    CK_3XXX),
diff --git a/arch/arm/mach-omap2/cclock54xx_data.c b/arch/arm/mach-omap2/cclock54xx_data.c
new file mode 100644 (file)
index 0000000..95d8e39
--- /dev/null
@@ -0,0 +1,1514 @@
+/*
+ * OMAP54xx Clock data
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ *
+ * Paul Walmsley (paul@pwsan.com)
+ * Rajendra Nayak (rnayak@ti.com)
+ * Benoit Cousson (b-cousson@ti.com)
+ * Mike Turquette (mturquette@ti.com)
+ *
+ * This file is automatically generated from the OMAP hardware databases.
+ * We respectfully ask that any modifications to this file be coordinated
+ * with the public linux-omap@vger.kernel.org mailing list and the
+ * authors above to ensure that the autogeneration scripts are kept
+ * up-to-date with the file contents.
+ *
+ * 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.
+ *
+ * XXX Some of the ES1 clocks have been removed/changed; once support
+ * is added for discriminating clocks by ES level, these should be added back
+ * in.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/clk-private.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+
+#include "soc.h"
+#include "iomap.h"
+#include "clock.h"
+#include "clock54xx.h"
+#include "cm1_54xx.h"
+#include "cm2_54xx.h"
+#include "cm-regbits-54xx.h"
+#include "prm54xx.h"
+#include "prm-regbits-54xx.h"
+#include "control.h"
+#include "scrm54xx.h"
+
+/* OMAP4 modulemode control */
+#define OMAP54XX_MODULEMODE_HWCTRL             0
+#define OMAP54XX_MODULEMODE_SWCTRL             1
+
+/*
+ * OMAP5 ABE DPLL default frequency. In OMAP5430 ES2.0 TRM version R, section
+ * "3.6.3.2.3 CKGEN_ABE Clock Generator" states that the "DPLL_ABE_X2_CLK
+ * must be set to 196.608 MHz" and hence, the DPLL locked frequency is
+ * half of this value.
+ */
+#define OMAP5_DPLL_ABE_DEFFREQ                         98304000
+
+/*
+ * OMAP543x TRM, section "3.6.3.9.5 DPLL_USB Preferred Settings"
+ * states it must be at 960MHz
+ */
+#define OMAP5_DPLL_USB_DEFFREQ                         960000000
+
+/* Root clocks */
+
+DEFINE_CLK_FIXED_RATE(pad_clks_src_ck, CLK_IS_ROOT, 12000000, 0x0);
+
+DEFINE_CLK_GATE(pad_clks_ck, "pad_clks_src_ck", &pad_clks_src_ck, 0x0,
+               OMAP54XX_CM_CLKSEL_ABE, OMAP54XX_PAD_CLKS_GATE_SHIFT, 0x0,
+               NULL);
+
+DEFINE_CLK_FIXED_RATE(secure_32k_clk_src_ck, CLK_IS_ROOT, 32768, 0x0);
+
+DEFINE_CLK_FIXED_RATE(slimbus_src_clk, CLK_IS_ROOT, 12000000, 0x0);
+
+DEFINE_CLK_GATE(slimbus_clk, "slimbus_src_clk", &slimbus_src_clk, 0x0,
+               OMAP54XX_CM_CLKSEL_ABE, OMAP54XX_SLIMBUS1_CLK_GATE_SHIFT, 0x0,
+               NULL);
+
+DEFINE_CLK_FIXED_RATE(sys_32k_ck, CLK_IS_ROOT, 32768, 0x0);
+
+DEFINE_CLK_FIXED_RATE(virt_12000000_ck, CLK_IS_ROOT, 12000000, 0x0);
+
+DEFINE_CLK_FIXED_RATE(virt_13000000_ck, CLK_IS_ROOT, 13000000, 0x0);
+
+DEFINE_CLK_FIXED_RATE(virt_16800000_ck, CLK_IS_ROOT, 16800000, 0x0);
+
+DEFINE_CLK_FIXED_RATE(virt_19200000_ck, CLK_IS_ROOT, 19200000, 0x0);
+
+DEFINE_CLK_FIXED_RATE(virt_26000000_ck, CLK_IS_ROOT, 26000000, 0x0);
+
+DEFINE_CLK_FIXED_RATE(virt_27000000_ck, CLK_IS_ROOT, 27000000, 0x0);
+
+DEFINE_CLK_FIXED_RATE(virt_38400000_ck, CLK_IS_ROOT, 38400000, 0x0);
+
+
+static const char *sys_clkin_parents[] = {
+       "virt_12000000_ck", "virt_13000000_ck", "virt_16800000_ck",
+       "virt_19200000_ck", "virt_26000000_ck", "virt_27000000_ck",
+       "virt_38400000_ck",
+};
+
+DEFINE_CLK_MUX(sys_clkin, sys_clkin_parents, NULL, 0x0, OMAP54XX_CM_CLKSEL_SYS,
+              OMAP54XX_SYS_CLKSEL_SHIFT, OMAP54XX_SYS_CLKSEL_WIDTH,
+              CLK_MUX_INDEX_ONE, NULL);
+
+DEFINE_CLK_FIXED_RATE(xclk60mhsp1_ck, CLK_IS_ROOT, 60000000, 0x0);
+
+DEFINE_CLK_FIXED_RATE(xclk60mhsp2_ck, CLK_IS_ROOT, 60000000, 0x0);
+
+/* Module clocks and DPLL outputs */
+
+static const char *abe_dpll_bypass_clk_mux_parents[] = {
+       "sys_clkin", "sys_32k_ck",
+};
+
+DEFINE_CLK_MUX(abe_dpll_bypass_clk_mux, abe_dpll_bypass_clk_mux_parents, NULL,
+              0x0, OMAP54XX_CM_CLKSEL_WKUPAON, OMAP54XX_CLKSEL_0_0_SHIFT,
+              OMAP54XX_CLKSEL_0_0_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(abe_dpll_clk_mux, abe_dpll_bypass_clk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_CLKSEL_ABE_PLL_REF, OMAP54XX_CLKSEL_0_0_SHIFT,
+              OMAP54XX_CLKSEL_0_0_WIDTH, 0x0, NULL);
+
+/* DPLL_ABE */
+static struct dpll_data dpll_abe_dd = {
+       .mult_div1_reg  = OMAP54XX_CM_CLKSEL_DPLL_ABE,
+       .clk_bypass     = &abe_dpll_bypass_clk_mux,
+       .clk_ref        = &abe_dpll_clk_mux,
+       .control_reg    = OMAP54XX_CM_CLKMODE_DPLL_ABE,
+       .modes          = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
+       .autoidle_reg   = OMAP54XX_CM_AUTOIDLE_DPLL_ABE,
+       .idlest_reg     = OMAP54XX_CM_IDLEST_DPLL_ABE,
+       .mult_mask      = OMAP54XX_DPLL_MULT_MASK,
+       .div1_mask      = OMAP54XX_DPLL_DIV_MASK,
+       .enable_mask    = OMAP54XX_DPLL_EN_MASK,
+       .autoidle_mask  = OMAP54XX_AUTO_DPLL_MODE_MASK,
+       .idlest_mask    = OMAP54XX_ST_DPLL_CLK_MASK,
+       .m4xen_mask     = OMAP54XX_DPLL_REGM4XEN_MASK,
+       .lpmode_mask    = OMAP54XX_DPLL_LPMODE_EN_MASK,
+       .max_multiplier = 2047,
+       .max_divider    = 128,
+       .min_divider    = 1,
+};
+
+static const char *dpll_abe_ck_parents[] = {
+       "abe_dpll_clk_mux", "abe_dpll_bypass_clk_mux"
+};
+
+static struct clk dpll_abe_ck;
+
+static const struct clk_ops dpll_abe_ck_ops = {
+       .enable         = &omap3_noncore_dpll_enable,
+       .disable        = &omap3_noncore_dpll_disable,
+       .recalc_rate    = &omap4_dpll_regm4xen_recalc,
+       .round_rate     = &omap4_dpll_regm4xen_round_rate,
+       .set_rate       = &omap3_noncore_dpll_set_rate,
+       .get_parent     = &omap2_init_dpll_parent,
+};
+
+static struct clk_hw_omap dpll_abe_ck_hw = {
+       .hw = {
+               .clk = &dpll_abe_ck,
+       },
+       .dpll_data      = &dpll_abe_dd,
+       .ops            = &clkhwops_omap3_dpll,
+};
+
+DEFINE_STRUCT_CLK(dpll_abe_ck, dpll_abe_ck_parents, dpll_abe_ck_ops);
+
+static const char *dpll_abe_x2_ck_parents[] = {
+       "dpll_abe_ck",
+};
+
+static struct clk dpll_abe_x2_ck;
+
+static const struct clk_ops dpll_abe_x2_ck_ops = {
+       .recalc_rate    = &omap3_clkoutx2_recalc,
+};
+
+static struct clk_hw_omap dpll_abe_x2_ck_hw = {
+       .hw = {
+               .clk = &dpll_abe_x2_ck,
+       },
+};
+
+DEFINE_STRUCT_CLK(dpll_abe_x2_ck, dpll_abe_x2_ck_parents, dpll_abe_x2_ck_ops);
+
+static const struct clk_ops omap_hsdivider_ops = {
+       .set_rate       = &omap2_clksel_set_rate,
+       .recalc_rate    = &omap2_clksel_recalc,
+       .round_rate     = &omap2_clksel_round_rate,
+};
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_abe_m2x2_ck, "dpll_abe_x2_ck", &dpll_abe_x2_ck,
+                           0x0, OMAP54XX_CM_DIV_M2_DPLL_ABE,
+                           OMAP54XX_DIVHS_0_4_MASK);
+
+DEFINE_CLK_FIXED_FACTOR(abe_24m_fclk, "dpll_abe_m2x2_ck", &dpll_abe_m2x2_ck,
+                       0x0, 1, 8);
+
+DEFINE_CLK_DIVIDER(abe_clk, "dpll_abe_m2x2_ck", &dpll_abe_m2x2_ck, 0x0,
+                  OMAP54XX_CM_CLKSEL_ABE, OMAP54XX_CLKSEL_OPP_SHIFT,
+                  OMAP54XX_CLKSEL_OPP_WIDTH, CLK_DIVIDER_POWER_OF_TWO, NULL);
+
+DEFINE_CLK_FIXED_FACTOR(abe_iclk, "abe_clk", &abe_clk, 0x0, 1, 2);
+
+DEFINE_CLK_FIXED_FACTOR(abe_lp_clk_div, "dpll_abe_m2x2_ck", &dpll_abe_m2x2_ck,
+                       0x0, 1, 16);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_abe_m3x2_ck, "dpll_abe_x2_ck", &dpll_abe_x2_ck,
+                           0x0, OMAP54XX_CM_DIV_M3_DPLL_ABE,
+                           OMAP54XX_DIVHS_0_4_MASK);
+
+/* DPLL_CORE */
+static struct dpll_data dpll_core_dd = {
+       .mult_div1_reg  = OMAP54XX_CM_CLKSEL_DPLL_CORE,
+       .clk_bypass     = &dpll_abe_m3x2_ck,
+       .clk_ref        = &sys_clkin,
+       .control_reg    = OMAP54XX_CM_CLKMODE_DPLL_CORE,
+       .modes          = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
+       .autoidle_reg   = OMAP54XX_CM_AUTOIDLE_DPLL_CORE,
+       .idlest_reg     = OMAP54XX_CM_IDLEST_DPLL_CORE,
+       .mult_mask      = OMAP54XX_DPLL_MULT_MASK,
+       .div1_mask      = OMAP54XX_DPLL_DIV_MASK,
+       .enable_mask    = OMAP54XX_DPLL_EN_MASK,
+       .autoidle_mask  = OMAP54XX_AUTO_DPLL_MODE_MASK,
+       .idlest_mask    = OMAP54XX_ST_DPLL_CLK_MASK,
+       .max_multiplier = 2047,
+       .max_divider    = 128,
+       .min_divider    = 1,
+};
+
+static const char *dpll_core_ck_parents[] = {
+       "sys_clkin", "dpll_abe_m3x2_ck"
+};
+
+static struct clk dpll_core_ck;
+
+static const struct clk_ops dpll_core_ck_ops = {
+       .recalc_rate    = &omap3_dpll_recalc,
+       .get_parent     = &omap2_init_dpll_parent,
+};
+
+static struct clk_hw_omap dpll_core_ck_hw = {
+       .hw = {
+               .clk = &dpll_core_ck,
+       },
+       .dpll_data      = &dpll_core_dd,
+       .ops            = &clkhwops_omap3_dpll,
+};
+
+DEFINE_STRUCT_CLK(dpll_core_ck, dpll_core_ck_parents, dpll_core_ck_ops);
+
+static const char *dpll_core_x2_ck_parents[] = {
+       "dpll_core_ck",
+};
+
+static struct clk dpll_core_x2_ck;
+
+static struct clk_hw_omap dpll_core_x2_ck_hw = {
+       .hw = {
+               .clk = &dpll_core_x2_ck,
+       },
+};
+
+DEFINE_STRUCT_CLK(dpll_core_x2_ck, dpll_core_x2_ck_parents, dpll_abe_x2_ck_ops);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_core_h21x2_ck, "dpll_core_x2_ck",
+                           &dpll_core_x2_ck, 0x0,
+                           OMAP54XX_CM_DIV_H21_DPLL_CORE, OMAP54XX_DIVHS_MASK);
+
+static const char *c2c_fclk_parents[] = {
+       "dpll_core_h21x2_ck",
+};
+
+static struct clk c2c_fclk;
+
+static const struct clk_ops c2c_fclk_ops = {
+};
+
+static struct clk_hw_omap c2c_fclk_hw = {
+       .hw = {
+               .clk = &c2c_fclk,
+       },
+};
+
+DEFINE_STRUCT_CLK(c2c_fclk, c2c_fclk_parents, c2c_fclk_ops);
+
+DEFINE_CLK_FIXED_FACTOR(c2c_iclk, "c2c_fclk", &c2c_fclk, 0x0, 1, 2);
+
+DEFINE_CLK_FIXED_FACTOR(custefuse_sys_gfclk_div, "sys_clkin", &sys_clkin, 0x0,
+                       1, 2);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_core_h11x2_ck, "dpll_core_x2_ck",
+                           &dpll_core_x2_ck, 0x0,
+                           OMAP54XX_CM_DIV_H11_DPLL_CORE, OMAP54XX_DIVHS_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_core_h12x2_ck, "dpll_core_x2_ck",
+                           &dpll_core_x2_ck, 0x0,
+                           OMAP54XX_CM_DIV_H12_DPLL_CORE, OMAP54XX_DIVHS_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_core_h13x2_ck, "dpll_core_x2_ck",
+                           &dpll_core_x2_ck, 0x0,
+                           OMAP54XX_CM_DIV_H13_DPLL_CORE, OMAP54XX_DIVHS_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_core_h14x2_ck, "dpll_core_x2_ck",
+                           &dpll_core_x2_ck, 0x0,
+                           OMAP54XX_CM_DIV_H14_DPLL_CORE, OMAP54XX_DIVHS_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_core_h22x2_ck, "dpll_core_x2_ck",
+                           &dpll_core_x2_ck, 0x0,
+                           OMAP54XX_CM_DIV_H22_DPLL_CORE, OMAP54XX_DIVHS_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_core_h23x2_ck, "dpll_core_x2_ck",
+                           &dpll_core_x2_ck, 0x0,
+                           OMAP54XX_CM_DIV_H23_DPLL_CORE, OMAP54XX_DIVHS_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_core_h24x2_ck, "dpll_core_x2_ck",
+                           &dpll_core_x2_ck, 0x0,
+                           OMAP54XX_CM_DIV_H24_DPLL_CORE, OMAP54XX_DIVHS_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_core_m2_ck, "dpll_core_ck", &dpll_core_ck, 0x0,
+                           OMAP54XX_CM_DIV_M2_DPLL_CORE,
+                           OMAP54XX_DIVHS_0_4_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_core_m3x2_ck, "dpll_core_x2_ck",
+                           &dpll_core_x2_ck, 0x0, OMAP54XX_CM_DIV_M3_DPLL_CORE,
+                           OMAP54XX_DIVHS_0_4_MASK);
+
+static const char *iva_dpll_hs_clk_div_parents[] = {
+       "dpll_core_h12x2_ck",
+};
+
+static struct clk iva_dpll_hs_clk_div;
+
+static struct clk_hw_omap iva_dpll_hs_clk_div_hw = {
+       .hw = {
+               .clk = &iva_dpll_hs_clk_div,
+       },
+};
+
+DEFINE_STRUCT_CLK(iva_dpll_hs_clk_div, iva_dpll_hs_clk_div_parents,
+                 c2c_fclk_ops);
+
+/* DPLL_IVA */
+static struct dpll_data dpll_iva_dd = {
+       .mult_div1_reg  = OMAP54XX_CM_CLKSEL_DPLL_IVA,
+       .clk_bypass     = &iva_dpll_hs_clk_div,
+       .clk_ref        = &sys_clkin,
+       .control_reg    = OMAP54XX_CM_CLKMODE_DPLL_IVA,
+       .modes          = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
+       .autoidle_reg   = OMAP54XX_CM_AUTOIDLE_DPLL_IVA,
+       .idlest_reg     = OMAP54XX_CM_IDLEST_DPLL_IVA,
+       .mult_mask      = OMAP54XX_DPLL_MULT_MASK,
+       .div1_mask      = OMAP54XX_DPLL_DIV_MASK,
+       .enable_mask    = OMAP54XX_DPLL_EN_MASK,
+       .autoidle_mask  = OMAP54XX_AUTO_DPLL_MODE_MASK,
+       .idlest_mask    = OMAP54XX_ST_DPLL_CLK_MASK,
+       .max_multiplier = 2047,
+       .max_divider    = 128,
+       .min_divider    = 1,
+};
+
+static const char *dpll_iva_ck_parents[] = {
+       "sys_clkin", "iva_dpll_hs_clk_div"
+};
+
+static struct clk dpll_iva_ck;
+
+static const struct clk_ops dpll_iva_ck_ops = {
+       .enable         = &omap3_noncore_dpll_enable,
+       .disable        = &omap3_noncore_dpll_disable,
+       .recalc_rate    = &omap3_dpll_recalc,
+       .round_rate     = &omap2_dpll_round_rate,
+       .set_rate       = &omap3_noncore_dpll_set_rate,
+       .get_parent     = &omap2_init_dpll_parent,
+};
+
+static struct clk_hw_omap dpll_iva_ck_hw = {
+       .hw = {
+               .clk = &dpll_iva_ck,
+       },
+       .dpll_data      = &dpll_iva_dd,
+       .ops            = &clkhwops_omap3_dpll,
+};
+
+DEFINE_STRUCT_CLK(dpll_iva_ck, dpll_iva_ck_parents, dpll_iva_ck_ops);
+
+static const char *dpll_iva_x2_ck_parents[] = {
+       "dpll_iva_ck",
+};
+
+static struct clk dpll_iva_x2_ck;
+
+static struct clk_hw_omap dpll_iva_x2_ck_hw = {
+       .hw = {
+               .clk = &dpll_iva_x2_ck,
+       },
+};
+
+DEFINE_STRUCT_CLK(dpll_iva_x2_ck, dpll_iva_x2_ck_parents, dpll_abe_x2_ck_ops);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_iva_h11x2_ck, "dpll_iva_x2_ck",
+                           &dpll_iva_x2_ck, 0x0, OMAP54XX_CM_DIV_H11_DPLL_IVA,
+                           OMAP54XX_DIVHS_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_iva_h12x2_ck, "dpll_iva_x2_ck",
+                           &dpll_iva_x2_ck, 0x0, OMAP54XX_CM_DIV_H12_DPLL_IVA,
+                           OMAP54XX_DIVHS_MASK);
+
+static struct clk mpu_dpll_hs_clk_div;
+
+static struct clk_hw_omap mpu_dpll_hs_clk_div_hw = {
+       .hw = {
+               .clk = &mpu_dpll_hs_clk_div,
+       },
+};
+
+DEFINE_STRUCT_CLK(mpu_dpll_hs_clk_div, iva_dpll_hs_clk_div_parents,
+                 c2c_fclk_ops);
+
+/* DPLL_MPU */
+static struct dpll_data dpll_mpu_dd = {
+       .mult_div1_reg  = OMAP54XX_CM_CLKSEL_DPLL_MPU,
+       .clk_bypass     = &mpu_dpll_hs_clk_div,
+       .clk_ref        = &sys_clkin,
+       .control_reg    = OMAP54XX_CM_CLKMODE_DPLL_MPU,
+       .modes          = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
+       .autoidle_reg   = OMAP54XX_CM_AUTOIDLE_DPLL_MPU,
+       .idlest_reg     = OMAP54XX_CM_IDLEST_DPLL_MPU,
+       .mult_mask      = OMAP54XX_DPLL_MULT_MASK,
+       .div1_mask      = OMAP54XX_DPLL_DIV_MASK,
+       .enable_mask    = OMAP54XX_DPLL_EN_MASK,
+       .autoidle_mask  = OMAP54XX_AUTO_DPLL_MODE_MASK,
+       .idlest_mask    = OMAP54XX_ST_DPLL_CLK_MASK,
+       .max_multiplier = 2047,
+       .max_divider    = 128,
+       .min_divider    = 1,
+};
+
+static const char *dpll_mpu_ck_parents[] = {
+       "sys_clkin", "mpu_dpll_hs_clk_div"
+};
+
+static struct clk dpll_mpu_ck;
+
+static struct clk_hw_omap dpll_mpu_ck_hw = {
+       .hw = {
+               .clk = &dpll_mpu_ck,
+       },
+       .dpll_data      = &dpll_mpu_dd,
+       .ops            = &clkhwops_omap3_dpll,
+};
+
+DEFINE_STRUCT_CLK(dpll_mpu_ck, dpll_mpu_ck_parents, dpll_iva_ck_ops);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_mpu_m2_ck, "dpll_mpu_ck", &dpll_mpu_ck, 0x0,
+                           OMAP54XX_CM_DIV_M2_DPLL_MPU,
+                           OMAP54XX_DIVHS_0_4_MASK);
+
+DEFINE_CLK_FIXED_FACTOR(per_dpll_hs_clk_div, "dpll_abe_m3x2_ck",
+                       &dpll_abe_m3x2_ck, 0x0, 1, 2);
+
+/* DPLL_PER */
+static struct dpll_data dpll_per_dd = {
+       .mult_div1_reg  = OMAP54XX_CM_CLKSEL_DPLL_PER,
+       .clk_bypass     = &per_dpll_hs_clk_div,
+       .clk_ref        = &sys_clkin,
+       .control_reg    = OMAP54XX_CM_CLKMODE_DPLL_PER,
+       .modes          = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
+       .autoidle_reg   = OMAP54XX_CM_AUTOIDLE_DPLL_PER,
+       .idlest_reg     = OMAP54XX_CM_IDLEST_DPLL_PER,
+       .mult_mask      = OMAP54XX_DPLL_MULT_MASK,
+       .div1_mask      = OMAP54XX_DPLL_DIV_MASK,
+       .enable_mask    = OMAP54XX_DPLL_EN_MASK,
+       .autoidle_mask  = OMAP54XX_AUTO_DPLL_MODE_MASK,
+       .idlest_mask    = OMAP54XX_ST_DPLL_CLK_MASK,
+       .max_multiplier = 2047,
+       .max_divider    = 128,
+       .min_divider    = 1,
+};
+
+static const char *dpll_per_ck_parents[] = {
+       "sys_clkin", "per_dpll_hs_clk_div"
+};
+
+static struct clk dpll_per_ck;
+
+static struct clk_hw_omap dpll_per_ck_hw = {
+       .hw = {
+               .clk = &dpll_per_ck,
+       },
+       .dpll_data      = &dpll_per_dd,
+       .ops            = &clkhwops_omap3_dpll,
+};
+
+DEFINE_STRUCT_CLK(dpll_per_ck, dpll_per_ck_parents, dpll_iva_ck_ops);
+
+static const char *dpll_per_x2_ck_parents[] = {
+       "dpll_per_ck",
+};
+
+static struct clk dpll_per_x2_ck;
+
+static struct clk_hw_omap dpll_per_x2_ck_hw = {
+       .hw = {
+               .clk = &dpll_per_x2_ck,
+       },
+};
+
+DEFINE_STRUCT_CLK(dpll_per_x2_ck, dpll_per_x2_ck_parents, dpll_abe_x2_ck_ops);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_per_h11x2_ck, "dpll_per_x2_ck",
+                           &dpll_per_x2_ck, 0x0, OMAP54XX_CM_DIV_H11_DPLL_PER,
+                           OMAP54XX_DIVHS_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_per_h12x2_ck, "dpll_per_x2_ck",
+                           &dpll_per_x2_ck, 0x0, OMAP54XX_CM_DIV_H12_DPLL_PER,
+                           OMAP54XX_DIVHS_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_per_h14x2_ck, "dpll_per_x2_ck",
+                           &dpll_per_x2_ck, 0x0, OMAP54XX_CM_DIV_H14_DPLL_PER,
+                           OMAP54XX_DIVHS_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_per_m2_ck, "dpll_per_ck", &dpll_per_ck, 0x0,
+                           OMAP54XX_CM_DIV_M2_DPLL_PER,
+                           OMAP54XX_DIVHS_0_4_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_per_m2x2_ck, "dpll_per_x2_ck", &dpll_per_x2_ck,
+                           0x0, OMAP54XX_CM_DIV_M2_DPLL_PER,
+                           OMAP54XX_DIVHS_0_4_MASK);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_per_m3x2_ck, "dpll_per_x2_ck", &dpll_per_x2_ck,
+                           0x0, OMAP54XX_CM_DIV_M3_DPLL_PER,
+                           OMAP54XX_DIVHS_0_4_MASK);
+
+/* DPLL_UNIPRO1 */
+static struct dpll_data dpll_unipro1_dd = {
+       .mult_div1_reg  = OMAP54XX_CM_CLKSEL_DPLL_UNIPRO1,
+       .clk_bypass     = &sys_clkin,
+       .clk_ref        = &sys_clkin,
+       .control_reg    = OMAP54XX_CM_CLKMODE_DPLL_UNIPRO1,
+       .modes          = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
+       .autoidle_reg   = OMAP54XX_CM_AUTOIDLE_DPLL_UNIPRO1,
+       .idlest_reg     = OMAP54XX_CM_IDLEST_DPLL_UNIPRO1,
+       .mult_mask      = OMAP54XX_DPLL_MULT_MASK,
+       .div1_mask      = OMAP54XX_DPLL_DIV_MASK,
+       .enable_mask    = OMAP54XX_DPLL_EN_MASK,
+       .autoidle_mask  = OMAP54XX_AUTO_DPLL_MODE_MASK,
+       .idlest_mask    = OMAP54XX_ST_DPLL_CLK_MASK,
+       .max_multiplier = 4095,
+       .max_divider    = 256,
+       .min_divider    = 1,
+};
+
+static const char *dpll_unipro1_ck_parents[] = {
+       "sys_clkin",
+};
+
+static struct clk dpll_unipro1_ck;
+
+static struct clk_hw_omap dpll_unipro1_ck_hw = {
+       .hw = {
+               .clk = &dpll_unipro1_ck,
+       },
+       .dpll_data      = &dpll_unipro1_dd,
+       .ops            = &clkhwops_omap3_dpll,
+};
+
+DEFINE_STRUCT_CLK(dpll_unipro1_ck, dpll_unipro1_ck_parents, dpll_iva_ck_ops);
+
+static const char *dpll_unipro1_clkdcoldo_parents[] = {
+       "dpll_unipro1_ck",
+};
+
+static struct clk dpll_unipro1_clkdcoldo;
+
+static struct clk_hw_omap dpll_unipro1_clkdcoldo_hw = {
+       .hw = {
+               .clk = &dpll_unipro1_clkdcoldo,
+       },
+       .clksel_reg     = OMAP54XX_CM_CLKDCOLDO_DPLL_UNIPRO1,
+};
+
+DEFINE_STRUCT_CLK(dpll_unipro1_clkdcoldo, dpll_unipro1_clkdcoldo_parents,
+                 c2c_fclk_ops);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_unipro1_m2_ck, "dpll_unipro1_ck",
+                           &dpll_unipro1_ck, 0x0,
+                           OMAP54XX_CM_DIV_M2_DPLL_UNIPRO1,
+                           OMAP54XX_DIVHS_0_6_MASK);
+
+/* DPLL_UNIPRO2 */
+static struct dpll_data dpll_unipro2_dd = {
+       .mult_div1_reg  = OMAP54XX_CM_CLKSEL_DPLL_UNIPRO2,
+       .clk_bypass     = &sys_clkin,
+       .clk_ref        = &sys_clkin,
+       .control_reg    = OMAP54XX_CM_CLKMODE_DPLL_UNIPRO2,
+       .modes          = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
+       .autoidle_reg   = OMAP54XX_CM_AUTOIDLE_DPLL_UNIPRO2,
+       .idlest_reg     = OMAP54XX_CM_IDLEST_DPLL_UNIPRO2,
+       .mult_mask      = OMAP54XX_DPLL_MULT_MASK,
+       .div1_mask      = OMAP54XX_DPLL_DIV_MASK,
+       .enable_mask    = OMAP54XX_DPLL_EN_MASK,
+       .autoidle_mask  = OMAP54XX_AUTO_DPLL_MODE_MASK,
+       .idlest_mask    = OMAP54XX_ST_DPLL_CLK_MASK,
+       .max_multiplier = 4095,
+       .max_divider    = 256,
+       .min_divider    = 1,
+};
+
+static struct clk dpll_unipro2_ck;
+
+static struct clk_hw_omap dpll_unipro2_ck_hw = {
+       .hw = {
+               .clk = &dpll_unipro2_ck,
+       },
+       .dpll_data      = &dpll_unipro2_dd,
+       .ops            = &clkhwops_omap3_dpll,
+};
+
+DEFINE_STRUCT_CLK(dpll_unipro2_ck, dpll_unipro1_ck_parents, dpll_iva_ck_ops);
+
+static const char *dpll_unipro2_clkdcoldo_parents[] = {
+       "dpll_unipro2_ck",
+};
+
+static struct clk dpll_unipro2_clkdcoldo;
+
+static struct clk_hw_omap dpll_unipro2_clkdcoldo_hw = {
+       .hw = {
+               .clk = &dpll_unipro2_clkdcoldo,
+       },
+       .clksel_reg     = OMAP54XX_CM_CLKDCOLDO_DPLL_UNIPRO2,
+};
+
+DEFINE_STRUCT_CLK(dpll_unipro2_clkdcoldo, dpll_unipro2_clkdcoldo_parents,
+                 c2c_fclk_ops);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_unipro2_m2_ck, "dpll_unipro2_ck",
+                           &dpll_unipro2_ck, 0x0,
+                           OMAP54XX_CM_DIV_M2_DPLL_UNIPRO2,
+                           OMAP54XX_DIVHS_0_6_MASK);
+
+DEFINE_CLK_FIXED_FACTOR(usb_dpll_hs_clk_div, "dpll_abe_m3x2_ck",
+                       &dpll_abe_m3x2_ck, 0x0, 1, 3);
+
+/* DPLL_USB */
+static struct dpll_data dpll_usb_dd = {
+       .mult_div1_reg  = OMAP54XX_CM_CLKSEL_DPLL_USB,
+       .clk_bypass     = &usb_dpll_hs_clk_div,
+       .flags          = DPLL_J_TYPE,
+       .clk_ref        = &sys_clkin,
+       .control_reg    = OMAP54XX_CM_CLKMODE_DPLL_USB,
+       .modes          = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
+       .autoidle_reg   = OMAP54XX_CM_AUTOIDLE_DPLL_USB,
+       .idlest_reg     = OMAP54XX_CM_IDLEST_DPLL_USB,
+       .mult_mask      = OMAP54XX_DPLL_MULT_MASK,
+       .div1_mask      = OMAP54XX_DPLL_DIV_MASK,
+       .enable_mask    = OMAP54XX_DPLL_EN_MASK,
+       .autoidle_mask  = OMAP54XX_AUTO_DPLL_MODE_MASK,
+       .idlest_mask    = OMAP54XX_ST_DPLL_CLK_MASK,
+       .sddiv_mask     = OMAP54XX_DPLL_SD_DIV_MASK,
+       .max_multiplier = 4095,
+       .max_divider    = 256,
+       .min_divider    = 1,
+};
+
+static const char *dpll_usb_ck_parents[] = {
+       "sys_clkin", "usb_dpll_hs_clk_div"
+};
+
+static struct clk dpll_usb_ck;
+
+static const struct clk_ops dpll_usb_ck_ops = {
+       .enable         = &omap3_noncore_dpll_enable,
+       .disable        = &omap3_noncore_dpll_disable,
+       .recalc_rate    = &omap3_dpll_recalc,
+       .round_rate     = &omap2_dpll_round_rate,
+       .set_rate       = &omap3_noncore_dpll_set_rate,
+       .get_parent     = &omap2_init_dpll_parent,
+       .init   = &omap2_init_clk_clkdm,
+};
+
+static struct clk_hw_omap dpll_usb_ck_hw = {
+       .hw = {
+               .clk = &dpll_usb_ck,
+       },
+       .dpll_data      = &dpll_usb_dd,
+       .clkdm_name     = "l3init_clkdm",
+       .ops            = &clkhwops_omap3_dpll,
+};
+
+DEFINE_STRUCT_CLK(dpll_usb_ck, dpll_usb_ck_parents, dpll_usb_ck_ops);
+
+static const char *dpll_usb_clkdcoldo_parents[] = {
+       "dpll_usb_ck",
+};
+
+static struct clk dpll_usb_clkdcoldo;
+
+static struct clk_hw_omap dpll_usb_clkdcoldo_hw = {
+       .hw = {
+               .clk = &dpll_usb_clkdcoldo,
+       },
+       .clksel_reg     = OMAP54XX_CM_CLKDCOLDO_DPLL_USB,
+};
+
+DEFINE_STRUCT_CLK(dpll_usb_clkdcoldo, dpll_usb_clkdcoldo_parents, c2c_fclk_ops);
+
+DEFINE_CLK_OMAP_HSDIVIDER63(dpll_usb_m2_ck, "dpll_usb_ck", &dpll_usb_ck, 0x0,
+                           OMAP54XX_CM_DIV_M2_DPLL_USB,
+                           OMAP54XX_DIVHS_0_6_MASK);
+
+static const char *dss_syc_gfclk_div_parents[] = {
+       "sys_clkin",
+};
+
+static struct clk dss_syc_gfclk_div;
+
+static struct clk_hw_omap dss_syc_gfclk_div_hw = {
+       .hw = {
+               .clk = &dss_syc_gfclk_div,
+       },
+};
+
+DEFINE_STRUCT_CLK(dss_syc_gfclk_div, dss_syc_gfclk_div_parents, c2c_fclk_ops);
+
+DEFINE_CLK_FIXED_FACTOR(func_128m_clk, "dpll_per_h11x2_ck", &dpll_per_h11x2_ck,
+                       0x0, 1, 2);
+
+DEFINE_CLK_FIXED_FACTOR(func_12m_fclk, "dpll_per_m2x2_ck", &dpll_per_m2x2_ck,
+                       0x0, 1, 16);
+
+DEFINE_CLK_FIXED_FACTOR(func_24m_clk, "dpll_per_m2_ck", &dpll_per_m2_ck, 0x0, 1,
+                       4);
+
+DEFINE_CLK_FIXED_FACTOR(func_48m_fclk, "dpll_per_m2x2_ck", &dpll_per_m2x2_ck,
+                       0x0, 1, 4);
+
+DEFINE_CLK_FIXED_FACTOR(func_96m_fclk, "dpll_per_m2x2_ck", &dpll_per_m2x2_ck,
+                       0x0, 1, 2);
+
+static struct clk l3_iclk_div;
+
+static struct clk_hw_omap l3_iclk_div_hw = {
+       .hw = {
+               .clk = &l3_iclk_div,
+       },
+};
+
+DEFINE_STRUCT_CLK(l3_iclk_div, iva_dpll_hs_clk_div_parents, c2c_fclk_ops);
+
+static const char *gpu_l3_iclk_parents[] = {
+       "l3_iclk_div",
+};
+
+static struct clk gpu_l3_iclk;
+
+static struct clk_hw_omap gpu_l3_iclk_hw = {
+       .hw = {
+               .clk = &gpu_l3_iclk,
+       },
+};
+
+DEFINE_STRUCT_CLK(gpu_l3_iclk, gpu_l3_iclk_parents, c2c_fclk_ops);
+
+static const struct clk_div_table l3init_60m_fclk_rates[] = {
+       { .div = 1, .val = 0 },
+       { .div = 8, .val = 1 },
+       { .div = 0 },
+};
+DEFINE_CLK_DIVIDER_TABLE(l3init_60m_fclk, "dpll_usb_m2_ck", &dpll_usb_m2_ck,
+                        0x0, OMAP54XX_CM_CLKSEL_USB_60MHZ,
+                        OMAP54XX_CLKSEL_0_0_SHIFT, OMAP54XX_CLKSEL_0_0_WIDTH,
+                        0x0, l3init_60m_fclk_rates, NULL);
+
+static const char *wkupaon_iclk_mux_parents[] = {
+       "sys_clkin", "abe_lp_clk_div",
+};
+
+DEFINE_CLK_MUX(wkupaon_iclk_mux, wkupaon_iclk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_CLKSEL_WKUPAON, OMAP54XX_CLKSEL_0_0_SHIFT,
+              OMAP54XX_CLKSEL_0_0_WIDTH, 0x0, NULL);
+
+static const char *l3instr_ts_gclk_div_parents[] = {
+       "wkupaon_iclk_mux",
+};
+
+static struct clk l3instr_ts_gclk_div;
+
+static struct clk_hw_omap l3instr_ts_gclk_div_hw = {
+       .hw = {
+               .clk = &l3instr_ts_gclk_div,
+       },
+};
+
+DEFINE_STRUCT_CLK(l3instr_ts_gclk_div, l3instr_ts_gclk_div_parents,
+                 c2c_fclk_ops);
+
+static struct clk l4_root_clk_div;
+
+static struct clk_hw_omap l4_root_clk_div_hw = {
+       .hw = {
+               .clk = &l4_root_clk_div,
+       },
+};
+
+DEFINE_STRUCT_CLK(l4_root_clk_div, gpu_l3_iclk_parents, c2c_fclk_ops);
+
+/* Leaf clocks controlled by modules */
+
+DEFINE_CLK_GATE(dss_32khz_clk, "sys_32k_ck", &sys_32k_ck, 0x0,
+               OMAP54XX_CM_DSS_DSS_CLKCTRL, OMAP54XX_OPTFCLKEN_32KHZ_CLK_SHIFT,
+               0x0, NULL);
+
+DEFINE_CLK_GATE(dss_48mhz_clk, "func_48m_fclk", &func_48m_fclk, 0x0,
+               OMAP54XX_CM_DSS_DSS_CLKCTRL, OMAP54XX_OPTFCLKEN_48MHZ_CLK_SHIFT,
+               0x0, NULL);
+
+DEFINE_CLK_GATE(dss_dss_clk, "dpll_per_h12x2_ck", &dpll_per_h12x2_ck, 0x0,
+               OMAP54XX_CM_DSS_DSS_CLKCTRL, OMAP54XX_OPTFCLKEN_DSSCLK_SHIFT,
+               0x0, NULL);
+
+static const struct clk_ops dss_sys_clk_ops = {
+       .enable         = &omap2_dflt_clk_enable,
+       .disable        = &omap2_dflt_clk_disable,
+       .is_enabled     = &omap2_dflt_clk_is_enabled,
+       .init   = &omap2_init_clk_clkdm,
+};
+
+static const char *dss_sys_clk_parents[] = {
+       "dss_syc_gfclk_div",
+};
+
+static struct clk dss_sys_clk;
+
+static struct clk_hw_omap dss_sys_clk_hw = {
+       .hw = {
+               .clk = &dss_sys_clk,
+       },
+       .clkdm_name     = "dss_clkdm",
+       .enable_reg     = OMAP54XX_CM_DSS_DSS_CLKCTRL,
+       .enable_bit     = OMAP54XX_OPTFCLKEN_SYS_CLK_SHIFT,
+};
+
+DEFINE_STRUCT_CLK(dss_sys_clk, dss_sys_clk_parents, dss_sys_clk_ops);
+
+DEFINE_CLK_GATE(gpio1_dbclk, "sys_32k_ck", &sys_32k_ck, 0x0,
+               OMAP54XX_CM_WKUPAON_GPIO1_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_DBCLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(gpio2_dbclk, "sys_32k_ck", &sys_32k_ck, 0x0,
+               OMAP54XX_CM_L4PER_GPIO2_CLKCTRL, OMAP54XX_OPTFCLKEN_DBCLK_SHIFT,
+               0x0, NULL);
+
+DEFINE_CLK_GATE(gpio3_dbclk, "sys_32k_ck", &sys_32k_ck, 0x0,
+               OMAP54XX_CM_L4PER_GPIO3_CLKCTRL, OMAP54XX_OPTFCLKEN_DBCLK_SHIFT,
+               0x0, NULL);
+
+DEFINE_CLK_GATE(gpio4_dbclk, "sys_32k_ck", &sys_32k_ck, 0x0,
+               OMAP54XX_CM_L4PER_GPIO4_CLKCTRL, OMAP54XX_OPTFCLKEN_DBCLK_SHIFT,
+               0x0, NULL);
+
+DEFINE_CLK_GATE(gpio5_dbclk, "sys_32k_ck", &sys_32k_ck, 0x0,
+               OMAP54XX_CM_L4PER_GPIO5_CLKCTRL, OMAP54XX_OPTFCLKEN_DBCLK_SHIFT,
+               0x0, NULL);
+
+DEFINE_CLK_GATE(gpio6_dbclk, "sys_32k_ck", &sys_32k_ck, 0x0,
+               OMAP54XX_CM_L4PER_GPIO6_CLKCTRL, OMAP54XX_OPTFCLKEN_DBCLK_SHIFT,
+               0x0, NULL);
+
+DEFINE_CLK_GATE(gpio7_dbclk, "sys_32k_ck", &sys_32k_ck, 0x0,
+               OMAP54XX_CM_L4PER_GPIO7_CLKCTRL, OMAP54XX_OPTFCLKEN_DBCLK_SHIFT,
+               0x0, NULL);
+
+DEFINE_CLK_GATE(gpio8_dbclk, "sys_32k_ck", &sys_32k_ck, 0x0,
+               OMAP54XX_CM_L4PER_GPIO8_CLKCTRL, OMAP54XX_OPTFCLKEN_DBCLK_SHIFT,
+               0x0, NULL);
+
+DEFINE_CLK_GATE(iss_ctrlclk, "func_96m_fclk", &func_96m_fclk, 0x0,
+               OMAP54XX_CM_CAM_ISS_CLKCTRL, OMAP54XX_OPTFCLKEN_CTRLCLK_SHIFT,
+               0x0, NULL);
+
+DEFINE_CLK_GATE(lli_txphy_clk, "dpll_unipro1_clkdcoldo",
+               &dpll_unipro1_clkdcoldo, 0x0, OMAP54XX_CM_MIPIEXT_LLI_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_TXPHY_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(lli_txphy_ls_clk, "dpll_unipro1_m2_ck", &dpll_unipro1_m2_ck,
+               0x0, OMAP54XX_CM_MIPIEXT_LLI_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_TXPHY_LS_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(mmc1_32khz_clk, "sys_32k_ck", &sys_32k_ck, 0x0,
+               OMAP54XX_CM_L3INIT_MMC1_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_32KHZ_CLK_8_8_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(sata_ref_clk, "sys_clkin", &sys_clkin, 0x0,
+               OMAP54XX_CM_L3INIT_SATA_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_REF_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(slimbus1_slimbus_clk, "slimbus_clk", &slimbus_clk, 0x0,
+               OMAP54XX_CM_ABE_SLIMBUS1_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_SLIMBUS_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_host_hs_hsic480m_p1_clk, "dpll_usb_m2_ck", &dpll_usb_m2_ck,
+               0x0, OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_HSIC480M_P1_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_host_hs_hsic480m_p2_clk, "dpll_usb_m2_ck", &dpll_usb_m2_ck,
+               0x0, OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_HSIC480M_P2_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_host_hs_hsic480m_p3_clk, "dpll_usb_m2_ck", &dpll_usb_m2_ck,
+               0x0, OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_HSIC480M_P3_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_host_hs_hsic60m_p1_clk, "l3init_60m_fclk", &l3init_60m_fclk,
+               0x0, OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_HSIC60M_P1_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_host_hs_hsic60m_p2_clk, "l3init_60m_fclk", &l3init_60m_fclk,
+               0x0, OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_HSIC60M_P2_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_host_hs_hsic60m_p3_clk, "l3init_60m_fclk", &l3init_60m_fclk,
+               0x0, OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_HSIC60M_P3_CLK_SHIFT, 0x0, NULL);
+
+static const char *utmi_p1_gfclk_parents[] = {
+       "l3init_60m_fclk", "xclk60mhsp1",
+};
+
+DEFINE_CLK_MUX(utmi_p1_gfclk, utmi_p1_gfclk_parents, NULL, 0x0,
+              OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL,
+              OMAP54XX_CLKSEL_UTMI_P1_SHIFT, OMAP54XX_CLKSEL_UTMI_P1_WIDTH,
+              0x0, NULL);
+
+DEFINE_CLK_GATE(usb_host_hs_utmi_p1_clk, "utmi_p1_gfclk", &utmi_p1_gfclk, 0x0,
+               OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_UTMI_P1_CLK_SHIFT, 0x0, NULL);
+
+static const char *utmi_p2_gfclk_parents[] = {
+       "l3init_60m_fclk", "xclk60mhsp2",
+};
+
+DEFINE_CLK_MUX(utmi_p2_gfclk, utmi_p2_gfclk_parents, NULL, 0x0,
+              OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL,
+              OMAP54XX_CLKSEL_UTMI_P2_SHIFT, OMAP54XX_CLKSEL_UTMI_P2_WIDTH,
+              0x0, NULL);
+
+DEFINE_CLK_GATE(usb_host_hs_utmi_p2_clk, "utmi_p2_gfclk", &utmi_p2_gfclk, 0x0,
+               OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_UTMI_P2_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_host_hs_utmi_p3_clk, "l3init_60m_fclk", &l3init_60m_fclk,
+               0x0, OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_UTMI_P3_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_otg_ss_refclk960m, "dpll_usb_clkdcoldo",
+               &dpll_usb_clkdcoldo, 0x0, OMAP54XX_CM_L3INIT_USB_OTG_SS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_REFCLK960M_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_phy_cm_clk32k, "sys_32k_ck", &sys_32k_ck, 0x0,
+               OMAP54XX_CM_COREAON_USB_PHY_CORE_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_CLK32K_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_tll_hs_usb_ch0_clk, "l3init_60m_fclk", &l3init_60m_fclk,
+               0x0, OMAP54XX_CM_L3INIT_USB_TLL_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_USB_CH0_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_tll_hs_usb_ch1_clk, "l3init_60m_fclk", &l3init_60m_fclk,
+               0x0, OMAP54XX_CM_L3INIT_USB_TLL_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_USB_CH1_CLK_SHIFT, 0x0, NULL);
+
+DEFINE_CLK_GATE(usb_tll_hs_usb_ch2_clk, "l3init_60m_fclk", &l3init_60m_fclk,
+               0x0, OMAP54XX_CM_L3INIT_USB_TLL_HS_CLKCTRL,
+               OMAP54XX_OPTFCLKEN_USB_CH2_CLK_SHIFT, 0x0, NULL);
+
+/* Remaining optional clocks */
+DEFINE_CLK_DIVIDER(aess_fclk, "abe_clk", &abe_clk, 0x0,
+                  OMAP54XX_CM_ABE_AESS_CLKCTRL,
+                  OMAP54XX_CLKSEL_AESS_FCLK_SHIFT,
+                  OMAP54XX_CLKSEL_AESS_FCLK_WIDTH, 0x0, NULL);
+
+static const char *dmic_sync_mux_ck_parents[] = {
+       "abe_24m_fclk", "dss_syc_gfclk_div", "func_24m_clk",
+};
+
+DEFINE_CLK_MUX(dmic_sync_mux_ck, dmic_sync_mux_ck_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_DMIC_CLKCTRL,
+              OMAP54XX_CLKSEL_INTERNAL_SOURCE_SHIFT,
+              OMAP54XX_CLKSEL_INTERNAL_SOURCE_WIDTH, 0x0, NULL);
+
+static const char *dmic_gfclk_parents[] = {
+       "dmic_sync_mux_ck", "pad_clks", "slimbus_clk",
+};
+
+DEFINE_CLK_MUX(dmic_gfclk, dmic_gfclk_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_DMIC_CLKCTRL, OMAP54XX_CLKSEL_SOURCE_SHIFT,
+              OMAP54XX_CLKSEL_SOURCE_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_DIVIDER(fdif_fclk, "dpll_per_h11x2_ck", &dpll_per_h11x2_ck, 0x0,
+                  OMAP54XX_CM_CAM_FDIF_CLKCTRL, OMAP54XX_CLKSEL_FCLK_SHIFT,
+                  OMAP54XX_CLKSEL_FCLK_WIDTH, 0x0, NULL);
+
+static const char *gpu_core_gclk_mux_parents[] = {
+       "dpll_core_h14x2_ck", "dpll_per_h14x2_ck",
+};
+
+DEFINE_CLK_MUX(gpu_core_gclk_mux, gpu_core_gclk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_GPU_GPU_CLKCTRL, OMAP54XX_CLKSEL_GPU_CORE_GCLK_SHIFT,
+              OMAP54XX_CLKSEL_GPU_CORE_GCLK_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(gpu_hyd_gclk_mux, gpu_core_gclk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_GPU_GPU_CLKCTRL, OMAP54XX_CLKSEL_GPU_HYD_GCLK_SHIFT,
+              OMAP54XX_CLKSEL_GPU_HYD_GCLK_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_DIVIDER(hsi_fclk, "dpll_per_m2x2_ck", &dpll_per_m2x2_ck, 0x0,
+                  OMAP54XX_CM_L3INIT_HSI_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+                  OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(mcasp_sync_mux_ck, dmic_sync_mux_ck_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_MCASP_CLKCTRL,
+              OMAP54XX_CLKSEL_INTERNAL_SOURCE_SHIFT,
+              OMAP54XX_CLKSEL_INTERNAL_SOURCE_WIDTH, 0x0, NULL);
+
+static const char *mcasp_gfclk_parents[] = {
+       "mcasp_sync_mux_ck", "pad_clks", "slimbus_clk",
+};
+
+DEFINE_CLK_MUX(mcasp_gfclk, mcasp_gfclk_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_MCASP_CLKCTRL, OMAP54XX_CLKSEL_SOURCE_SHIFT,
+              OMAP54XX_CLKSEL_SOURCE_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(mcbsp1_sync_mux_ck, dmic_sync_mux_ck_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_MCBSP1_CLKCTRL,
+              OMAP54XX_CLKSEL_INTERNAL_SOURCE_SHIFT,
+              OMAP54XX_CLKSEL_INTERNAL_SOURCE_WIDTH, 0x0, NULL);
+
+static const char *mcbsp1_gfclk_parents[] = {
+       "mcbsp1_sync_mux_ck", "pad_clks", "slimbus_clk",
+};
+
+DEFINE_CLK_MUX(mcbsp1_gfclk, mcbsp1_gfclk_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_MCBSP1_CLKCTRL, OMAP54XX_CLKSEL_SOURCE_SHIFT,
+              OMAP54XX_CLKSEL_SOURCE_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(mcbsp2_sync_mux_ck, dmic_sync_mux_ck_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_MCBSP2_CLKCTRL,
+              OMAP54XX_CLKSEL_INTERNAL_SOURCE_SHIFT,
+              OMAP54XX_CLKSEL_INTERNAL_SOURCE_WIDTH, 0x0, NULL);
+
+static const char *mcbsp2_gfclk_parents[] = {
+       "mcbsp2_sync_mux_ck", "pad_clks", "slimbus_clk",
+};
+
+DEFINE_CLK_MUX(mcbsp2_gfclk, mcbsp2_gfclk_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_MCBSP2_CLKCTRL, OMAP54XX_CLKSEL_SOURCE_SHIFT,
+              OMAP54XX_CLKSEL_SOURCE_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(mcbsp3_sync_mux_ck, dmic_sync_mux_ck_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_MCBSP3_CLKCTRL,
+              OMAP54XX_CLKSEL_INTERNAL_SOURCE_SHIFT,
+              OMAP54XX_CLKSEL_INTERNAL_SOURCE_WIDTH, 0x0, NULL);
+
+static const char *mcbsp3_gfclk_parents[] = {
+       "mcbsp3_sync_mux_ck", "pad_clks", "slimbus_clk",
+};
+
+DEFINE_CLK_MUX(mcbsp3_gfclk, mcbsp3_gfclk_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_MCBSP3_CLKCTRL, OMAP54XX_CLKSEL_SOURCE_SHIFT,
+              OMAP54XX_CLKSEL_SOURCE_WIDTH, 0x0, NULL);
+
+static const char *mmc1_fclk_mux_parents[] = {
+       "func_128m_clk", "dpll_per_m2x2_ck",
+};
+
+DEFINE_CLK_MUX(mmc1_fclk_mux, mmc1_fclk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_L3INIT_MMC1_CLKCTRL,
+              OMAP54XX_CLKSEL_SOURCE_L3INIT_MMC1_SHIFT,
+              OMAP54XX_CLKSEL_SOURCE_L3INIT_MMC1_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_DIVIDER(mmc1_fclk, "mmc1_fclk_mux", &mmc1_fclk_mux, 0x0,
+                  OMAP54XX_CM_L3INIT_MMC1_CLKCTRL, OMAP54XX_CLKSEL_DIV_SHIFT,
+                  OMAP54XX_CLKSEL_DIV_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(mmc2_fclk_mux, mmc1_fclk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_L3INIT_MMC2_CLKCTRL,
+              OMAP54XX_CLKSEL_SOURCE_L3INIT_MMC1_SHIFT,
+              OMAP54XX_CLKSEL_SOURCE_L3INIT_MMC1_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_DIVIDER(mmc2_fclk, "mmc2_fclk_mux", &mmc2_fclk_mux, 0x0,
+                  OMAP54XX_CM_L3INIT_MMC2_CLKCTRL, OMAP54XX_CLKSEL_DIV_SHIFT,
+                  OMAP54XX_CLKSEL_DIV_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(timer10_gfclk_mux, abe_dpll_bypass_clk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_L4PER_TIMER10_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+              OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(timer11_gfclk_mux, abe_dpll_bypass_clk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_L4PER_TIMER11_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+              OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(timer1_gfclk_mux, abe_dpll_bypass_clk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_WKUPAON_TIMER1_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+              OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(timer2_gfclk_mux, abe_dpll_bypass_clk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_L4PER_TIMER2_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+              OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(timer3_gfclk_mux, abe_dpll_bypass_clk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_L4PER_TIMER3_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+              OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(timer4_gfclk_mux, abe_dpll_bypass_clk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_L4PER_TIMER4_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+              OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+static const char *timer5_gfclk_mux_parents[] = {
+       "dss_syc_gfclk_div", "sys_32k_ck",
+};
+
+DEFINE_CLK_MUX(timer5_gfclk_mux, timer5_gfclk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_TIMER5_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+              OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(timer6_gfclk_mux, timer5_gfclk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_TIMER6_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+              OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(timer7_gfclk_mux, timer5_gfclk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_TIMER7_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+              OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(timer8_gfclk_mux, timer5_gfclk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_ABE_TIMER8_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+              OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+DEFINE_CLK_MUX(timer9_gfclk_mux, abe_dpll_bypass_clk_mux_parents, NULL, 0x0,
+              OMAP54XX_CM_L4PER_TIMER9_CLKCTRL, OMAP54XX_CLKSEL_SHIFT,
+              OMAP54XX_CLKSEL_WIDTH, 0x0, NULL);
+
+/* SCRM aux clk nodes */
+static const char *dpll_core_m3x2_opt_ck_parents[] = {
+       "dpll_core_m3x2_ck",
+};
+
+static struct clk dpll_core_m3x2_opt_ck;
+
+static struct clk_hw_omap dpll_core_m3x2_opt_ck_hw = {
+       .hw = {
+               .clk = &dpll_core_m3x2_opt_ck,
+       },
+       .clkdm_name     = "wkupaon_clkdm",
+       .enable_reg     = OMAP54XX_CM_WKUPAON_SCRM_CLKCTRL,
+       .enable_bit     = OMAP54XX_OPTFCLKEN_SCRM_CORE_SHIFT,
+};
+
+DEFINE_STRUCT_CLK(dpll_core_m3x2_opt_ck, dpll_core_m3x2_opt_ck_parents,
+                 dss_sys_clk_ops);
+
+static const char *dpll_per_m3x2_opt_ck_parents[] = {
+       "dpll_per_m3x2_ck",
+};
+
+static struct clk dpll_per_m3x2_opt_ck;
+
+static struct clk_hw_omap dpll_per_m3x2_opt_ck_hw = {
+       .hw = {
+               .clk = &dpll_per_m3x2_opt_ck,
+       },
+       .clkdm_name     = "wkupaon_clkdm",
+       .enable_reg     = OMAP54XX_CM_WKUPAON_SCRM_CLKCTRL,
+       .enable_bit     = OMAP54XX_OPTFCLKEN_SCRM_PER_SHIFT,
+};
+
+DEFINE_STRUCT_CLK(dpll_per_m3x2_opt_ck, dpll_per_m3x2_opt_ck_parents,
+                 dss_sys_clk_ops);
+
+static const struct clksel auxclk_src_sel[] = {
+       { .parent = &sys_clkin, .rates = div_1_0_rates },
+       { .parent = &dpll_core_m3x2_opt_ck, .rates = div_1_1_rates },
+       { .parent = &dpll_per_m3x2_opt_ck, .rates = div_1_2_rates },
+       { .parent = NULL },
+};
+
+static const char *auxclk_src_ck_parents[] = {
+       "sys_clkin_ck", "dpll_core_m3x2_ck", "dpll_per_m3x2_ck",
+};
+
+static const struct clk_ops auxclk_src_ck_ops = {
+       .enable         = &omap2_dflt_clk_enable,
+       .disable        = &omap2_dflt_clk_disable,
+       .is_enabled     = &omap2_dflt_clk_is_enabled,
+       .recalc_rate    = &omap2_clksel_recalc,
+       .get_parent     = &omap2_clksel_find_parent_index,
+};
+
+DEFINE_CLK_OMAP_MUX_GATE(auxclk0_src_ck, NULL, auxclk_src_sel,
+                        OMAP5_SCRM_AUXCLK0, OMAP5_SRCSELECT_MASK,
+                        OMAP5_SCRM_AUXCLK0, OMAP5_ENABLE_SHIFT, NULL,
+                        auxclk_src_ck_parents, auxclk_src_ck_ops);
+
+DEFINE_CLK_DIVIDER(auxclk0_ck, "auxclk0_src_ck", &auxclk0_src_ck, 0x0,
+                  OMAP5_SCRM_AUXCLK0, OMAP5_CLKDIV_SHIFT, OMAP5_CLKDIV_WIDTH,
+                  0x0, NULL);
+
+DEFINE_CLK_OMAP_MUX_GATE(auxclk1_src_ck, NULL, auxclk_src_sel,
+                        OMAP5_SCRM_AUXCLK1, OMAP5_SRCSELECT_MASK,
+                        OMAP5_SCRM_AUXCLK1, OMAP5_ENABLE_SHIFT, NULL,
+                        auxclk_src_ck_parents, auxclk_src_ck_ops);
+
+DEFINE_CLK_DIVIDER(auxclk1_ck, "auxclk1_src_ck", &auxclk1_src_ck, 0x0,
+                  OMAP5_SCRM_AUXCLK1, OMAP5_CLKDIV_SHIFT, OMAP5_CLKDIV_WIDTH,
+                  0x0, NULL);
+
+DEFINE_CLK_OMAP_MUX_GATE(auxclk2_src_ck, NULL, auxclk_src_sel,
+                        OMAP5_SCRM_AUXCLK2, OMAP5_SRCSELECT_MASK,
+                        OMAP5_SCRM_AUXCLK2, OMAP5_ENABLE_SHIFT, NULL,
+                        auxclk_src_ck_parents, auxclk_src_ck_ops);
+
+DEFINE_CLK_DIVIDER(auxclk2_ck, "auxclk2_src_ck", &auxclk2_src_ck, 0x0,
+                  OMAP5_SCRM_AUXCLK2, OMAP5_CLKDIV_SHIFT, OMAP5_CLKDIV_WIDTH,
+                  0x0, NULL);
+
+DEFINE_CLK_OMAP_MUX_GATE(auxclk3_src_ck, NULL, auxclk_src_sel,
+                        OMAP5_SCRM_AUXCLK3, OMAP5_SRCSELECT_MASK,
+                        OMAP5_SCRM_AUXCLK3, OMAP5_ENABLE_SHIFT, NULL,
+                        auxclk_src_ck_parents, auxclk_src_ck_ops);
+
+DEFINE_CLK_DIVIDER(auxclk3_ck, "auxclk3_src_ck", &auxclk3_src_ck, 0x0,
+                  OMAP5_SCRM_AUXCLK3, OMAP5_CLKDIV_SHIFT, OMAP5_CLKDIV_WIDTH,
+                  0x0, NULL);
+
+static const char *auxclkreq_ck_parents[] = {
+       "auxclk0_ck", "auxclk1_ck", "auxclk2_ck", "auxclk3_ck", "auxclk4_ck",
+       "auxclk5_ck",
+};
+
+DEFINE_CLK_MUX(auxclkreq0_ck, auxclkreq_ck_parents, NULL, 0x0,
+              OMAP5_SCRM_AUXCLKREQ0, OMAP5_MAPPING_SHIFT, OMAP5_MAPPING_WIDTH,
+              0x0, NULL);
+
+DEFINE_CLK_MUX(auxclkreq1_ck, auxclkreq_ck_parents, NULL, 0x0,
+              OMAP5_SCRM_AUXCLKREQ1, OMAP5_MAPPING_SHIFT, OMAP5_MAPPING_WIDTH,
+              0x0, NULL);
+
+DEFINE_CLK_MUX(auxclkreq2_ck, auxclkreq_ck_parents, NULL, 0x0,
+              OMAP5_SCRM_AUXCLKREQ2, OMAP5_MAPPING_SHIFT, OMAP5_MAPPING_WIDTH,
+              0x0, NULL);
+
+DEFINE_CLK_MUX(auxclkreq3_ck, auxclkreq_ck_parents, NULL, 0x0,
+              OMAP5_SCRM_AUXCLKREQ3, OMAP5_MAPPING_SHIFT, OMAP5_MAPPING_WIDTH,
+              0x0, NULL);
+
+/*
+ * clkdev
+ */
+
+static struct omap_clk omap54xx_clks[] = {
+       CLK(NULL,       "pad_clks_src_ck",              &pad_clks_src_ck,       CK_54XX),
+       CLK(NULL,       "pad_clks_ck",                  &pad_clks_ck,   CK_54XX),
+       CLK(NULL,       "secure_32k_clk_src_ck",        &secure_32k_clk_src_ck, CK_54XX),
+       CLK(NULL,       "slimbus_src_clk",              &slimbus_src_clk,       CK_54XX),
+       CLK(NULL,       "slimbus_clk",                  &slimbus_clk,   CK_54XX),
+       CLK(NULL,       "sys_32k_ck",                   &sys_32k_ck,    CK_54XX),
+       CLK(NULL,       "virt_12000000_ck",             &virt_12000000_ck,      CK_54XX),
+       CLK(NULL,       "virt_13000000_ck",             &virt_13000000_ck,      CK_54XX),
+       CLK(NULL,       "virt_16800000_ck",             &virt_16800000_ck,      CK_54XX),
+       CLK(NULL,       "virt_19200000_ck",             &virt_19200000_ck,      CK_54XX),
+       CLK(NULL,       "virt_26000000_ck",             &virt_26000000_ck,      CK_54XX),
+       CLK(NULL,       "virt_27000000_ck",             &virt_27000000_ck,      CK_54XX),
+       CLK(NULL,       "virt_38400000_ck",             &virt_38400000_ck,      CK_54XX),
+       CLK(NULL,       "sys_clkin",                    &sys_clkin,     CK_54XX),
+       CLK(NULL,       "xclk60mhsp1_ck",               &xclk60mhsp1_ck,        CK_54XX),
+       CLK(NULL,       "xclk60mhsp2_ck",               &xclk60mhsp2_ck,        CK_54XX),
+       CLK(NULL,       "abe_dpll_bypass_clk_mux",      &abe_dpll_bypass_clk_mux,       CK_54XX),
+       CLK(NULL,       "abe_dpll_clk_mux",             &abe_dpll_clk_mux,      CK_54XX),
+       CLK(NULL,       "dpll_abe_ck",                  &dpll_abe_ck,   CK_54XX),
+       CLK(NULL,       "dpll_abe_x2_ck",               &dpll_abe_x2_ck,        CK_54XX),
+       CLK(NULL,       "dpll_abe_m2x2_ck",             &dpll_abe_m2x2_ck,      CK_54XX),
+       CLK(NULL,       "abe_24m_fclk",                 &abe_24m_fclk,  CK_54XX),
+       CLK(NULL,       "abe_clk",                      &abe_clk,       CK_54XX),
+       CLK(NULL,       "abe_iclk",                     &abe_iclk,      CK_54XX),
+       CLK(NULL,       "abe_lp_clk_div",               &abe_lp_clk_div,        CK_54XX),
+       CLK(NULL,       "dpll_abe_m3x2_ck",             &dpll_abe_m3x2_ck,      CK_54XX),
+       CLK(NULL,       "dpll_core_ck",                 &dpll_core_ck,  CK_54XX),
+       CLK(NULL,       "dpll_core_x2_ck",              &dpll_core_x2_ck,       CK_54XX),
+       CLK(NULL,       "dpll_core_h21x2_ck",           &dpll_core_h21x2_ck,    CK_54XX),
+       CLK(NULL,       "c2c_fclk",                     &c2c_fclk,      CK_54XX),
+       CLK(NULL,       "c2c_iclk",                     &c2c_iclk,      CK_54XX),
+       CLK(NULL,       "custefuse_sys_gfclk_div",      &custefuse_sys_gfclk_div,       CK_54XX),
+       CLK(NULL,       "dpll_core_h11x2_ck",           &dpll_core_h11x2_ck,    CK_54XX),
+       CLK(NULL,       "dpll_core_h12x2_ck",           &dpll_core_h12x2_ck,    CK_54XX),
+       CLK(NULL,       "dpll_core_h13x2_ck",           &dpll_core_h13x2_ck,    CK_54XX),
+       CLK(NULL,       "dpll_core_h14x2_ck",           &dpll_core_h14x2_ck,    CK_54XX),
+       CLK(NULL,       "dpll_core_h22x2_ck",           &dpll_core_h22x2_ck,    CK_54XX),
+       CLK(NULL,       "dpll_core_h23x2_ck",           &dpll_core_h23x2_ck,    CK_54XX),
+       CLK(NULL,       "dpll_core_h24x2_ck",           &dpll_core_h24x2_ck,    CK_54XX),
+       CLK(NULL,       "dpll_core_m2_ck",              &dpll_core_m2_ck,       CK_54XX),
+       CLK(NULL,       "dpll_core_m3x2_ck",            &dpll_core_m3x2_ck,     CK_54XX),
+       CLK(NULL,       "dpll_core_m3x2_opt_ck",        &dpll_core_m3x2_opt_ck, CK_54XX),
+       CLK(NULL,       "iva_dpll_hs_clk_div",          &iva_dpll_hs_clk_div,   CK_54XX),
+       CLK(NULL,       "dpll_iva_ck",                  &dpll_iva_ck,   CK_54XX),
+       CLK(NULL,       "dpll_iva_x2_ck",               &dpll_iva_x2_ck,        CK_54XX),
+       CLK(NULL,       "dpll_iva_h11x2_ck",            &dpll_iva_h11x2_ck,     CK_54XX),
+       CLK(NULL,       "dpll_iva_h12x2_ck",            &dpll_iva_h12x2_ck,     CK_54XX),
+       CLK(NULL,       "mpu_dpll_hs_clk_div",          &mpu_dpll_hs_clk_div,   CK_54XX),
+       CLK(NULL,       "dpll_mpu_ck",                  &dpll_mpu_ck,   CK_54XX),
+       CLK(NULL,       "dpll_mpu_m2_ck",               &dpll_mpu_m2_ck,        CK_54XX),
+       CLK(NULL,       "per_dpll_hs_clk_div",          &per_dpll_hs_clk_div,   CK_54XX),
+       CLK(NULL,       "dpll_per_ck",                  &dpll_per_ck,   CK_54XX),
+       CLK(NULL,       "dpll_per_x2_ck",               &dpll_per_x2_ck,        CK_54XX),
+       CLK(NULL,       "dpll_per_h11x2_ck",            &dpll_per_h11x2_ck,     CK_54XX),
+       CLK(NULL,       "dpll_per_h12x2_ck",            &dpll_per_h12x2_ck,     CK_54XX),
+       CLK(NULL,       "dpll_per_h14x2_ck",            &dpll_per_h14x2_ck,     CK_54XX),
+       CLK(NULL,       "dpll_per_m2_ck",               &dpll_per_m2_ck,        CK_54XX),
+       CLK(NULL,       "dpll_per_m2x2_ck",             &dpll_per_m2x2_ck,      CK_54XX),
+       CLK(NULL,       "dpll_per_m3x2_opt_ck",         &dpll_per_m3x2_opt_ck,  CK_54XX),
+       CLK(NULL,       "dpll_unipro1_ck",              &dpll_unipro1_ck,       CK_54XX),
+       CLK(NULL,       "dpll_unipro1_clkdcoldo",       &dpll_unipro1_clkdcoldo,        CK_54XX),
+       CLK(NULL,       "dpll_unipro1_m2_ck",           &dpll_unipro1_m2_ck,    CK_54XX),
+       CLK(NULL,       "dpll_unipro2_ck",              &dpll_unipro2_ck,       CK_54XX),
+       CLK(NULL,       "dpll_unipro2_clkdcoldo",       &dpll_unipro2_clkdcoldo,        CK_54XX),
+       CLK(NULL,       "dpll_unipro2_m2_ck",           &dpll_unipro2_m2_ck,    CK_54XX),
+       CLK(NULL,       "usb_dpll_hs_clk_div",          &usb_dpll_hs_clk_div,   CK_54XX),
+       CLK(NULL,       "dpll_usb_ck",                  &dpll_usb_ck,   CK_54XX),
+       CLK(NULL,       "dpll_usb_clkdcoldo",           &dpll_usb_clkdcoldo,    CK_54XX),
+       CLK(NULL,       "dpll_usb_m2_ck",               &dpll_usb_m2_ck,        CK_54XX),
+       CLK(NULL,       "dss_syc_gfclk_div",            &dss_syc_gfclk_div,     CK_54XX),
+       CLK(NULL,       "func_128m_clk",                &func_128m_clk, CK_54XX),
+       CLK(NULL,       "func_12m_fclk",                &func_12m_fclk, CK_54XX),
+       CLK(NULL,       "func_24m_clk",                 &func_24m_clk,  CK_54XX),
+       CLK(NULL,       "func_48m_fclk",                &func_48m_fclk, CK_54XX),
+       CLK(NULL,       "func_96m_fclk",                &func_96m_fclk, CK_54XX),
+       CLK(NULL,       "l3_iclk_div",                  &l3_iclk_div,   CK_54XX),
+       CLK(NULL,       "gpu_l3_iclk",                  &gpu_l3_iclk,   CK_54XX),
+       CLK(NULL,       "l3init_60m_fclk",              &l3init_60m_fclk,       CK_54XX),
+       CLK(NULL,       "init_60m_fclk",                &l3init_60m_fclk,       CK_54XX),
+       CLK(NULL,       "wkupaon_iclk_mux",             &wkupaon_iclk_mux,      CK_54XX),
+       CLK(NULL,       "l3instr_ts_gclk_div",          &l3instr_ts_gclk_div,   CK_54XX),
+       CLK(NULL,       "l4_root_clk_div",              &l4_root_clk_div,       CK_54XX),
+       CLK(NULL,       "dss_32khz_clk",                &dss_32khz_clk, CK_54XX),
+       CLK(NULL,       "dss_48mhz_clk",                &dss_48mhz_clk, CK_54XX),
+       CLK(NULL,       "dss_dss_clk",                  &dss_dss_clk,   CK_54XX),
+       CLK(NULL,       "dss_sys_clk",                  &dss_sys_clk,   CK_54XX),
+       CLK(NULL,       "gpio1_dbclk",                  &gpio1_dbclk,   CK_54XX),
+       CLK(NULL,       "gpio2_dbclk",                  &gpio2_dbclk,   CK_54XX),
+       CLK(NULL,       "gpio3_dbclk",                  &gpio3_dbclk,   CK_54XX),
+       CLK(NULL,       "gpio4_dbclk",                  &gpio4_dbclk,   CK_54XX),
+       CLK(NULL,       "gpio5_dbclk",                  &gpio5_dbclk,   CK_54XX),
+       CLK(NULL,       "gpio6_dbclk",                  &gpio6_dbclk,   CK_54XX),
+       CLK(NULL,       "gpio7_dbclk",                  &gpio7_dbclk,   CK_54XX),
+       CLK(NULL,       "gpio8_dbclk",                  &gpio8_dbclk,   CK_54XX),
+       CLK(NULL,       "iss_ctrlclk",                  &iss_ctrlclk,   CK_54XX),
+       CLK(NULL,       "lli_txphy_clk",                &lli_txphy_clk, CK_54XX),
+       CLK(NULL,       "lli_txphy_ls_clk",             &lli_txphy_ls_clk,      CK_54XX),
+       CLK(NULL,       "mmc1_32khz_clk",               &mmc1_32khz_clk,        CK_54XX),
+       CLK(NULL,       "sata_ref_clk",                 &sata_ref_clk,  CK_54XX),
+       CLK(NULL,       "slimbus1_slimbus_clk",         &slimbus1_slimbus_clk,  CK_54XX),
+       CLK(NULL,       "usb_host_hs_hsic480m_p1_clk",  &usb_host_hs_hsic480m_p1_clk,   CK_54XX),
+       CLK(NULL,       "usb_host_hs_hsic480m_p2_clk",  &usb_host_hs_hsic480m_p2_clk,   CK_54XX),
+       CLK(NULL,       "usb_host_hs_hsic480m_p3_clk",  &usb_host_hs_hsic480m_p3_clk,   CK_54XX),
+       CLK(NULL,       "usb_host_hs_hsic60m_p1_clk",   &usb_host_hs_hsic60m_p1_clk,    CK_54XX),
+       CLK(NULL,       "usb_host_hs_hsic60m_p2_clk",   &usb_host_hs_hsic60m_p2_clk,    CK_54XX),
+       CLK(NULL,       "usb_host_hs_hsic60m_p3_clk",   &usb_host_hs_hsic60m_p3_clk,    CK_54XX),
+       CLK(NULL,       "usb_host_hs_utmi_p1_clk",      &usb_host_hs_utmi_p1_clk,       CK_54XX),
+       CLK(NULL,       "usb_host_hs_utmi_p2_clk",      &usb_host_hs_utmi_p2_clk,       CK_54XX),
+       CLK(NULL,       "usb_host_hs_utmi_p3_clk",      &usb_host_hs_utmi_p3_clk,       CK_54XX),
+       CLK(NULL,       "usb_otg_ss_refclk960m",        &usb_otg_ss_refclk960m, CK_54XX),
+       CLK(NULL,       "usb_phy_cm_clk32k",            &usb_phy_cm_clk32k,     CK_54XX),
+       CLK(NULL,       "usb_tll_hs_usb_ch0_clk",       &usb_tll_hs_usb_ch0_clk,        CK_54XX),
+       CLK(NULL,       "usb_tll_hs_usb_ch1_clk",       &usb_tll_hs_usb_ch1_clk,        CK_54XX),
+       CLK(NULL,       "usb_tll_hs_usb_ch2_clk",       &usb_tll_hs_usb_ch2_clk,        CK_54XX),
+       CLK(NULL,       "aess_fclk",                    &aess_fclk,     CK_54XX),
+       CLK(NULL,       "dmic_sync_mux_ck",             &dmic_sync_mux_ck,      CK_54XX),
+       CLK(NULL,       "dmic_gfclk",                   &dmic_gfclk,    CK_54XX),
+       CLK(NULL,       "fdif_fclk",                    &fdif_fclk,     CK_54XX),
+       CLK(NULL,       "gpu_core_gclk_mux",            &gpu_core_gclk_mux,     CK_54XX),
+       CLK(NULL,       "gpu_hyd_gclk_mux",             &gpu_hyd_gclk_mux,      CK_54XX),
+       CLK(NULL,       "hsi_fclk",                     &hsi_fclk,      CK_54XX),
+       CLK(NULL,       "mcasp_sync_mux_ck",            &mcasp_sync_mux_ck,     CK_54XX),
+       CLK(NULL,       "mcasp_gfclk",                  &mcasp_gfclk,   CK_54XX),
+       CLK(NULL,       "mcbsp1_sync_mux_ck",           &mcbsp1_sync_mux_ck,    CK_54XX),
+       CLK(NULL,       "mcbsp1_gfclk",                 &mcbsp1_gfclk,  CK_54XX),
+       CLK(NULL,       "mcbsp2_sync_mux_ck",           &mcbsp2_sync_mux_ck,    CK_54XX),
+       CLK(NULL,       "mcbsp2_gfclk",                 &mcbsp2_gfclk,  CK_54XX),
+       CLK(NULL,       "mcbsp3_sync_mux_ck",           &mcbsp3_sync_mux_ck,    CK_54XX),
+       CLK(NULL,       "mcbsp3_gfclk",                 &mcbsp3_gfclk,  CK_54XX),
+       CLK(NULL,       "mmc1_fclk_mux",                &mmc1_fclk_mux, CK_54XX),
+       CLK(NULL,       "mmc1_fclk",                    &mmc1_fclk,     CK_54XX),
+       CLK(NULL,       "mmc2_fclk_mux",                &mmc2_fclk_mux, CK_54XX),
+       CLK(NULL,       "mmc2_fclk",                    &mmc2_fclk,     CK_54XX),
+       CLK(NULL,       "timer10_gfclk_mux",            &timer10_gfclk_mux,     CK_54XX),
+       CLK(NULL,       "timer11_gfclk_mux",            &timer11_gfclk_mux,     CK_54XX),
+       CLK(NULL,       "timer1_gfclk_mux",             &timer1_gfclk_mux,      CK_54XX),
+       CLK(NULL,       "timer2_gfclk_mux",             &timer2_gfclk_mux,      CK_54XX),
+       CLK(NULL,       "timer3_gfclk_mux",             &timer3_gfclk_mux,      CK_54XX),
+       CLK(NULL,       "timer4_gfclk_mux",             &timer4_gfclk_mux,      CK_54XX),
+       CLK(NULL,       "timer5_gfclk_mux",             &timer5_gfclk_mux,      CK_54XX),
+       CLK(NULL,       "timer6_gfclk_mux",             &timer6_gfclk_mux,      CK_54XX),
+       CLK(NULL,       "timer7_gfclk_mux",             &timer7_gfclk_mux,      CK_54XX),
+       CLK(NULL,       "timer8_gfclk_mux",             &timer8_gfclk_mux,      CK_54XX),
+       CLK(NULL,       "timer9_gfclk_mux",             &timer9_gfclk_mux,      CK_54XX),
+       CLK(NULL,       "utmi_p1_gfclk",                &utmi_p1_gfclk, CK_54XX),
+       CLK(NULL,       "utmi_p2_gfclk",                &utmi_p2_gfclk, CK_54XX),
+       CLK(NULL,       "auxclk0_src_ck",               &auxclk0_src_ck,        CK_54XX),
+       CLK(NULL,       "auxclk0_ck",                   &auxclk0_ck,    CK_54XX),
+       CLK(NULL,       "auxclkreq0_ck",                &auxclkreq0_ck, CK_54XX),
+       CLK(NULL,       "auxclk1_src_ck",               &auxclk1_src_ck,        CK_54XX),
+       CLK(NULL,       "auxclk1_ck",                   &auxclk1_ck,    CK_54XX),
+       CLK(NULL,       "auxclkreq1_ck",                &auxclkreq1_ck, CK_54XX),
+       CLK(NULL,       "auxclk2_src_ck",               &auxclk2_src_ck,        CK_54XX),
+       CLK(NULL,       "auxclk2_ck",                   &auxclk2_ck,    CK_54XX),
+       CLK(NULL,       "auxclkreq2_ck",                &auxclkreq2_ck, CK_54XX),
+       CLK(NULL,       "auxclk3_src_ck",               &auxclk3_src_ck,        CK_54XX),
+       CLK(NULL,       "auxclk3_ck",                   &auxclk3_ck,    CK_54XX),
+       CLK(NULL,       "auxclkreq3_ck",                &auxclkreq3_ck, CK_54XX),
+       CLK(NULL,       "gpmc_ck",                      &dummy_ck,      CK_54XX),
+       CLK("omap_i2c.1",       "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap_i2c.2",       "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap_i2c.3",       "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap_i2c.4",       "ick",                  &dummy_ck,      CK_54XX),
+       CLK(NULL,       "mailboxes_ick",                &dummy_ck,      CK_54XX),
+       CLK("omap_hsmmc.0",     "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap_hsmmc.1",     "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap_hsmmc.2",     "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap_hsmmc.3",     "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap_hsmmc.4",     "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap-mcbsp.1",     "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap-mcbsp.2",     "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap-mcbsp.3",     "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap-mcbsp.4",     "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap2_mcspi.1",    "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap2_mcspi.2",    "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap2_mcspi.3",    "ick",                  &dummy_ck,      CK_54XX),
+       CLK("omap2_mcspi.4",    "ick",                  &dummy_ck,      CK_54XX),
+       CLK(NULL,       "uart1_ick",                    &dummy_ck,      CK_54XX),
+       CLK(NULL,       "uart2_ick",                    &dummy_ck,      CK_54XX),
+       CLK(NULL,       "uart3_ick",                    &dummy_ck,      CK_54XX),
+       CLK(NULL,       "uart4_ick",                    &dummy_ck,      CK_54XX),
+       CLK("usbhs_omap",       "usbhost_ick",          &dummy_ck,      CK_54XX),
+       CLK("usbhs_omap",       "usbtll_fck",           &dummy_ck,      CK_54XX),
+       CLK("omap_wdt", "ick",                          &dummy_ck,      CK_54XX),
+       CLK(NULL,       "timer_32k_ck",                 &sys_32k_ck,    CK_54XX),
+       CLK("4ae18000.timer",   "timer_sys_ck",         &sys_clkin,     CK_54XX),
+       CLK("48032000.timer",   "timer_sys_ck",         &sys_clkin,     CK_54XX),
+       CLK("48034000.timer",   "timer_sys_ck",         &sys_clkin,     CK_54XX),
+       CLK("48036000.timer",   "timer_sys_ck",         &sys_clkin,     CK_54XX),
+       CLK("4803e000.timer",   "timer_sys_ck",         &sys_clkin,     CK_54XX),
+       CLK("48086000.timer",   "timer_sys_ck",         &sys_clkin,     CK_54XX),
+       CLK("48088000.timer",   "timer_sys_ck",         &sys_clkin,     CK_54XX),
+       CLK("40138000.timer",   "timer_sys_ck",         &dss_syc_gfclk_div,     CK_54XX),
+       CLK("4013a000.timer",   "timer_sys_ck",         &dss_syc_gfclk_div,     CK_54XX),
+       CLK("4013c000.timer",   "timer_sys_ck",         &dss_syc_gfclk_div,     CK_54XX),
+       CLK("4013e000.timer",   "timer_sys_ck",         &dss_syc_gfclk_div,     CK_54XX),
+};
+
+int __init omap5xxx_clk_init(void)
+{
+       u32 cpu_clkflg;
+       struct omap_clk *c;
+       int rc;
+
+       if (soc_is_omap54xx()) {
+               cpu_mask = RATE_IN_54XX;
+               cpu_clkflg = CK_54XX;
+       }
+
+       /*
+        * Must stay commented until all OMAP SoC drivers are
+        * converted to runtime PM, or drivers may start crashing
+        *
+        * omap2_clk_disable_clkdm_control();
+        */
+
+       for (c = omap54xx_clks; c < omap54xx_clks + ARRAY_SIZE(omap54xx_clks);
+                                                                       c++) {
+               if (c->cpu & cpu_clkflg) {
+                       clkdev_add(&c->lk);
+                       if (!__clk_init(NULL, c->lk.clk))
+                               omap2_init_clk_hw_omap_clocks(c->lk.clk);
+               }
+       }
+
+       omap2_clk_disable_autoidle_all();
+
+       /*
+        * Lock USB_DPLL to avoid issues with USB host and OFF mode
+        */
+       rc = clk_set_rate(&dpll_usb_ck, OMAP5_DPLL_USB_DEFFREQ);
+       if (rc) {
+               pr_err("%s: failed to configure DPLL_USB: %d\n", __func__, rc);
+       } else {
+               rc = clk_set_rate(&dpll_usb_m2_ck, OMAP5_DPLL_USB_DEFFREQ/2);
+               if (rc)
+                       pr_err("%s: failed to configure DPLL_USB_M2: %d\n",
+                              __func__, rc);
+       }
+
+       /*
+        * The bootloaders does not lock the ABE dpll by default.
+        * This results in all sorts of problem for pheripherals
+        * clocked from ABE dpll. Workaround this by
+        * locking it on boot.
+        */
+       rc = clk_set_parent(&abe_dpll_clk_mux, &sys_32k_ck);
+       if (!rc)
+               rc = clk_set_rate(&dpll_abe_ck, OMAP5_DPLL_ABE_DEFFREQ);
+       if (rc)
+               pr_err("%s: failed to configure ABE DPLL!\n", __func__);
+
+       return 0;
+}
index b40204837bd7e22564e81874b25b6fe35712e638..836311f770a9f94d4e2983cd0a01bc15d9690ebb 100644 (file)
@@ -48,6 +48,7 @@ struct omap_clk {
 #define CK_TI816X      (1 << 7)
 #define CK_446X                (1 << 8)
 #define CK_AM33XX      (1 << 9)        /* AM33xx specific clocks */
+#define CK_54XX                (1 << 10)       /* OMAP54xx specific clocks */
 
 
 #define CK_34XX                (CK_3430ES1 | CK_3430ES2PLUS)
@@ -107,13 +108,31 @@ struct clockdomain;
        };                                                      \
        DEFINE_STRUCT_CLK(_name, _parent_names, _ops);
 
+
+#define DEFINE_CLK_OMAP_HSDIVIDER63(_name, _parent_name,       \
+                               _parent_ptr, _flags,            \
+                               _clksel_reg, _clksel_mask)      \
+                                                               \
+       _DEFINE_CLK_OMAP_HSDIVIDER(_name, _parent_name,         \
+                               _parent_ptr, _flags,            \
+                               _clksel_reg, _clksel_mask, 63)
+
 #define DEFINE_CLK_OMAP_HSDIVIDER(_name, _parent_name,         \
                                _parent_ptr, _flags,            \
                                _clksel_reg, _clksel_mask)      \
+                                                               \
+       _DEFINE_CLK_OMAP_HSDIVIDER(_name, _parent_name,         \
+                               _parent_ptr, _flags,            \
+                               _clksel_reg, _clksel_mask, 31)
+
+
+#define _DEFINE_CLK_OMAP_HSDIVIDER(_name, _parent_name,                \
+                               _parent_ptr, _flags,            \
+                               _clksel_reg, _clksel_mask, mdiv)\
        static const struct clksel _name##_div[] = {            \
                {                                               \
                        .parent = _parent_ptr,                  \
-                       .rates = div31_1to31_rates              \
+                       .rates = div##mdiv##_1to##mdiv##_rates  \
                },                                              \
                { .parent = NULL },                             \
        };                                                      \
@@ -143,6 +162,7 @@ struct clockdomain;
 #define RATE_IN_4460           (1 << 7)
 #define RATE_IN_AM33XX         (1 << 8)
 #define RATE_IN_TI814X         (1 << 9)
+#define RATE_IN_54XX           (1 << 10)
 
 #define RATE_IN_24XX           (RATE_IN_242X | RATE_IN_243X)
 #define RATE_IN_34XX           (RATE_IN_3430ES1 | RATE_IN_3430ES2PLUS)
@@ -463,6 +483,7 @@ extern const struct clksel_rate div_1_2_rates[];
 extern const struct clksel_rate div_1_3_rates[];
 extern const struct clksel_rate div_1_4_rates[];
 extern const struct clksel_rate div31_1to31_rates[];
+extern const struct clksel_rate div63_1to63_rates[];
 
 extern int am33xx_clk_init(void);
 
diff --git a/arch/arm/mach-omap2/clock54xx.h b/arch/arm/mach-omap2/clock54xx.h
new file mode 100644 (file)
index 0000000..3b09134
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * OMAP5 clock function prototypes and macros
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ */
+
+#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK54XX_H
+#define __ARCH_ARM_MACH_OMAP2_CLOCK54XX_H
+
+int omap5xxx_clk_init(void);
+
+#endif
index ef4d21bfb96478da0b9ef681c931303aa9fb1bf4..c918efb465af5a8a70ac4065c269735c56feaa25 100644 (file)
@@ -49,7 +49,7 @@ const struct clksel_rate dsp_ick_rates[] = {
 /* clksel_rate blocks shared between OMAP44xx and AM33xx */
 
 const struct clksel_rate div_1_0_rates[] = {
-       { .div = 1, .val = 0, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
+       { .div = 1, .val = 0, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
        { .div = 0 },
 };
 
@@ -61,57 +61,124 @@ const struct clksel_rate div3_1to4_rates[] = {
 };
 
 const struct clksel_rate div_1_1_rates[] = {
-       { .div = 1, .val = 1, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
+       { .div = 1, .val = 1, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
        { .div = 0 },
 };
 
 const struct clksel_rate div_1_2_rates[] = {
-       { .div = 1, .val = 2, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
+       { .div = 1, .val = 2, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
        { .div = 0 },
 };
 
 const struct clksel_rate div_1_3_rates[] = {
-       { .div = 1, .val = 3, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
+       { .div = 1, .val = 3, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
        { .div = 0 },
 };
 
 const struct clksel_rate div_1_4_rates[] = {
-       { .div = 1, .val = 4, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
+       { .div = 1, .val = 4, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
        { .div = 0 },
 };
 
 const struct clksel_rate div31_1to31_rates[] = {
-       { .div = 1, .val = 1, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 2, .val = 2, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 3, .val = 3, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 4, .val = 4, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 5, .val = 5, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 6, .val = 6, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 7, .val = 7, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 8, .val = 8, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 9, .val = 9, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 10, .val = 10, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 11, .val = 11, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 12, .val = 12, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 13, .val = 13, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 14, .val = 14, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 15, .val = 15, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 16, .val = 16, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 17, .val = 17, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 18, .val = 18, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 19, .val = 19, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 20, .val = 20, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 21, .val = 21, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 22, .val = 22, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 23, .val = 23, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 24, .val = 24, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 25, .val = 25, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 26, .val = 26, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 27, .val = 27, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 28, .val = 28, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 29, .val = 29, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 30, .val = 30, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
-       { .div = 31, .val = 31, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
+       { .div = 1, .val = 1, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 2, .val = 2, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 3, .val = 3, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 4, .val = 4, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 5, .val = 5, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 6, .val = 6, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 7, .val = 7, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 8, .val = 8, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 9, .val = 9, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 10, .val = 10, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 11, .val = 11, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 12, .val = 12, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 13, .val = 13, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 14, .val = 14, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 15, .val = 15, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 16, .val = 16, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 17, .val = 17, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 18, .val = 18, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 19, .val = 19, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 20, .val = 20, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 21, .val = 21, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 22, .val = 22, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 23, .val = 23, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 24, .val = 24, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 25, .val = 25, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 26, .val = 26, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 27, .val = 27, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 28, .val = 28, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 29, .val = 29, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 30, .val = 30, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 31, .val = 31, .flags = RATE_IN_4430 | RATE_IN_AM33XX | RATE_IN_54XX },
+       { .div = 0 },
+};
+
+const struct clksel_rate div63_1to63_rates[] = {
+       { .div = 1, .val = 1, .flags = RATE_IN_54XX },
+       { .div = 2, .val = 2, .flags = RATE_IN_54XX },
+       { .div = 3, .val = 3, .flags = RATE_IN_54XX },
+       { .div = 4, .val = 4, .flags = RATE_IN_54XX },
+       { .div = 5, .val = 5, .flags = RATE_IN_54XX },
+       { .div = 6, .val = 6, .flags = RATE_IN_54XX },
+       { .div = 7, .val = 7, .flags = RATE_IN_54XX },
+       { .div = 8, .val = 8, .flags = RATE_IN_54XX },
+       { .div = 9, .val = 9, .flags = RATE_IN_54XX },
+       { .div = 10, .val = 10, .flags = RATE_IN_54XX },
+       { .div = 11, .val = 11, .flags = RATE_IN_54XX },
+       { .div = 12, .val = 12, .flags = RATE_IN_54XX },
+       { .div = 13, .val = 13, .flags = RATE_IN_54XX },
+       { .div = 14, .val = 14, .flags = RATE_IN_54XX },
+       { .div = 15, .val = 15, .flags = RATE_IN_54XX },
+       { .div = 16, .val = 16, .flags = RATE_IN_54XX },
+       { .div = 17, .val = 17, .flags = RATE_IN_54XX },
+       { .div = 18, .val = 18, .flags = RATE_IN_54XX },
+       { .div = 19, .val = 19, .flags = RATE_IN_54XX },
+       { .div = 20, .val = 20, .flags = RATE_IN_54XX },
+       { .div = 21, .val = 21, .flags = RATE_IN_54XX },
+       { .div = 22, .val = 22, .flags = RATE_IN_54XX },
+       { .div = 23, .val = 23, .flags = RATE_IN_54XX },
+       { .div = 24, .val = 24, .flags = RATE_IN_54XX },
+       { .div = 25, .val = 25, .flags = RATE_IN_54XX },
+       { .div = 26, .val = 26, .flags = RATE_IN_54XX },
+       { .div = 27, .val = 27, .flags = RATE_IN_54XX },
+       { .div = 28, .val = 28, .flags = RATE_IN_54XX },
+       { .div = 29, .val = 29, .flags = RATE_IN_54XX },
+       { .div = 30, .val = 30, .flags = RATE_IN_54XX },
+       { .div = 31, .val = 31, .flags = RATE_IN_54XX },
+       { .div = 32, .val = 32, .flags = RATE_IN_54XX },
+       { .div = 33, .val = 33, .flags = RATE_IN_54XX },
+       { .div = 34, .val = 34, .flags = RATE_IN_54XX },
+       { .div = 35, .val = 35, .flags = RATE_IN_54XX },
+       { .div = 36, .val = 36, .flags = RATE_IN_54XX },
+       { .div = 37, .val = 37, .flags = RATE_IN_54XX },
+       { .div = 38, .val = 38, .flags = RATE_IN_54XX },
+       { .div = 39, .val = 39, .flags = RATE_IN_54XX },
+       { .div = 40, .val = 40, .flags = RATE_IN_54XX },
+       { .div = 41, .val = 41, .flags = RATE_IN_54XX },
+       { .div = 42, .val = 42, .flags = RATE_IN_54XX },
+       { .div = 43, .val = 43, .flags = RATE_IN_54XX },
+       { .div = 44, .val = 44, .flags = RATE_IN_54XX },
+       { .div = 45, .val = 45, .flags = RATE_IN_54XX },
+       { .div = 46, .val = 46, .flags = RATE_IN_54XX },
+       { .div = 47, .val = 47, .flags = RATE_IN_54XX },
+       { .div = 48, .val = 48, .flags = RATE_IN_54XX },
+       { .div = 49, .val = 49, .flags = RATE_IN_54XX },
+       { .div = 50, .val = 50, .flags = RATE_IN_54XX },
+       { .div = 51, .val = 51, .flags = RATE_IN_54XX },
+       { .div = 52, .val = 52, .flags = RATE_IN_54XX },
+       { .div = 53, .val = 53, .flags = RATE_IN_54XX },
+       { .div = 54, .val = 54, .flags = RATE_IN_54XX },
+       { .div = 55, .val = 55, .flags = RATE_IN_54XX },
+       { .div = 56, .val = 56, .flags = RATE_IN_54XX },
+       { .div = 57, .val = 57, .flags = RATE_IN_54XX },
+       { .div = 58, .val = 58, .flags = RATE_IN_54XX },
+       { .div = 59, .val = 59, .flags = RATE_IN_54XX },
+       { .div = 60, .val = 60, .flags = RATE_IN_54XX },
+       { .div = 61, .val = 61, .flags = RATE_IN_54XX },
+       { .div = 62, .val = 62, .flags = RATE_IN_54XX },
+       { .div = 63, .val = 63, .flags = RATE_IN_54XX },
        { .div = 0 },
 };
 
index 7faf82d4e85cfea7837106dfe4de50cc764b6eb4..ec7ca7ca539ee96e55edc365f3b3a8ce109e1488 100644 (file)
@@ -23,9 +23,8 @@
 #include <linux/limits.h>
 #include <linux/err.h>
 #include <linux/clk-provider.h>
-
+#include <linux/seq_file.h>
 #include <linux/io.h>
-
 #include <linux/bitops.h>
 
 #include "soc.h"
@@ -92,8 +91,6 @@ static int _clkdm_register(struct clockdomain *clkdm)
 
        pwrdm_add_clkdm(pwrdm, clkdm);
 
-       spin_lock_init(&clkdm->lock);
-
        pr_debug("clockdomain: registered %s\n", clkdm->name);
 
        return 0;
@@ -122,7 +119,7 @@ static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm,
        return cd;
 }
 
-/*
+/**
  * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store
  * @autodep: struct clkdm_autodep * to resolve
  *
@@ -154,88 +151,206 @@ static void _autodep_lookup(struct clkdm_autodep *autodep)
        autodep->clkdm.ptr = clkdm;
 }
 
-/*
- * _clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable
- * @clkdm: struct clockdomain *
+/**
+ * _resolve_clkdm_deps() - resolve clkdm_names in @clkdm_deps to clkdms
+ * @clkdm: clockdomain that we are resolving dependencies for
+ * @clkdm_deps: ptr to array of struct clkdm_deps to resolve
  *
- * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm'
- * in hardware-supervised mode.  Meant to be called from clock framework
- * when a clock inside clockdomain 'clkdm' is enabled. No return value.
+ * Iterates through @clkdm_deps, looking up the struct clockdomain named by
+ * clkdm_name and storing the clockdomain pointer in the struct clkdm_dep.
+ * No return value.
+ */
+static void _resolve_clkdm_deps(struct clockdomain *clkdm,
+                               struct clkdm_dep *clkdm_deps)
+{
+       struct clkdm_dep *cd;
+
+       for (cd = clkdm_deps; cd && cd->clkdm_name; cd++) {
+               if (cd->clkdm)
+                       continue;
+               cd->clkdm = _clkdm_lookup(cd->clkdm_name);
+
+               WARN(!cd->clkdm, "clockdomain: %s: could not find clkdm %s while resolving dependencies - should never happen",
+                    clkdm->name, cd->clkdm_name);
+       }
+}
+
+/**
+ * _clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1 (lockless)
+ * @clkdm1: wake this struct clockdomain * up (dependent)
+ * @clkdm2: when this struct clockdomain * wakes up (source)
  *
- * XXX autodeps are deprecated and should be removed at the earliest
- * opportunity
+ * When the clockdomain represented by @clkdm2 wakes up, wake up
+ * @clkdm1. Implemented in hardware on the OMAP, this feature is
+ * designed to reduce wakeup latency of the dependent clockdomain @clkdm1.
+ * Returns -EINVAL if presented with invalid clockdomain pointers,
+ * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon
+ * success.
  */
-void _clkdm_add_autodeps(struct clockdomain *clkdm)
+static int _clkdm_add_wkdep(struct clockdomain *clkdm1,
+                           struct clockdomain *clkdm2)
 {
-       struct clkdm_autodep *autodep;
+       struct clkdm_dep *cd;
+       int ret = 0;
 
-       if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
-               return;
+       if (!clkdm1 || !clkdm2)
+               return -EINVAL;
 
-       for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
-               if (IS_ERR(autodep->clkdm.ptr))
-                       continue;
+       cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
+       if (IS_ERR(cd))
+               ret = PTR_ERR(cd);
 
-               pr_debug("clockdomain: %s: adding %s sleepdep/wkdep\n",
-                        clkdm->name, autodep->clkdm.ptr->name);
+       if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep)
+               ret = -EINVAL;
+
+       if (ret) {
+               pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
+                        clkdm1->name, clkdm2->name);
+               return ret;
+       }
+
+       cd->wkdep_usecount++;
+       if (cd->wkdep_usecount == 1) {
+               pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n",
+                        clkdm1->name, clkdm2->name);
 
-               clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr);
-               clkdm_add_wkdep(clkdm, autodep->clkdm.ptr);
+               ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2);
        }
+
+       return ret;
 }
 
-/*
- * _clkdm_add_autodeps - remove auto sleepdeps/wkdeps from clkdm
- * @clkdm: struct clockdomain *
+/**
+ * _clkdm_del_wkdep - remove a wakeup dep from clkdm2 to clkdm1 (lockless)
+ * @clkdm1: wake this struct clockdomain * up (dependent)
+ * @clkdm2: when this struct clockdomain * wakes up (source)
  *
- * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm'
- * in hardware-supervised mode.  Meant to be called from clock framework
- * when a clock inside clockdomain 'clkdm' is disabled.  No return value.
+ * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2
+ * wakes up.  Returns -EINVAL if presented with invalid clockdomain
+ * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or
+ * 0 upon success.
+ */
+static int _clkdm_del_wkdep(struct clockdomain *clkdm1,
+                           struct clockdomain *clkdm2)
+{
+       struct clkdm_dep *cd;
+       int ret = 0;
+
+       if (!clkdm1 || !clkdm2)
+               return -EINVAL;
+
+       cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
+       if (IS_ERR(cd))
+               ret = PTR_ERR(cd);
+
+       if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep)
+               ret = -EINVAL;
+
+       if (ret) {
+               pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
+                        clkdm1->name, clkdm2->name);
+               return ret;
+       }
+
+       cd->wkdep_usecount--;
+       if (cd->wkdep_usecount == 0) {
+               pr_debug("clockdomain: hardware will no longer wake up %s after %s wakes up\n",
+                        clkdm1->name, clkdm2->name);
+
+               ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2);
+       }
+
+       return ret;
+}
+
+/**
+ * _clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 (lockless)
+ * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
+ * @clkdm2: when this struct clockdomain * is active (source)
  *
- * XXX autodeps are deprecated and should be removed at the earliest
- * opportunity
+ * Prevent @clkdm1 from automatically going inactive (and then to
+ * retention or off) if @clkdm2 is active.  Returns -EINVAL if
+ * presented with invalid clockdomain pointers or called on a machine
+ * that does not support software-configurable hardware sleep
+ * dependencies, -ENOENT if the specified dependency cannot be set in
+ * hardware, or 0 upon success.
  */
-void _clkdm_del_autodeps(struct clockdomain *clkdm)
+static int _clkdm_add_sleepdep(struct clockdomain *clkdm1,
+                              struct clockdomain *clkdm2)
 {
-       struct clkdm_autodep *autodep;
+       struct clkdm_dep *cd;
+       int ret = 0;
 
-       if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
-               return;
+       if (!clkdm1 || !clkdm2)
+               return -EINVAL;
 
-       for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
-               if (IS_ERR(autodep->clkdm.ptr))
-                       continue;
+       cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
+       if (IS_ERR(cd))
+               ret = PTR_ERR(cd);
 
-               pr_debug("clockdomain: %s: removing %s sleepdep/wkdep\n",
-                        clkdm->name, autodep->clkdm.ptr->name);
+       if (!arch_clkdm || !arch_clkdm->clkdm_add_sleepdep)
+               ret = -EINVAL;
+
+       if (ret) {
+               pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
+                        clkdm1->name, clkdm2->name);
+               return ret;
+       }
 
-               clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr);
-               clkdm_del_wkdep(clkdm, autodep->clkdm.ptr);
+       cd->sleepdep_usecount++;
+       if (cd->sleepdep_usecount == 1) {
+               pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n",
+                        clkdm1->name, clkdm2->name);
+
+               ret = arch_clkdm->clkdm_add_sleepdep(clkdm1, clkdm2);
        }
+
+       return ret;
 }
 
 /**
- * _resolve_clkdm_deps() - resolve clkdm_names in @clkdm_deps to clkdms
- * @clkdm: clockdomain that we are resolving dependencies for
- * @clkdm_deps: ptr to array of struct clkdm_deps to resolve
+ * _clkdm_del_sleepdep - remove a sleep dep from clkdm2 to clkdm1 (lockless)
+ * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
+ * @clkdm2: when this struct clockdomain * is active (source)
  *
- * Iterates through @clkdm_deps, looking up the struct clockdomain named by
- * clkdm_name and storing the clockdomain pointer in the struct clkdm_dep.
- * No return value.
+ * Allow @clkdm1 to automatically go inactive (and then to retention or
+ * off), independent of the activity state of @clkdm2.  Returns -EINVAL
+ * if presented with invalid clockdomain pointers or called on a machine
+ * that does not support software-configurable hardware sleep dependencies,
+ * -ENOENT if the specified dependency cannot be cleared in hardware, or
+ * 0 upon success.
  */
-static void _resolve_clkdm_deps(struct clockdomain *clkdm,
-                               struct clkdm_dep *clkdm_deps)
+static int _clkdm_del_sleepdep(struct clockdomain *clkdm1,
+                              struct clockdomain *clkdm2)
 {
        struct clkdm_dep *cd;
+       int ret = 0;
 
-       for (cd = clkdm_deps; cd && cd->clkdm_name; cd++) {
-               if (cd->clkdm)
-                       continue;
-               cd->clkdm = _clkdm_lookup(cd->clkdm_name);
+       if (!clkdm1 || !clkdm2)
+               return -EINVAL;
 
-               WARN(!cd->clkdm, "clockdomain: %s: could not find clkdm %s while resolving dependencies - should never happen",
-                    clkdm->name, cd->clkdm_name);
+       cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
+       if (IS_ERR(cd))
+               ret = PTR_ERR(cd);
+
+       if (!arch_clkdm || !arch_clkdm->clkdm_del_sleepdep)
+               ret = -EINVAL;
+
+       if (ret) {
+               pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
+                        clkdm1->name, clkdm2->name);
+               return ret;
+       }
+
+       cd->sleepdep_usecount--;
+       if (cd->sleepdep_usecount == 0) {
+               pr_debug("clockdomain: will no longer prevent %s from sleeping if %s is active\n",
+                        clkdm1->name, clkdm2->name);
+
+               ret = arch_clkdm->clkdm_del_sleepdep(clkdm1, clkdm2);
        }
+
+       return ret;
 }
 
 /* Public functions */
@@ -456,30 +571,18 @@ struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
 int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
 {
        struct clkdm_dep *cd;
-       int ret = 0;
+       int ret;
 
        if (!clkdm1 || !clkdm2)
                return -EINVAL;
 
        cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
        if (IS_ERR(cd))
-               ret = PTR_ERR(cd);
+               return PTR_ERR(cd);
 
-       if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep)
-               ret = -EINVAL;
-
-       if (ret) {
-               pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
-                        clkdm1->name, clkdm2->name);
-               return ret;
-       }
-
-       if (atomic_inc_return(&cd->wkdep_usecount) == 1) {
-               pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n",
-                        clkdm1->name, clkdm2->name);
-
-               ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2);
-       }
+       pwrdm_lock(cd->clkdm->pwrdm.ptr);
+       ret = _clkdm_add_wkdep(clkdm1, clkdm2);
+       pwrdm_unlock(cd->clkdm->pwrdm.ptr);
 
        return ret;
 }
@@ -497,30 +600,18 @@ int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
 int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
 {
        struct clkdm_dep *cd;
-       int ret = 0;
+       int ret;
 
        if (!clkdm1 || !clkdm2)
                return -EINVAL;
 
        cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
        if (IS_ERR(cd))
-               ret = PTR_ERR(cd);
-
-       if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep)
-               ret = -EINVAL;
+               return PTR_ERR(cd);
 
-       if (ret) {
-               pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
-                        clkdm1->name, clkdm2->name);
-               return ret;
-       }
-
-       if (atomic_dec_return(&cd->wkdep_usecount) == 0) {
-               pr_debug("clockdomain: hardware will no longer wake up %s after %s wakes up\n",
-                        clkdm1->name, clkdm2->name);
-
-               ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2);
-       }
+       pwrdm_lock(cd->clkdm->pwrdm.ptr);
+       ret = _clkdm_del_wkdep(clkdm1, clkdm2);
+       pwrdm_unlock(cd->clkdm->pwrdm.ptr);
 
        return ret;
 }
@@ -560,7 +651,7 @@ int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
                return ret;
        }
 
-       /* XXX It's faster to return the atomic wkdep_usecount */
+       /* XXX It's faster to return the wkdep_usecount */
        return arch_clkdm->clkdm_read_wkdep(clkdm1, clkdm2);
 }
 
@@ -600,30 +691,18 @@ int clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
 int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
 {
        struct clkdm_dep *cd;
-       int ret = 0;
+       int ret;
 
        if (!clkdm1 || !clkdm2)
                return -EINVAL;
 
-       cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
+       cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
        if (IS_ERR(cd))
-               ret = PTR_ERR(cd);
-
-       if (!arch_clkdm || !arch_clkdm->clkdm_add_sleepdep)
-               ret = -EINVAL;
-
-       if (ret) {
-               pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
-                        clkdm1->name, clkdm2->name);
-               return ret;
-       }
-
-       if (atomic_inc_return(&cd->sleepdep_usecount) == 1) {
-               pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n",
-                        clkdm1->name, clkdm2->name);
+               return PTR_ERR(cd);
 
-               ret = arch_clkdm->clkdm_add_sleepdep(clkdm1, clkdm2);
-       }
+       pwrdm_lock(cd->clkdm->pwrdm.ptr);
+       ret = _clkdm_add_sleepdep(clkdm1, clkdm2);
+       pwrdm_unlock(cd->clkdm->pwrdm.ptr);
 
        return ret;
 }
@@ -643,30 +722,18 @@ int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
 int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
 {
        struct clkdm_dep *cd;
-       int ret = 0;
+       int ret;
 
        if (!clkdm1 || !clkdm2)
                return -EINVAL;
 
-       cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
+       cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
        if (IS_ERR(cd))
-               ret = PTR_ERR(cd);
-
-       if (!arch_clkdm || !arch_clkdm->clkdm_del_sleepdep)
-               ret = -EINVAL;
-
-       if (ret) {
-               pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
-                        clkdm1->name, clkdm2->name);
-               return ret;
-       }
+               return PTR_ERR(cd);
 
-       if (atomic_dec_return(&cd->sleepdep_usecount) == 0) {
-               pr_debug("clockdomain: will no longer prevent %s from sleeping if %s is active\n",
-                        clkdm1->name, clkdm2->name);
-
-               ret = arch_clkdm->clkdm_del_sleepdep(clkdm1, clkdm2);
-       }
+       pwrdm_lock(cd->clkdm->pwrdm.ptr);
+       ret = _clkdm_del_sleepdep(clkdm1, clkdm2);
+       pwrdm_unlock(cd->clkdm->pwrdm.ptr);
 
        return ret;
 }
@@ -708,7 +775,7 @@ int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
                return ret;
        }
 
-       /* XXX It's faster to return the atomic sleepdep_usecount */
+       /* XXX It's faster to return the sleepdep_usecount */
        return arch_clkdm->clkdm_read_sleepdep(clkdm1, clkdm2);
 }
 
@@ -734,18 +801,17 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
 }
 
 /**
- * clkdm_sleep - force clockdomain sleep transition
+ * clkdm_sleep_nolock - force clockdomain sleep transition (lockless)
  * @clkdm: struct clockdomain *
  *
  * Instruct the CM to force a sleep transition on the specified
- * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if
- * clockdomain does not support software-initiated sleep; 0 upon
- * success.
+ * clockdomain @clkdm.  Only for use by the powerdomain code.  Returns
+ * -EINVAL if @clkdm is NULL or if clockdomain does not support
+ * software-initiated sleep; 0 upon success.
  */
-int clkdm_sleep(struct clockdomain *clkdm)
+int clkdm_sleep_nolock(struct clockdomain *clkdm)
 {
        int ret;
-       unsigned long flags;
 
        if (!clkdm)
                return -EINVAL;
@@ -761,26 +827,45 @@ int clkdm_sleep(struct clockdomain *clkdm)
 
        pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
 
-       spin_lock_irqsave(&clkdm->lock, flags);
        clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
        ret = arch_clkdm->clkdm_sleep(clkdm);
-       spin_unlock_irqrestore(&clkdm->lock, flags);
+       ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
+
        return ret;
 }
 
 /**
- * clkdm_wakeup - force clockdomain wakeup transition
+ * clkdm_sleep - force clockdomain sleep transition
  * @clkdm: struct clockdomain *
  *
- * Instruct the CM to force a wakeup transition on the specified
- * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if the
- * clockdomain does not support software-controlled wakeup; 0 upon
+ * Instruct the CM to force a sleep transition on the specified
+ * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if
+ * clockdomain does not support software-initiated sleep; 0 upon
  * success.
  */
-int clkdm_wakeup(struct clockdomain *clkdm)
+int clkdm_sleep(struct clockdomain *clkdm)
+{
+       int ret;
+
+       pwrdm_lock(clkdm->pwrdm.ptr);
+       ret = clkdm_sleep_nolock(clkdm);
+       pwrdm_unlock(clkdm->pwrdm.ptr);
+
+       return ret;
+}
+
+/**
+ * clkdm_wakeup_nolock - force clockdomain wakeup transition (lockless)
+ * @clkdm: struct clockdomain *
+ *
+ * Instruct the CM to force a wakeup transition on the specified
+ * clockdomain @clkdm.  Only for use by the powerdomain code.  Returns
+ * -EINVAL if @clkdm is NULL or if the clockdomain does not support
+ * software-controlled wakeup; 0 upon success.
+ */
+int clkdm_wakeup_nolock(struct clockdomain *clkdm)
 {
        int ret;
-       unsigned long flags;
 
        if (!clkdm)
                return -EINVAL;
@@ -796,28 +881,46 @@ int clkdm_wakeup(struct clockdomain *clkdm)
 
        pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
 
-       spin_lock_irqsave(&clkdm->lock, flags);
        clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
        ret = arch_clkdm->clkdm_wakeup(clkdm);
-       ret |= pwrdm_state_switch(clkdm->pwrdm.ptr);
-       spin_unlock_irqrestore(&clkdm->lock, flags);
+       ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
+
        return ret;
 }
 
 /**
- * clkdm_allow_idle - enable hwsup idle transitions for clkdm
+ * clkdm_wakeup - force clockdomain wakeup transition
  * @clkdm: struct clockdomain *
  *
- * Allow the hardware to automatically switch the clockdomain @clkdm into
- * active or idle states, as needed by downstream clocks.  If the
+ * Instruct the CM to force a wakeup transition on the specified
+ * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if the
+ * clockdomain does not support software-controlled wakeup; 0 upon
+ * success.
+ */
+int clkdm_wakeup(struct clockdomain *clkdm)
+{
+       int ret;
+
+       pwrdm_lock(clkdm->pwrdm.ptr);
+       ret = clkdm_wakeup_nolock(clkdm);
+       pwrdm_unlock(clkdm->pwrdm.ptr);
+
+       return ret;
+}
+
+/**
+ * clkdm_allow_idle_nolock - enable hwsup idle transitions for clkdm
+ * @clkdm: struct clockdomain *
+ *
+ * Allow the hardware to automatically switch the clockdomain @clkdm
+ * into active or idle states, as needed by downstream clocks.  If the
  * clockdomain has any downstream clocks enabled in the clock
  * framework, wkdep/sleepdep autodependencies are added; this is so
- * device drivers can read and write to the device.  No return value.
+ * device drivers can read and write to the device.  Only for use by
+ * the powerdomain code.  No return value.
  */
-void clkdm_allow_idle(struct clockdomain *clkdm)
+void clkdm_allow_idle_nolock(struct clockdomain *clkdm)
 {
-       unsigned long flags;
-
        if (!clkdm)
                return;
 
@@ -833,11 +936,26 @@ void clkdm_allow_idle(struct clockdomain *clkdm)
        pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
                 clkdm->name);
 
-       spin_lock_irqsave(&clkdm->lock, flags);
        clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED;
        arch_clkdm->clkdm_allow_idle(clkdm);
-       pwrdm_state_switch(clkdm->pwrdm.ptr);
-       spin_unlock_irqrestore(&clkdm->lock, flags);
+       pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
+}
+
+/**
+ * clkdm_allow_idle - enable hwsup idle transitions for clkdm
+ * @clkdm: struct clockdomain *
+ *
+ * Allow the hardware to automatically switch the clockdomain @clkdm into
+ * active or idle states, as needed by downstream clocks.  If the
+ * clockdomain has any downstream clocks enabled in the clock
+ * framework, wkdep/sleepdep autodependencies are added; this is so
+ * device drivers can read and write to the device.  No return value.
+ */
+void clkdm_allow_idle(struct clockdomain *clkdm)
+{
+       pwrdm_lock(clkdm->pwrdm.ptr);
+       clkdm_allow_idle_nolock(clkdm);
+       pwrdm_unlock(clkdm->pwrdm.ptr);
 }
 
 /**
@@ -847,12 +965,11 @@ void clkdm_allow_idle(struct clockdomain *clkdm)
  * Prevent the hardware from automatically switching the clockdomain
  * @clkdm into inactive or idle states.  If the clockdomain has
  * downstream clocks enabled in the clock framework, wkdep/sleepdep
- * autodependencies are removed.  No return value.
+ * autodependencies are removed.  Only for use by the powerdomain
+ * code.  No return value.
  */
-void clkdm_deny_idle(struct clockdomain *clkdm)
+void clkdm_deny_idle_nolock(struct clockdomain *clkdm)
 {
-       unsigned long flags;
-
        if (!clkdm)
                return;
 
@@ -868,11 +985,25 @@ void clkdm_deny_idle(struct clockdomain *clkdm)
        pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
                 clkdm->name);
 
-       spin_lock_irqsave(&clkdm->lock, flags);
        clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
        arch_clkdm->clkdm_deny_idle(clkdm);
-       pwrdm_state_switch(clkdm->pwrdm.ptr);
-       spin_unlock_irqrestore(&clkdm->lock, flags);
+       pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
+}
+
+/**
+ * clkdm_deny_idle - disable hwsup idle transitions for clkdm
+ * @clkdm: struct clockdomain *
+ *
+ * Prevent the hardware from automatically switching the clockdomain
+ * @clkdm into inactive or idle states.  If the clockdomain has
+ * downstream clocks enabled in the clock framework, wkdep/sleepdep
+ * autodependencies are removed.  No return value.
+ */
+void clkdm_deny_idle(struct clockdomain *clkdm)
+{
+       pwrdm_lock(clkdm->pwrdm.ptr);
+       clkdm_deny_idle_nolock(clkdm);
+       pwrdm_unlock(clkdm->pwrdm.ptr);
 }
 
 /**
@@ -889,14 +1020,11 @@ void clkdm_deny_idle(struct clockdomain *clkdm)
 bool clkdm_in_hwsup(struct clockdomain *clkdm)
 {
        bool ret;
-       unsigned long flags;
 
        if (!clkdm)
                return false;
 
-       spin_lock_irqsave(&clkdm->lock, flags);
        ret = (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false;
-       spin_unlock_irqrestore(&clkdm->lock, flags);
 
        return ret;
 }
@@ -918,30 +1046,91 @@ bool clkdm_missing_idle_reporting(struct clockdomain *clkdm)
        return (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) ? true : false;
 }
 
+/* Public autodep handling functions (deprecated) */
+
+/**
+ * clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable
+ * @clkdm: struct clockdomain *
+ *
+ * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm'
+ * in hardware-supervised mode.  Meant to be called from clock framework
+ * when a clock inside clockdomain 'clkdm' is enabled. No return value.
+ *
+ * XXX autodeps are deprecated and should be removed at the earliest
+ * opportunity
+ */
+void clkdm_add_autodeps(struct clockdomain *clkdm)
+{
+       struct clkdm_autodep *autodep;
+
+       if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
+               return;
+
+       for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
+               if (IS_ERR(autodep->clkdm.ptr))
+                       continue;
+
+               pr_debug("clockdomain: %s: adding %s sleepdep/wkdep\n",
+                        clkdm->name, autodep->clkdm.ptr->name);
+
+               _clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr);
+               _clkdm_add_wkdep(clkdm, autodep->clkdm.ptr);
+       }
+}
+
+/**
+ * clkdm_del_autodeps - remove auto sleepdeps/wkdeps from clkdm
+ * @clkdm: struct clockdomain *
+ *
+ * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm'
+ * in hardware-supervised mode.  Meant to be called from clock framework
+ * when a clock inside clockdomain 'clkdm' is disabled.  No return value.
+ *
+ * XXX autodeps are deprecated and should be removed at the earliest
+ * opportunity
+ */
+void clkdm_del_autodeps(struct clockdomain *clkdm)
+{
+       struct clkdm_autodep *autodep;
+
+       if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
+               return;
+
+       for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
+               if (IS_ERR(autodep->clkdm.ptr))
+                       continue;
+
+               pr_debug("clockdomain: %s: removing %s sleepdep/wkdep\n",
+                        clkdm->name, autodep->clkdm.ptr->name);
+
+               _clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr);
+               _clkdm_del_wkdep(clkdm, autodep->clkdm.ptr);
+       }
+}
+
 /* Clockdomain-to-clock/hwmod framework interface code */
 
 static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)
 {
-       unsigned long flags;
-
        if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable)
                return -EINVAL;
 
-       spin_lock_irqsave(&clkdm->lock, flags);
+       pwrdm_lock(clkdm->pwrdm.ptr);
 
        /*
         * For arch's with no autodeps, clkcm_clk_enable
         * should be called for every clock instance or hwmod that is
         * enabled, so the clkdm can be force woken up.
         */
-       if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps) {
-               spin_unlock_irqrestore(&clkdm->lock, flags);
+       clkdm->usecount++;
+       if (clkdm->usecount > 1 && autodeps) {
+               pwrdm_unlock(clkdm->pwrdm.ptr);
                return 0;
        }
 
        arch_clkdm->clkdm_clk_enable(clkdm);
-       pwrdm_state_switch(clkdm->pwrdm.ptr);
-       spin_unlock_irqrestore(&clkdm->lock, flags);
+       pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
+       pwrdm_unlock(clkdm->pwrdm.ptr);
 
        pr_debug("clockdomain: %s: enabled\n", clkdm->name);
 
@@ -990,36 +1179,34 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
  */
 int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
 {
-       unsigned long flags;
-
        if (!clkdm || !clk || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)
                return -EINVAL;
 
-       spin_lock_irqsave(&clkdm->lock, flags);
+       pwrdm_lock(clkdm->pwrdm.ptr);
 
        /* corner case: disabling unused clocks */
-       if ((__clk_get_enable_count(clk) == 0) &&
-           (atomic_read(&clkdm->usecount) == 0))
+       if ((__clk_get_enable_count(clk) == 0) && clkdm->usecount == 0)
                goto ccd_exit;
 
-       if (atomic_read(&clkdm->usecount) == 0) {
-               spin_unlock_irqrestore(&clkdm->lock, flags);
+       if (clkdm->usecount == 0) {
+               pwrdm_unlock(clkdm->pwrdm.ptr);
                WARN_ON(1); /* underflow */
                return -ERANGE;
        }
 
-       if (atomic_dec_return(&clkdm->usecount) > 0) {
-               spin_unlock_irqrestore(&clkdm->lock, flags);
+       clkdm->usecount--;
+       if (clkdm->usecount > 0) {
+               pwrdm_unlock(clkdm->pwrdm.ptr);
                return 0;
        }
 
        arch_clkdm->clkdm_clk_disable(clkdm);
-       pwrdm_state_switch(clkdm->pwrdm.ptr);
+       pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
 
        pr_debug("clockdomain: %s: disabled\n", clkdm->name);
 
 ccd_exit:
-       spin_unlock_irqrestore(&clkdm->lock, flags);
+       pwrdm_unlock(clkdm->pwrdm.ptr);
 
        return 0;
 }
@@ -1072,8 +1259,6 @@ int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh)
  */
 int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh)
 {
-       unsigned long flags;
-
        /* The clkdm attribute does not exist yet prior OMAP4 */
        if (cpu_is_omap24xx() || cpu_is_omap34xx())
                return 0;
@@ -1086,25 +1271,41 @@ int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh)
        if (!clkdm || !oh || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)
                return -EINVAL;
 
-       spin_lock_irqsave(&clkdm->lock, flags);
+       pwrdm_lock(clkdm->pwrdm.ptr);
 
-       if (atomic_read(&clkdm->usecount) == 0) {
-               spin_unlock_irqrestore(&clkdm->lock, flags);
+       if (clkdm->usecount == 0) {
+               pwrdm_unlock(clkdm->pwrdm.ptr);
                WARN_ON(1); /* underflow */
                return -ERANGE;
        }
 
-       if (atomic_dec_return(&clkdm->usecount) > 0) {
-               spin_unlock_irqrestore(&clkdm->lock, flags);
+       clkdm->usecount--;
+       if (clkdm->usecount > 0) {
+               pwrdm_unlock(clkdm->pwrdm.ptr);
                return 0;
        }
 
        arch_clkdm->clkdm_clk_disable(clkdm);
-       pwrdm_state_switch(clkdm->pwrdm.ptr);
-       spin_unlock_irqrestore(&clkdm->lock, flags);
+       pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
+       pwrdm_unlock(clkdm->pwrdm.ptr);
 
        pr_debug("clockdomain: %s: disabled\n", clkdm->name);
 
        return 0;
 }
 
+
+/* Clockdomain debugfs functions */
+
+int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *seq_file)
+{
+       struct seq_file *s = (struct seq_file *)seq_file;
+
+       if (!clkdm->flags)
+               return 0;
+
+       seq_printf(s, "%s->%s (%d)\n", clkdm->name, clkdm->pwrdm.ptr->name,
+                  clkdm->usecount);
+
+       return 0;
+}
\ No newline at end of file
index bc42446e23ab852fec7f5c268299b37160031351..aa800679c80a2f3fa224665ca87b016c924a6fc6 100644 (file)
@@ -15,7 +15,6 @@
 #define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H
 
 #include <linux/init.h>
-#include <linux/spinlock.h>
 
 #include "powerdomain.h"
 #include "clock.h"
@@ -92,8 +91,8 @@ struct clkdm_autodep {
 struct clkdm_dep {
        const char *clkdm_name;
        struct clockdomain *clkdm;
-       atomic_t wkdep_usecount;
-       atomic_t sleepdep_usecount;
+       s16 wkdep_usecount;
+       s16 sleepdep_usecount;
 };
 
 /* Possible flags for struct clockdomain._flags */
@@ -137,9 +136,8 @@ struct clockdomain {
        const u16 clkdm_offs;
        struct clkdm_dep *wkdep_srcs;
        struct clkdm_dep *sleepdep_srcs;
-       atomic_t usecount;
+       int usecount;
        struct list_head node;
-       spinlock_t lock;
 };
 
 /**
@@ -196,12 +194,16 @@ int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2);
 int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2);
 int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm);
 
+void clkdm_allow_idle_nolock(struct clockdomain *clkdm);
 void clkdm_allow_idle(struct clockdomain *clkdm);
+void clkdm_deny_idle_nolock(struct clockdomain *clkdm);
 void clkdm_deny_idle(struct clockdomain *clkdm);
 bool clkdm_in_hwsup(struct clockdomain *clkdm);
 bool clkdm_missing_idle_reporting(struct clockdomain *clkdm);
 
+int clkdm_wakeup_nolock(struct clockdomain *clkdm);
 int clkdm_wakeup(struct clockdomain *clkdm);
+int clkdm_sleep_nolock(struct clockdomain *clkdm);
 int clkdm_sleep(struct clockdomain *clkdm);
 
 int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk);
@@ -214,8 +216,10 @@ extern void __init omap243x_clockdomains_init(void);
 extern void __init omap3xxx_clockdomains_init(void);
 extern void __init am33xx_clockdomains_init(void);
 extern void __init omap44xx_clockdomains_init(void);
-extern void _clkdm_add_autodeps(struct clockdomain *clkdm);
-extern void _clkdm_del_autodeps(struct clockdomain *clkdm);
+extern void __init omap54xx_clockdomains_init(void);
+
+extern void clkdm_add_autodeps(struct clockdomain *clkdm);
+extern void clkdm_del_autodeps(struct clockdomain *clkdm);
 
 extern struct clkdm_ops omap2_clkdm_operations;
 extern struct clkdm_ops omap3_clkdm_operations;
@@ -226,4 +230,6 @@ extern struct clkdm_dep gfx_24xx_wkdeps[];
 extern struct clkdm_dep dsp_24xx_wkdeps[];
 extern struct clockdomain wkup_common_clkdm;
 
+extern int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *seq_file);
+
 #endif
diff --git a/arch/arm/mach-omap2/clockdomains54xx_data.c b/arch/arm/mach-omap2/clockdomains54xx_data.c
new file mode 100644 (file)
index 0000000..1a3c69d
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * OMAP54XX Clock domains framework
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ *
+ * Abhijit Pagare (abhijitpagare@ti.com)
+ * Benoit Cousson (b-cousson@ti.com)
+ * Paul Walmsley (paul@pwsan.com)
+ *
+ * This file is automatically generated from the OMAP hardware databases.
+ * We respectfully ask that any modifications to this file be coordinated
+ * with the public linux-omap@vger.kernel.org mailing list and the
+ * authors above to ensure that the autogeneration scripts are kept
+ * up-to-date with the file contents.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+#include "clockdomain.h"
+#include "cm1_54xx.h"
+#include "cm2_54xx.h"
+
+#include "cm-regbits-54xx.h"
+#include "prm54xx.h"
+#include "prcm44xx.h"
+#include "prcm_mpu54xx.h"
+
+/* Static Dependencies for OMAP4 Clock Domains */
+
+static struct clkdm_dep c2c_wkup_sleep_deps[] = {
+       { .clkdm_name = "abe_clkdm" },
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "iva_clkdm" },
+       { .clkdm_name = "l3init_clkdm" },
+       { .clkdm_name = "l3main1_clkdm" },
+       { .clkdm_name = "l3main2_clkdm" },
+       { .clkdm_name = "l4cfg_clkdm" },
+       { .clkdm_name = "l4per_clkdm" },
+       { NULL },
+};
+
+static struct clkdm_dep cam_wkup_sleep_deps[] = {
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "iva_clkdm" },
+       { .clkdm_name = "l3main1_clkdm" },
+       { NULL },
+};
+
+static struct clkdm_dep dma_wkup_sleep_deps[] = {
+       { .clkdm_name = "abe_clkdm" },
+       { .clkdm_name = "dss_clkdm" },
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "ipu_clkdm" },
+       { .clkdm_name = "iva_clkdm" },
+       { .clkdm_name = "l3init_clkdm" },
+       { .clkdm_name = "l3main1_clkdm" },
+       { .clkdm_name = "l4cfg_clkdm" },
+       { .clkdm_name = "l4per_clkdm" },
+       { .clkdm_name = "l4sec_clkdm" },
+       { .clkdm_name = "wkupaon_clkdm" },
+       { NULL },
+};
+
+static struct clkdm_dep dsp_wkup_sleep_deps[] = {
+       { .clkdm_name = "abe_clkdm" },
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "iva_clkdm" },
+       { .clkdm_name = "l3init_clkdm" },
+       { .clkdm_name = "l3main1_clkdm" },
+       { .clkdm_name = "l3main2_clkdm" },
+       { .clkdm_name = "l4cfg_clkdm" },
+       { .clkdm_name = "l4per_clkdm" },
+       { .clkdm_name = "wkupaon_clkdm" },
+       { NULL },
+};
+
+static struct clkdm_dep dss_wkup_sleep_deps[] = {
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "iva_clkdm" },
+       { .clkdm_name = "l3main2_clkdm" },
+       { NULL },
+};
+
+static struct clkdm_dep gpu_wkup_sleep_deps[] = {
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "iva_clkdm" },
+       { .clkdm_name = "l3main1_clkdm" },
+       { NULL },
+};
+
+static struct clkdm_dep ipu_wkup_sleep_deps[] = {
+       { .clkdm_name = "abe_clkdm" },
+       { .clkdm_name = "dsp_clkdm" },
+       { .clkdm_name = "dss_clkdm" },
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "gpu_clkdm" },
+       { .clkdm_name = "iva_clkdm" },
+       { .clkdm_name = "l3init_clkdm" },
+       { .clkdm_name = "l3main1_clkdm" },
+       { .clkdm_name = "l3main2_clkdm" },
+       { .clkdm_name = "l4cfg_clkdm" },
+       { .clkdm_name = "l4per_clkdm" },
+       { .clkdm_name = "l4sec_clkdm" },
+       { .clkdm_name = "wkupaon_clkdm" },
+       { NULL },
+};
+
+static struct clkdm_dep iva_wkup_sleep_deps[] = {
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "l3main1_clkdm" },
+       { NULL },
+};
+
+static struct clkdm_dep l3init_wkup_sleep_deps[] = {
+       { .clkdm_name = "abe_clkdm" },
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "iva_clkdm" },
+       { .clkdm_name = "l4cfg_clkdm" },
+       { .clkdm_name = "l4per_clkdm" },
+       { .clkdm_name = "l4sec_clkdm" },
+       { .clkdm_name = "wkupaon_clkdm" },
+       { NULL },
+};
+
+static struct clkdm_dep l4sec_wkup_sleep_deps[] = {
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "l3main1_clkdm" },
+       { .clkdm_name = "l4per_clkdm" },
+       { NULL },
+};
+
+static struct clkdm_dep mipiext_wkup_sleep_deps[] = {
+       { .clkdm_name = "abe_clkdm" },
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "iva_clkdm" },
+       { .clkdm_name = "l3init_clkdm" },
+       { .clkdm_name = "l3main1_clkdm" },
+       { .clkdm_name = "l3main2_clkdm" },
+       { .clkdm_name = "l4cfg_clkdm" },
+       { .clkdm_name = "l4per_clkdm" },
+       { NULL },
+};
+
+static struct clkdm_dep mpu_wkup_sleep_deps[] = {
+       { .clkdm_name = "abe_clkdm" },
+       { .clkdm_name = "dsp_clkdm" },
+       { .clkdm_name = "dss_clkdm" },
+       { .clkdm_name = "emif_clkdm" },
+       { .clkdm_name = "gpu_clkdm" },
+       { .clkdm_name = "ipu_clkdm" },
+       { .clkdm_name = "iva_clkdm" },
+       { .clkdm_name = "l3init_clkdm" },
+       { .clkdm_name = "l3main1_clkdm" },
+       { .clkdm_name = "l3main2_clkdm" },
+       { .clkdm_name = "l4cfg_clkdm" },
+       { .clkdm_name = "l4per_clkdm" },
+       { .clkdm_name = "l4sec_clkdm" },
+       { .clkdm_name = "wkupaon_clkdm" },
+       { NULL },
+};
+
+static struct clockdomain l4sec_54xx_clkdm = {
+       .name             = "l4sec_clkdm",
+       .pwrdm            = { .name = "core_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CORE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CORE_L4SEC_CDOFFS,
+       .dep_bit          = OMAP54XX_L4SEC_STATDEP_SHIFT,
+       .wkdep_srcs       = l4sec_wkup_sleep_deps,
+       .sleepdep_srcs    = l4sec_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_HWSUP_SWSUP,
+};
+
+static struct clockdomain iva_54xx_clkdm = {
+       .name             = "iva_clkdm",
+       .pwrdm            = { .name = "iva_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_IVA_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_IVA_IVA_CDOFFS,
+       .dep_bit          = OMAP54XX_IVA_STATDEP_SHIFT,
+       .wkdep_srcs       = iva_wkup_sleep_deps,
+       .sleepdep_srcs    = iva_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_HWSUP_SWSUP,
+};
+
+static struct clockdomain mipiext_54xx_clkdm = {
+       .name             = "mipiext_clkdm",
+       .pwrdm            = { .name = "core_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CORE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CORE_MIPIEXT_CDOFFS,
+       .wkdep_srcs       = mipiext_wkup_sleep_deps,
+       .sleepdep_srcs    = mipiext_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain l3main2_54xx_clkdm = {
+       .name             = "l3main2_clkdm",
+       .pwrdm            = { .name = "core_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CORE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CORE_L3MAIN2_CDOFFS,
+       .dep_bit          = OMAP54XX_L3MAIN2_STATDEP_SHIFT,
+       .flags            = CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain l3main1_54xx_clkdm = {
+       .name             = "l3main1_clkdm",
+       .pwrdm            = { .name = "core_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CORE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CORE_L3MAIN1_CDOFFS,
+       .dep_bit          = OMAP54XX_L3MAIN1_STATDEP_SHIFT,
+       .flags            = CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain custefuse_54xx_clkdm = {
+       .name             = "custefuse_clkdm",
+       .pwrdm            = { .name = "custefuse_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CUSTEFUSE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CUSTEFUSE_CUSTEFUSE_CDOFFS,
+       .flags            = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain ipu_54xx_clkdm = {
+       .name             = "ipu_clkdm",
+       .pwrdm            = { .name = "core_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CORE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CORE_IPU_CDOFFS,
+       .dep_bit          = OMAP54XX_IPU_STATDEP_SHIFT,
+       .wkdep_srcs       = ipu_wkup_sleep_deps,
+       .sleepdep_srcs    = ipu_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_HWSUP_SWSUP,
+};
+
+static struct clockdomain l4cfg_54xx_clkdm = {
+       .name             = "l4cfg_clkdm",
+       .pwrdm            = { .name = "core_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CORE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CORE_L4CFG_CDOFFS,
+       .dep_bit          = OMAP54XX_L4CFG_STATDEP_SHIFT,
+       .flags            = CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain abe_54xx_clkdm = {
+       .name             = "abe_clkdm",
+       .pwrdm            = { .name = "abe_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_AON_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_AON_ABE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_AON_ABE_ABE_CDOFFS,
+       .dep_bit          = OMAP54XX_ABE_STATDEP_SHIFT,
+       .flags            = CLKDM_CAN_HWSUP_SWSUP,
+};
+
+static struct clockdomain dss_54xx_clkdm = {
+       .name             = "dss_clkdm",
+       .pwrdm            = { .name = "dss_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_DSS_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_DSS_DSS_CDOFFS,
+       .dep_bit          = OMAP54XX_DSS_STATDEP_SHIFT,
+       .wkdep_srcs       = dss_wkup_sleep_deps,
+       .sleepdep_srcs    = dss_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_HWSUP_SWSUP,
+};
+
+static struct clockdomain dsp_54xx_clkdm = {
+       .name             = "dsp_clkdm",
+       .pwrdm            = { .name = "dsp_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_AON_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_AON_DSP_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_AON_DSP_DSP_CDOFFS,
+       .dep_bit          = OMAP54XX_DSP_STATDEP_SHIFT,
+       .wkdep_srcs       = dsp_wkup_sleep_deps,
+       .sleepdep_srcs    = dsp_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_HWSUP_SWSUP,
+};
+
+static struct clockdomain c2c_54xx_clkdm = {
+       .name             = "c2c_clkdm",
+       .pwrdm            = { .name = "core_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CORE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CORE_C2C_CDOFFS,
+       .wkdep_srcs       = c2c_wkup_sleep_deps,
+       .sleepdep_srcs    = c2c_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain l4per_54xx_clkdm = {
+       .name             = "l4per_clkdm",
+       .pwrdm            = { .name = "core_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CORE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CORE_L4PER_CDOFFS,
+       .dep_bit          = OMAP54XX_L4PER_STATDEP_SHIFT,
+       .flags            = CLKDM_CAN_HWSUP_SWSUP,
+};
+
+static struct clockdomain gpu_54xx_clkdm = {
+       .name             = "gpu_clkdm",
+       .pwrdm            = { .name = "gpu_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_GPU_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_GPU_GPU_CDOFFS,
+       .dep_bit          = OMAP54XX_GPU_STATDEP_SHIFT,
+       .wkdep_srcs       = gpu_wkup_sleep_deps,
+       .sleepdep_srcs    = gpu_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_HWSUP_SWSUP,
+};
+
+static struct clockdomain wkupaon_54xx_clkdm = {
+       .name             = "wkupaon_clkdm",
+       .pwrdm            = { .name = "wkupaon_pwrdm" },
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .cm_inst          = OMAP54XX_PRM_WKUPAON_CM_INST,
+       .clkdm_offs       = OMAP54XX_PRM_WKUPAON_CM_WKUPAON_CDOFFS,
+       .dep_bit          = OMAP54XX_WKUPAON_STATDEP_SHIFT,
+       .flags            = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain mpu0_54xx_clkdm = {
+       .name             = "mpu0_clkdm",
+       .pwrdm            = { .name = "cpu0_pwrdm" },
+       .prcm_partition   = OMAP54XX_PRCM_MPU_PARTITION,
+       .cm_inst          = OMAP54XX_PRCM_MPU_CM_C0_INST,
+       .clkdm_offs       = OMAP54XX_PRCM_MPU_CM_C0_CPU0_CDOFFS,
+       .flags            = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain mpu1_54xx_clkdm = {
+       .name             = "mpu1_clkdm",
+       .pwrdm            = { .name = "cpu1_pwrdm" },
+       .prcm_partition   = OMAP54XX_PRCM_MPU_PARTITION,
+       .cm_inst          = OMAP54XX_PRCM_MPU_CM_C1_INST,
+       .clkdm_offs       = OMAP54XX_PRCM_MPU_CM_C1_CPU1_CDOFFS,
+       .flags            = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain coreaon_54xx_clkdm = {
+       .name             = "coreaon_clkdm",
+       .pwrdm            = { .name = "coreaon_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_COREAON_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_COREAON_COREAON_CDOFFS,
+       .flags            = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain mpu_54xx_clkdm = {
+       .name             = "mpu_clkdm",
+       .pwrdm            = { .name = "mpu_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_AON_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_AON_MPU_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_AON_MPU_MPU_CDOFFS,
+       .wkdep_srcs       = mpu_wkup_sleep_deps,
+       .sleepdep_srcs    = mpu_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain l3init_54xx_clkdm = {
+       .name             = "l3init_clkdm",
+       .pwrdm            = { .name = "l3init_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_L3INIT_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_L3INIT_L3INIT_CDOFFS,
+       .dep_bit          = OMAP54XX_L3INIT_STATDEP_SHIFT,
+       .wkdep_srcs       = l3init_wkup_sleep_deps,
+       .sleepdep_srcs    = l3init_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_HWSUP_SWSUP,
+};
+
+static struct clockdomain dma_54xx_clkdm = {
+       .name             = "dma_clkdm",
+       .pwrdm            = { .name = "core_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CORE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CORE_DMA_CDOFFS,
+       .wkdep_srcs       = dma_wkup_sleep_deps,
+       .sleepdep_srcs    = dma_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain l3instr_54xx_clkdm = {
+       .name             = "l3instr_clkdm",
+       .pwrdm            = { .name = "core_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CORE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CORE_L3INSTR_CDOFFS,
+};
+
+static struct clockdomain emif_54xx_clkdm = {
+       .name             = "emif_clkdm",
+       .pwrdm            = { .name = "core_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CORE_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CORE_EMIF_CDOFFS,
+       .dep_bit          = OMAP54XX_EMIF_STATDEP_SHIFT,
+       .flags            = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain emu_54xx_clkdm = {
+       .name             = "emu_clkdm",
+       .pwrdm            = { .name = "emu_pwrdm" },
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .cm_inst          = OMAP54XX_PRM_EMU_CM_INST,
+       .clkdm_offs       = OMAP54XX_PRM_EMU_CM_EMU_CDOFFS,
+       .flags            = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+};
+
+static struct clockdomain cam_54xx_clkdm = {
+       .name             = "cam_clkdm",
+       .pwrdm            = { .name = "cam_pwrdm" },
+       .prcm_partition   = OMAP54XX_CM_CORE_PARTITION,
+       .cm_inst          = OMAP54XX_CM_CORE_CAM_INST,
+       .clkdm_offs       = OMAP54XX_CM_CORE_CAM_CAM_CDOFFS,
+       .wkdep_srcs       = cam_wkup_sleep_deps,
+       .sleepdep_srcs    = cam_wkup_sleep_deps,
+       .flags            = CLKDM_CAN_HWSUP_SWSUP,
+};
+
+/* As clockdomains are added or removed above, this list must also be changed */
+static struct clockdomain *clockdomains_omap54xx[] __initdata = {
+       &l4sec_54xx_clkdm,
+       &iva_54xx_clkdm,
+       &mipiext_54xx_clkdm,
+       &l3main2_54xx_clkdm,
+       &l3main1_54xx_clkdm,
+       &custefuse_54xx_clkdm,
+       &ipu_54xx_clkdm,
+       &l4cfg_54xx_clkdm,
+       &abe_54xx_clkdm,
+       &dss_54xx_clkdm,
+       &dsp_54xx_clkdm,
+       &c2c_54xx_clkdm,
+       &l4per_54xx_clkdm,
+       &gpu_54xx_clkdm,
+       &wkupaon_54xx_clkdm,
+       &mpu0_54xx_clkdm,
+       &mpu1_54xx_clkdm,
+       &coreaon_54xx_clkdm,
+       &mpu_54xx_clkdm,
+       &l3init_54xx_clkdm,
+       &dma_54xx_clkdm,
+       &l3instr_54xx_clkdm,
+       &emif_54xx_clkdm,
+       &emu_54xx_clkdm,
+       &cam_54xx_clkdm,
+       NULL
+};
+
+void __init omap54xx_clockdomains_init(void)
+{
+       clkdm_register_platform_funcs(&omap4_clkdm_operations);
+       clkdm_register_clkdms(clockdomains_omap54xx);
+       clkdm_complete_init();
+}
diff --git a/arch/arm/mach-omap2/cm-regbits-54xx.h b/arch/arm/mach-omap2/cm-regbits-54xx.h
new file mode 100644 (file)
index 0000000..e83b8e3
--- /dev/null
@@ -0,0 +1,1737 @@
+/*
+ * OMAP54xx Clock Management register bits
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Paul Walmsley (paul@pwsan.com)
+ * Rajendra Nayak (rnayak@ti.com)
+ * Benoit Cousson (b-cousson@ti.com)
+ *
+ * This file is automatically generated from the OMAP hardware databases.
+ * We respectfully ask that any modifications to this file be coordinated
+ * with the public linux-omap@vger.kernel.org mailing list and the
+ * authors above to ensure that the autogeneration scripts are kept
+ * up-to-date with the file contents.
+ *
+ * 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 __ARCH_ARM_MACH_OMAP2_CM_REGBITS_54XX_H
+#define __ARCH_ARM_MACH_OMAP2_CM_REGBITS_54XX_H
+
+/* Used by CM_DSP_DYNAMICDEP, CM_L3MAIN1_DYNAMICDEP, CM_MPU_DYNAMICDEP */
+#define OMAP54XX_ABE_DYNDEP_SHIFT                                      3
+#define OMAP54XX_ABE_DYNDEP_WIDTH                                      0x1
+#define OMAP54XX_ABE_DYNDEP_MASK                                       (1 << 3)
+
+/*
+ * Used by CM_C2C_STATICDEP, CM_DMA_STATICDEP, CM_DSP_STATICDEP,
+ * CM_IPU_STATICDEP, CM_L3INIT_STATICDEP, CM_MIPIEXT_STATICDEP, CM_MPU_STATICDEP
+ */
+#define OMAP54XX_ABE_STATDEP_SHIFT                                     3
+#define OMAP54XX_ABE_STATDEP_WIDTH                                     0x1
+#define OMAP54XX_ABE_STATDEP_MASK                                      (1 << 3)
+
+/*
+ * Used by CM_AUTOIDLE_DPLL_ABE, CM_AUTOIDLE_DPLL_CORE, CM_AUTOIDLE_DPLL_IVA,
+ * CM_AUTOIDLE_DPLL_MPU, CM_AUTOIDLE_DPLL_PER, CM_AUTOIDLE_DPLL_UNIPRO1,
+ * CM_AUTOIDLE_DPLL_UNIPRO2, CM_AUTOIDLE_DPLL_USB
+ */
+#define OMAP54XX_AUTO_DPLL_MODE_SHIFT                                  0
+#define OMAP54XX_AUTO_DPLL_MODE_WIDTH                                  0x3
+#define OMAP54XX_AUTO_DPLL_MODE_MASK                                   (0x7 << 0)
+
+/* Used by CM_L3MAIN2_DYNAMICDEP, CM_L4CFG_DYNAMICDEP */
+#define OMAP54XX_C2C_DYNDEP_SHIFT                                      18
+#define OMAP54XX_C2C_DYNDEP_WIDTH                                      0x1
+#define OMAP54XX_C2C_DYNDEP_MASK                                       (1 << 18)
+
+/* Used by CM_MPU_STATICDEP */
+#define OMAP54XX_C2C_STATDEP_SHIFT                                     18
+#define OMAP54XX_C2C_STATDEP_WIDTH                                     0x1
+#define OMAP54XX_C2C_STATDEP_MASK                                      (1 << 18)
+
+/* Used by CM_IPU_DYNAMICDEP, CM_L3MAIN2_DYNAMICDEP, CM_L4CFG_DYNAMICDEP */
+#define OMAP54XX_CAM_DYNDEP_SHIFT                                      9
+#define OMAP54XX_CAM_DYNDEP_WIDTH                                      0x1
+#define OMAP54XX_CAM_DYNDEP_MASK                                       (1 << 9)
+
+/*
+ * Used by CM_DMA_STATICDEP, CM_DSP_STATICDEP, CM_IPU_STATICDEP,
+ * CM_MPU_STATICDEP
+ */
+#define OMAP54XX_CAM_STATDEP_SHIFT                                     9
+#define OMAP54XX_CAM_STATDEP_WIDTH                                     0x1
+#define OMAP54XX_CAM_STATDEP_MASK                                      (1 << 9)
+
+/* Used by CM_ABE_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_ABE_24M_GFCLK_SHIFT                       13
+#define OMAP54XX_CLKACTIVITY_ABE_24M_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_ABE_24M_GFCLK_MASK                                (1 << 13)
+
+/* Used by CM_ABE_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_ABE_32K_CLK_SHIFT                         12
+#define OMAP54XX_CLKACTIVITY_ABE_32K_CLK_WIDTH                         0x1
+#define OMAP54XX_CLKACTIVITY_ABE_32K_CLK_MASK                          (1 << 12)
+
+/* Used by CM_ABE_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_ABE_GICLK_SHIFT                           9
+#define OMAP54XX_CLKACTIVITY_ABE_GICLK_WIDTH                           0x1
+#define OMAP54XX_CLKACTIVITY_ABE_GICLK_MASK                            (1 << 9)
+
+/* Used by CM_WKUPAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_ABE_LP_CLK_SHIFT                          9
+#define OMAP54XX_CLKACTIVITY_ABE_LP_CLK_WIDTH                          0x1
+#define OMAP54XX_CLKACTIVITY_ABE_LP_CLK_MASK                           (1 << 9)
+
+/* Used by CM_ABE_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_ABE_SYS_CLK_SHIFT                         11
+#define OMAP54XX_CLKACTIVITY_ABE_SYS_CLK_WIDTH                         0x1
+#define OMAP54XX_CLKACTIVITY_ABE_SYS_CLK_MASK                          (1 << 11)
+
+/* Used by CM_ABE_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_ABE_X2_CLK_SHIFT                          8
+#define OMAP54XX_CLKACTIVITY_ABE_X2_CLK_WIDTH                          0x1
+#define OMAP54XX_CLKACTIVITY_ABE_X2_CLK_MASK                           (1 << 8)
+
+/* Used by CM_DSS_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_BB2D_GFCLK_SHIFT                          13
+#define OMAP54XX_CLKACTIVITY_BB2D_GFCLK_WIDTH                          0x1
+#define OMAP54XX_CLKACTIVITY_BB2D_GFCLK_MASK                           (1 << 13)
+
+/* Used by CM_C2C_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_C2C_GFCLK_SHIFT                           9
+#define OMAP54XX_CLKACTIVITY_C2C_GFCLK_WIDTH                           0x1
+#define OMAP54XX_CLKACTIVITY_C2C_GFCLK_MASK                            (1 << 9)
+
+/* Used by CM_C2C_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_C2C_GICLK_SHIFT                           10
+#define OMAP54XX_CLKACTIVITY_C2C_GICLK_WIDTH                           0x1
+#define OMAP54XX_CLKACTIVITY_C2C_GICLK_MASK                            (1 << 10)
+
+/* Used by CM_C2C_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_C2C_L4_GICLK_SHIFT                                8
+#define OMAP54XX_CLKACTIVITY_C2C_L4_GICLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_C2C_L4_GICLK_MASK                         (1 << 8)
+
+/* Used by CM_CAM_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_CAM_BOOST_GCLK_SHIFT                      11
+#define OMAP54XX_CLKACTIVITY_CAM_BOOST_GCLK_WIDTH                      0x1
+#define OMAP54XX_CLKACTIVITY_CAM_BOOST_GCLK_MASK                       (1 << 11)
+
+/* Used by CM_CAM_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_CAM_GCLK_SHIFT                            8
+#define OMAP54XX_CLKACTIVITY_CAM_GCLK_WIDTH                            0x1
+#define OMAP54XX_CLKACTIVITY_CAM_GCLK_MASK                             (1 << 8)
+
+/* Used by CM_CAM_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_CAM_L3_GICLK_SHIFT                                12
+#define OMAP54XX_CLKACTIVITY_CAM_L3_GICLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_CAM_L3_GICLK_MASK                         (1 << 12)
+
+/* Used by CM_COREAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_COREAON_32K_GFCLK_SHIFT                   12
+#define OMAP54XX_CLKACTIVITY_COREAON_32K_GFCLK_WIDTH                   0x1
+#define OMAP54XX_CLKACTIVITY_COREAON_32K_GFCLK_MASK                    (1 << 12)
+
+/* Used by CM_COREAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_COREAON_IO_SRCOMP_GFCLK_SHIFT             14
+#define OMAP54XX_CLKACTIVITY_COREAON_IO_SRCOMP_GFCLK_WIDTH             0x1
+#define OMAP54XX_CLKACTIVITY_COREAON_IO_SRCOMP_GFCLK_MASK              (1 << 14)
+
+/* Used by CM_COREAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_COREAON_L4_GICLK_SHIFT                    8
+#define OMAP54XX_CLKACTIVITY_COREAON_L4_GICLK_WIDTH                    0x1
+#define OMAP54XX_CLKACTIVITY_COREAON_L4_GICLK_MASK                     (1 << 8)
+
+/* Used by CM_CAM_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_CSI_PHY_GFCLK_SHIFT                       9
+#define OMAP54XX_CLKACTIVITY_CSI_PHY_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_CSI_PHY_GFCLK_MASK                                (1 << 9)
+
+/* Used by CM_CUSTEFUSE_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_CUSTEFUSE_L4_GICLK_SHIFT                  8
+#define OMAP54XX_CLKACTIVITY_CUSTEFUSE_L4_GICLK_WIDTH                  0x1
+#define OMAP54XX_CLKACTIVITY_CUSTEFUSE_L4_GICLK_MASK                   (1 << 8)
+
+/* Used by CM_CUSTEFUSE_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_CUSTEFUSE_SYS_GFCLK_SHIFT                 9
+#define OMAP54XX_CLKACTIVITY_CUSTEFUSE_SYS_GFCLK_WIDTH                 0x1
+#define OMAP54XX_CLKACTIVITY_CUSTEFUSE_SYS_GFCLK_MASK                  (1 << 9)
+
+/* Used by CM_EMIF_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_DLL_GCLK_SHIFT                            9
+#define OMAP54XX_CLKACTIVITY_DLL_GCLK_WIDTH                            0x1
+#define OMAP54XX_CLKACTIVITY_DLL_GCLK_MASK                             (1 << 9)
+
+/* Used by CM_DMA_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_DMA_L3_GICLK_SHIFT                                8
+#define OMAP54XX_CLKACTIVITY_DMA_L3_GICLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_DMA_L3_GICLK_MASK                         (1 << 8)
+
+/* Used by CM_DSP_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_DSP_GCLK_SHIFT                            8
+#define OMAP54XX_CLKACTIVITY_DSP_GCLK_WIDTH                            0x1
+#define OMAP54XX_CLKACTIVITY_DSP_GCLK_MASK                             (1 << 8)
+
+/* Used by CM_DSS_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_DSS_GFCLK_SHIFT                           9
+#define OMAP54XX_CLKACTIVITY_DSS_GFCLK_WIDTH                           0x1
+#define OMAP54XX_CLKACTIVITY_DSS_GFCLK_MASK                            (1 << 9)
+
+/* Used by CM_DSS_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_DSS_L3_GICLK_SHIFT                                8
+#define OMAP54XX_CLKACTIVITY_DSS_L3_GICLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_DSS_L3_GICLK_MASK                         (1 << 8)
+
+/* Used by CM_DSS_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_DSS_SYS_GFCLK_SHIFT                       10
+#define OMAP54XX_CLKACTIVITY_DSS_SYS_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_DSS_SYS_GFCLK_MASK                                (1 << 10)
+
+/* Used by CM_EMIF_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_EMIF_L3_GICLK_SHIFT                       8
+#define OMAP54XX_CLKACTIVITY_EMIF_L3_GICLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_EMIF_L3_GICLK_MASK                                (1 << 8)
+
+/* Used by CM_EMIF_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_EMIF_LL_GCLK_SHIFT                                11
+#define OMAP54XX_CLKACTIVITY_EMIF_LL_GCLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_EMIF_LL_GCLK_MASK                         (1 << 11)
+
+/* Used by CM_EMIF_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_EMIF_PHY_GCLK_SHIFT                       10
+#define OMAP54XX_CLKACTIVITY_EMIF_PHY_GCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_EMIF_PHY_GCLK_MASK                                (1 << 10)
+
+/* Used by CM_EMU_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_EMU_SYS_GCLK_SHIFT                                8
+#define OMAP54XX_CLKACTIVITY_EMU_SYS_GCLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_EMU_SYS_GCLK_MASK                         (1 << 8)
+
+/* Used by CM_CAM_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_FDIF_GCLK_SHIFT                           10
+#define OMAP54XX_CLKACTIVITY_FDIF_GCLK_WIDTH                           0x1
+#define OMAP54XX_CLKACTIVITY_FDIF_GCLK_MASK                            (1 << 10)
+
+/* Used by CM_ABE_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_FUNC_24M_GFCLK_SHIFT                      10
+#define OMAP54XX_CLKACTIVITY_FUNC_24M_GFCLK_WIDTH                      0x1
+#define OMAP54XX_CLKACTIVITY_FUNC_24M_GFCLK_MASK                       (1 << 10)
+
+/* Used by CM_GPU_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_GPU_CORE_GCLK_SHIFT                       9
+#define OMAP54XX_CLKACTIVITY_GPU_CORE_GCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_GPU_CORE_GCLK_MASK                                (1 << 9)
+
+/* Used by CM_GPU_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_GPU_HYD_GCLK_SHIFT                                10
+#define OMAP54XX_CLKACTIVITY_GPU_HYD_GCLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_GPU_HYD_GCLK_MASK                         (1 << 10)
+
+/* Used by CM_GPU_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_GPU_SYS_GCLK_SHIFT                                8
+#define OMAP54XX_CLKACTIVITY_GPU_SYS_GCLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_GPU_SYS_GCLK_MASK                         (1 << 8)
+
+/* Used by CM_DSS_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_HDMI_CEC_GFCLK_SHIFT                      12
+#define OMAP54XX_CLKACTIVITY_HDMI_CEC_GFCLK_WIDTH                      0x1
+#define OMAP54XX_CLKACTIVITY_HDMI_CEC_GFCLK_MASK                       (1 << 12)
+
+/* Used by CM_DSS_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_HDMI_PHY_GFCLK_SHIFT                      11
+#define OMAP54XX_CLKACTIVITY_HDMI_PHY_GFCLK_WIDTH                      0x1
+#define OMAP54XX_CLKACTIVITY_HDMI_PHY_GFCLK_MASK                       (1 << 11)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_HSIC_P1_480M_GFCLK_SHIFT                  20
+#define OMAP54XX_CLKACTIVITY_HSIC_P1_480M_GFCLK_WIDTH                  0x1
+#define OMAP54XX_CLKACTIVITY_HSIC_P1_480M_GFCLK_MASK                   (1 << 20)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_HSIC_P1_GFCLK_SHIFT                       26
+#define OMAP54XX_CLKACTIVITY_HSIC_P1_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_HSIC_P1_GFCLK_MASK                                (1 << 26)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_HSIC_P2_480M_GFCLK_SHIFT                  21
+#define OMAP54XX_CLKACTIVITY_HSIC_P2_480M_GFCLK_WIDTH                  0x1
+#define OMAP54XX_CLKACTIVITY_HSIC_P2_480M_GFCLK_MASK                   (1 << 21)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_HSIC_P2_GFCLK_SHIFT                       27
+#define OMAP54XX_CLKACTIVITY_HSIC_P2_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_HSIC_P2_GFCLK_MASK                                (1 << 27)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_HSIC_P3_480M_GFCLK_SHIFT                  6
+#define OMAP54XX_CLKACTIVITY_HSIC_P3_480M_GFCLK_WIDTH                  0x1
+#define OMAP54XX_CLKACTIVITY_HSIC_P3_480M_GFCLK_MASK                   (1 << 6)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_HSIC_P3_GFCLK_SHIFT                       7
+#define OMAP54XX_CLKACTIVITY_HSIC_P3_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_HSIC_P3_GFCLK_MASK                                (1 << 7)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_HSI_GFCLK_SHIFT                           16
+#define OMAP54XX_CLKACTIVITY_HSI_GFCLK_WIDTH                           0x1
+#define OMAP54XX_CLKACTIVITY_HSI_GFCLK_MASK                            (1 << 16)
+
+/* Used by CM_IPU_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_IPU_GCLK_SHIFT                            8
+#define OMAP54XX_CLKACTIVITY_IPU_GCLK_WIDTH                            0x1
+#define OMAP54XX_CLKACTIVITY_IPU_GCLK_MASK                             (1 << 8)
+
+/* Used by CM_IVA_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_IVA_GCLK_SHIFT                            8
+#define OMAP54XX_CLKACTIVITY_IVA_GCLK_WIDTH                            0x1
+#define OMAP54XX_CLKACTIVITY_IVA_GCLK_MASK                             (1 << 8)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L3INIT_48M_GFCLK_SHIFT                    12
+#define OMAP54XX_CLKACTIVITY_L3INIT_48M_GFCLK_WIDTH                    0x1
+#define OMAP54XX_CLKACTIVITY_L3INIT_48M_GFCLK_MASK                     (1 << 12)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L3INIT_60M_P1_GFCLK_SHIFT                 28
+#define OMAP54XX_CLKACTIVITY_L3INIT_60M_P1_GFCLK_WIDTH                 0x1
+#define OMAP54XX_CLKACTIVITY_L3INIT_60M_P1_GFCLK_MASK                  (1 << 28)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L3INIT_60M_P2_GFCLK_SHIFT                 29
+#define OMAP54XX_CLKACTIVITY_L3INIT_60M_P2_GFCLK_WIDTH                 0x1
+#define OMAP54XX_CLKACTIVITY_L3INIT_60M_P2_GFCLK_MASK                  (1 << 29)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L3INIT_L3_GICLK_SHIFT                     8
+#define OMAP54XX_CLKACTIVITY_L3INIT_L3_GICLK_WIDTH                     0x1
+#define OMAP54XX_CLKACTIVITY_L3INIT_L3_GICLK_MASK                      (1 << 8)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L3INIT_L4_GICLK_SHIFT                     9
+#define OMAP54XX_CLKACTIVITY_L3INIT_L4_GICLK_WIDTH                     0x1
+#define OMAP54XX_CLKACTIVITY_L3INIT_L4_GICLK_MASK                      (1 << 9)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L3INIT_USB_OTG_SS_LFPS_TX_GFCLK_SHIFT     11
+#define OMAP54XX_CLKACTIVITY_L3INIT_USB_OTG_SS_LFPS_TX_GFCLK_WIDTH     0x1
+#define OMAP54XX_CLKACTIVITY_L3INIT_USB_OTG_SS_LFPS_TX_GFCLK_MASK      (1 << 11)
+
+/* Used by CM_L3INSTR_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L3INSTR_DLL_AGING_GCLK_SHIFT              9
+#define OMAP54XX_CLKACTIVITY_L3INSTR_DLL_AGING_GCLK_WIDTH              0x1
+#define OMAP54XX_CLKACTIVITY_L3INSTR_DLL_AGING_GCLK_MASK               (1 << 9)
+
+/* Used by CM_L3INSTR_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L3INSTR_L3_GICLK_SHIFT                    8
+#define OMAP54XX_CLKACTIVITY_L3INSTR_L3_GICLK_WIDTH                    0x1
+#define OMAP54XX_CLKACTIVITY_L3INSTR_L3_GICLK_MASK                     (1 << 8)
+
+/* Used by CM_L3INSTR_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L3INSTR_TS_GCLK_SHIFT                     10
+#define OMAP54XX_CLKACTIVITY_L3INSTR_TS_GCLK_WIDTH                     0x1
+#define OMAP54XX_CLKACTIVITY_L3INSTR_TS_GCLK_MASK                      (1 << 10)
+
+/* Used by CM_L3MAIN1_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L3MAIN1_L3_GICLK_SHIFT                    8
+#define OMAP54XX_CLKACTIVITY_L3MAIN1_L3_GICLK_WIDTH                    0x1
+#define OMAP54XX_CLKACTIVITY_L3MAIN1_L3_GICLK_MASK                     (1 << 8)
+
+/* Used by CM_L3MAIN2_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L3MAIN2_L3_GICLK_SHIFT                    8
+#define OMAP54XX_CLKACTIVITY_L3MAIN2_L3_GICLK_WIDTH                    0x1
+#define OMAP54XX_CLKACTIVITY_L3MAIN2_L3_GICLK_MASK                     (1 << 8)
+
+/* Used by CM_L4CFG_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L4CFG_L4_GICLK_SHIFT                      8
+#define OMAP54XX_CLKACTIVITY_L4CFG_L4_GICLK_WIDTH                      0x1
+#define OMAP54XX_CLKACTIVITY_L4CFG_L4_GICLK_MASK                       (1 << 8)
+
+/* Used by CM_L4PER_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L4PER_L4_GICLK_SHIFT                      8
+#define OMAP54XX_CLKACTIVITY_L4PER_L4_GICLK_WIDTH                      0x1
+#define OMAP54XX_CLKACTIVITY_L4PER_L4_GICLK_MASK                       (1 << 8)
+
+/* Used by CM_L4SEC_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L4SEC_L3_GICLK_SHIFT                      8
+#define OMAP54XX_CLKACTIVITY_L4SEC_L3_GICLK_WIDTH                      0x1
+#define OMAP54XX_CLKACTIVITY_L4SEC_L3_GICLK_MASK                       (1 << 8)
+
+/* Used by CM_L4SEC_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_L4SEC_L4_GICLK_SHIFT                      9
+#define OMAP54XX_CLKACTIVITY_L4SEC_L4_GICLK_WIDTH                      0x1
+#define OMAP54XX_CLKACTIVITY_L4SEC_L4_GICLK_MASK                       (1 << 9)
+
+/* Used by CM_MIPIEXT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_MIPIEXT_L3_GICLK_SHIFT                    8
+#define OMAP54XX_CLKACTIVITY_MIPIEXT_L3_GICLK_WIDTH                    0x1
+#define OMAP54XX_CLKACTIVITY_MIPIEXT_L3_GICLK_MASK                     (1 << 8)
+
+/* Used by CM_MIPIEXT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_MIPIEXT_PHY_REF_GFCLK_SHIFT               11
+#define OMAP54XX_CLKACTIVITY_MIPIEXT_PHY_REF_GFCLK_WIDTH               0x1
+#define OMAP54XX_CLKACTIVITY_MIPIEXT_PHY_REF_GFCLK_MASK                        (1 << 11)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_MMC1_32K_GFCLK_SHIFT                      2
+#define OMAP54XX_CLKACTIVITY_MMC1_32K_GFCLK_WIDTH                      0x1
+#define OMAP54XX_CLKACTIVITY_MMC1_32K_GFCLK_MASK                       (1 << 2)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_MMC1_GFCLK_SHIFT                          17
+#define OMAP54XX_CLKACTIVITY_MMC1_GFCLK_WIDTH                          0x1
+#define OMAP54XX_CLKACTIVITY_MMC1_GFCLK_MASK                           (1 << 17)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_MMC2_GFCLK_SHIFT                          18
+#define OMAP54XX_CLKACTIVITY_MMC2_GFCLK_WIDTH                          0x1
+#define OMAP54XX_CLKACTIVITY_MMC2_GFCLK_MASK                           (1 << 18)
+
+/* Used by CM_MPU_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_MPU_GCLK_SHIFT                            8
+#define OMAP54XX_CLKACTIVITY_MPU_GCLK_WIDTH                            0x1
+#define OMAP54XX_CLKACTIVITY_MPU_GCLK_MASK                             (1 << 8)
+
+/* Used by CM_ABE_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_PAD_CLKS_SHIFT                            14
+#define OMAP54XX_CLKACTIVITY_PAD_CLKS_WIDTH                            0x1
+#define OMAP54XX_CLKACTIVITY_PAD_CLKS_MASK                             (1 << 14)
+
+/* Used by CM_ABE_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_PAD_SLIMBUS1_CLK_SHIFT                    15
+#define OMAP54XX_CLKACTIVITY_PAD_SLIMBUS1_CLK_WIDTH                    0x1
+#define OMAP54XX_CLKACTIVITY_PAD_SLIMBUS1_CLK_MASK                     (1 << 15)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_PAD_XCLK60MHSP1_SHIFT                     3
+#define OMAP54XX_CLKACTIVITY_PAD_XCLK60MHSP1_WIDTH                     0x1
+#define OMAP54XX_CLKACTIVITY_PAD_XCLK60MHSP1_MASK                      (1 << 3)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_PAD_XCLK60MHSP2_SHIFT                     4
+#define OMAP54XX_CLKACTIVITY_PAD_XCLK60MHSP2_WIDTH                     0x1
+#define OMAP54XX_CLKACTIVITY_PAD_XCLK60MHSP2_MASK                      (1 << 4)
+
+/* Used by CM_L4PER_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_PER_12M_GFCLK_SHIFT                       15
+#define OMAP54XX_CLKACTIVITY_PER_12M_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_PER_12M_GFCLK_MASK                                (1 << 15)
+
+/* Used by CM_L4PER_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_PER_32K_GFCLK_SHIFT                       17
+#define OMAP54XX_CLKACTIVITY_PER_32K_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_PER_32K_GFCLK_MASK                                (1 << 17)
+
+/* Used by CM_L4PER_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_PER_48M_GFCLK_SHIFT                       18
+#define OMAP54XX_CLKACTIVITY_PER_48M_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_PER_48M_GFCLK_MASK                                (1 << 18)
+
+/* Used by CM_L4PER_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_PER_96M_GFCLK_SHIFT                       19
+#define OMAP54XX_CLKACTIVITY_PER_96M_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_PER_96M_GFCLK_MASK                                (1 << 19)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_SATA_REF_GFCLK_SHIFT                      19
+#define OMAP54XX_CLKACTIVITY_SATA_REF_GFCLK_WIDTH                      0x1
+#define OMAP54XX_CLKACTIVITY_SATA_REF_GFCLK_MASK                       (1 << 19)
+
+/* Used by CM_COREAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_SR_CORE_SYS_GFCLK_SHIFT                   11
+#define OMAP54XX_CLKACTIVITY_SR_CORE_SYS_GFCLK_WIDTH                   0x1
+#define OMAP54XX_CLKACTIVITY_SR_CORE_SYS_GFCLK_MASK                    (1 << 11)
+
+/* Used by CM_COREAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_SR_MM_SYS_GFCLK_SHIFT                     10
+#define OMAP54XX_CLKACTIVITY_SR_MM_SYS_GFCLK_WIDTH                     0x1
+#define OMAP54XX_CLKACTIVITY_SR_MM_SYS_GFCLK_MASK                      (1 << 10)
+
+/* Used by CM_COREAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_SR_MPU_SYS_GFCLK_SHIFT                    9
+#define OMAP54XX_CLKACTIVITY_SR_MPU_SYS_GFCLK_WIDTH                    0x1
+#define OMAP54XX_CLKACTIVITY_SR_MPU_SYS_GFCLK_MASK                     (1 << 9)
+
+/* Used by CM_WKUPAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_SYS_CLK_SHIFT                             8
+#define OMAP54XX_CLKACTIVITY_SYS_CLK_WIDTH                             0x1
+#define OMAP54XX_CLKACTIVITY_SYS_CLK_MASK                              (1 << 8)
+
+/* Used by CM_WKUPAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_SYS_CLK_ALL_SHIFT                         15
+#define OMAP54XX_CLKACTIVITY_SYS_CLK_ALL_WIDTH                         0x1
+#define OMAP54XX_CLKACTIVITY_SYS_CLK_ALL_MASK                          (1 << 15)
+
+/* Used by CM_WKUPAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_SYS_CLK_FUNC_SHIFT                                14
+#define OMAP54XX_CLKACTIVITY_SYS_CLK_FUNC_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_SYS_CLK_FUNC_MASK                         (1 << 14)
+
+/* Used by CM_L4PER_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_TIMER10_GFCLK_SHIFT                       9
+#define OMAP54XX_CLKACTIVITY_TIMER10_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_TIMER10_GFCLK_MASK                                (1 << 9)
+
+/* Used by CM_L4PER_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_TIMER11_GFCLK_SHIFT                       10
+#define OMAP54XX_CLKACTIVITY_TIMER11_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_TIMER11_GFCLK_MASK                                (1 << 10)
+
+/* Used by CM_L4PER_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_TIMER2_GFCLK_SHIFT                                11
+#define OMAP54XX_CLKACTIVITY_TIMER2_GFCLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_TIMER2_GFCLK_MASK                         (1 << 11)
+
+/* Used by CM_L4PER_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_TIMER3_GFCLK_SHIFT                                12
+#define OMAP54XX_CLKACTIVITY_TIMER3_GFCLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_TIMER3_GFCLK_MASK                         (1 << 12)
+
+/* Used by CM_L4PER_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_TIMER4_GFCLK_SHIFT                                13
+#define OMAP54XX_CLKACTIVITY_TIMER4_GFCLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_TIMER4_GFCLK_MASK                         (1 << 13)
+
+/* Used by CM_L4PER_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_TIMER9_GFCLK_SHIFT                                14
+#define OMAP54XX_CLKACTIVITY_TIMER9_GFCLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_TIMER9_GFCLK_MASK                         (1 << 14)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_TLL_CH0_GFCLK_SHIFT                       22
+#define OMAP54XX_CLKACTIVITY_TLL_CH0_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_TLL_CH0_GFCLK_MASK                                (1 << 22)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_TLL_CH1_GFCLK_SHIFT                       23
+#define OMAP54XX_CLKACTIVITY_TLL_CH1_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_TLL_CH1_GFCLK_MASK                                (1 << 23)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_TLL_CH2_GFCLK_SHIFT                       24
+#define OMAP54XX_CLKACTIVITY_TLL_CH2_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_TLL_CH2_GFCLK_MASK                                (1 << 24)
+
+/* Used by CM_MIPIEXT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_UNIPRO1_DPLL_CLK_SHIFT                    10
+#define OMAP54XX_CLKACTIVITY_UNIPRO1_DPLL_CLK_WIDTH                    0x1
+#define OMAP54XX_CLKACTIVITY_UNIPRO1_DPLL_CLK_MASK                     (1 << 10)
+
+/* Used by CM_MIPIEXT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_UNIPRO1_PHY_GFCLK_SHIFT                   13
+#define OMAP54XX_CLKACTIVITY_UNIPRO1_PHY_GFCLK_WIDTH                   0x1
+#define OMAP54XX_CLKACTIVITY_UNIPRO1_PHY_GFCLK_MASK                    (1 << 13)
+
+/* Used by CM_MIPIEXT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_UNIPRO1_TXPHY_LS_GFCLK_SHIFT              12
+#define OMAP54XX_CLKACTIVITY_UNIPRO1_TXPHY_LS_GFCLK_WIDTH              0x1
+#define OMAP54XX_CLKACTIVITY_UNIPRO1_TXPHY_LS_GFCLK_MASK               (1 << 12)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_UNIPRO2_DPLL_CLK_SHIFT                    10
+#define OMAP54XX_CLKACTIVITY_UNIPRO2_DPLL_CLK_WIDTH                    0x1
+#define OMAP54XX_CLKACTIVITY_UNIPRO2_DPLL_CLK_MASK                     (1 << 10)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_UNIPRO2_PHY_GFCLK_SHIFT                   13
+#define OMAP54XX_CLKACTIVITY_UNIPRO2_PHY_GFCLK_WIDTH                   0x1
+#define OMAP54XX_CLKACTIVITY_UNIPRO2_PHY_GFCLK_MASK                    (1 << 13)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_UNIPRO2_PHY_REF_GFCLK_SHIFT               5
+#define OMAP54XX_CLKACTIVITY_UNIPRO2_PHY_REF_GFCLK_WIDTH               0x1
+#define OMAP54XX_CLKACTIVITY_UNIPRO2_PHY_REF_GFCLK_MASK                        (1 << 5)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_USB_DPLL_CLK_SHIFT                                14
+#define OMAP54XX_CLKACTIVITY_USB_DPLL_CLK_WIDTH                                0x1
+#define OMAP54XX_CLKACTIVITY_USB_DPLL_CLK_MASK                         (1 << 14)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_USB_DPLL_HS_CLK_SHIFT                     15
+#define OMAP54XX_CLKACTIVITY_USB_DPLL_HS_CLK_WIDTH                     0x1
+#define OMAP54XX_CLKACTIVITY_USB_DPLL_HS_CLK_MASK                      (1 << 15)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_USB_OTG_SS_REF_CLK_SHIFT                  31
+#define OMAP54XX_CLKACTIVITY_USB_OTG_SS_REF_CLK_WIDTH                  0x1
+#define OMAP54XX_CLKACTIVITY_USB_OTG_SS_REF_CLK_MASK                   (1 << 31)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_UTMI_P3_GFCLK_SHIFT                       30
+#define OMAP54XX_CLKACTIVITY_UTMI_P3_GFCLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_UTMI_P3_GFCLK_MASK                                (1 << 30)
+
+/* Used by CM_L3INIT_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_UTMI_ROOT_GFCLK_SHIFT                     25
+#define OMAP54XX_CLKACTIVITY_UTMI_ROOT_GFCLK_WIDTH                     0x1
+#define OMAP54XX_CLKACTIVITY_UTMI_ROOT_GFCLK_MASK                      (1 << 25)
+
+/* Used by CM_WKUPAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_WKUPAON_32K_GFCLK_SHIFT                   11
+#define OMAP54XX_CLKACTIVITY_WKUPAON_32K_GFCLK_WIDTH                   0x1
+#define OMAP54XX_CLKACTIVITY_WKUPAON_32K_GFCLK_MASK                    (1 << 11)
+
+/* Used by CM_WKUPAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_WKUPAON_GICLK_SHIFT                       12
+#define OMAP54XX_CLKACTIVITY_WKUPAON_GICLK_WIDTH                       0x1
+#define OMAP54XX_CLKACTIVITY_WKUPAON_GICLK_MASK                                (1 << 12)
+
+/* Used by CM_WKUPAON_CLKSTCTRL */
+#define OMAP54XX_CLKACTIVITY_WKUPAON_IO_SRCOMP_GFCLK_SHIFT             13
+#define OMAP54XX_CLKACTIVITY_WKUPAON_IO_SRCOMP_GFCLK_WIDTH             0x1
+#define OMAP54XX_CLKACTIVITY_WKUPAON_IO_SRCOMP_GFCLK_MASK              (1 << 13)
+
+/* Used by CM_COREAON_IO_SRCOMP_CLKCTRL, CM_WKUPAON_IO_SRCOMP_CLKCTRL */
+#define OMAP54XX_CLKEN_SRCOMP_FCLK_SHIFT                               8
+#define OMAP54XX_CLKEN_SRCOMP_FCLK_WIDTH                               0x1
+#define OMAP54XX_CLKEN_SRCOMP_FCLK_MASK                                        (1 << 8)
+
+/*
+ * Used by CM_ABE_TIMER5_CLKCTRL, CM_ABE_TIMER6_CLKCTRL, CM_ABE_TIMER7_CLKCTRL,
+ * CM_ABE_TIMER8_CLKCTRL, CM_L3INIT_HSI_CLKCTRL, CM_L4PER_TIMER10_CLKCTRL,
+ * CM_L4PER_TIMER11_CLKCTRL, CM_L4PER_TIMER2_CLKCTRL, CM_L4PER_TIMER3_CLKCTRL,
+ * CM_L4PER_TIMER4_CLKCTRL, CM_L4PER_TIMER9_CLKCTRL, CM_WKUPAON_TIMER1_CLKCTRL
+ */
+#define OMAP54XX_CLKSEL_SHIFT                                          24
+#define OMAP54XX_CLKSEL_WIDTH                                          0x1
+#define OMAP54XX_CLKSEL_MASK                                           (1 << 24)
+
+/*
+ * Renamed from CLKSEL Used by CM_CLKSEL_ABE_DSS_SYS, CM_CLKSEL_ABE_PLL_REF,
+ * CM_CLKSEL_USB_60MHZ, CM_CLKSEL_WKUPAON
+ */
+#define OMAP54XX_CLKSEL_0_0_SHIFT                                      0
+#define OMAP54XX_CLKSEL_0_0_WIDTH                                      0x1
+#define OMAP54XX_CLKSEL_0_0_MASK                                       (1 << 0)
+
+/* Renamed from CLKSEL Used by CM_BYPCLK_DPLL_IVA, CM_BYPCLK_DPLL_MPU */
+#define OMAP54XX_CLKSEL_0_1_SHIFT                                      0
+#define OMAP54XX_CLKSEL_0_1_WIDTH                                      0x2
+#define OMAP54XX_CLKSEL_0_1_MASK                                       (0x3 << 0)
+
+/* Renamed from CLKSEL Used by CM_L3INSTR_CTRL_MODULE_BANDGAP_CLKCTRL */
+#define OMAP54XX_CLKSEL_24_25_SHIFT                                    24
+#define OMAP54XX_CLKSEL_24_25_WIDTH                                    0x2
+#define OMAP54XX_CLKSEL_24_25_MASK                                     (0x3 << 24)
+
+/* Used by CM_MPU_MPU_CLKCTRL */
+#define OMAP54XX_CLKSEL_ABE_DIV_MODE_SHIFT                             26
+#define OMAP54XX_CLKSEL_ABE_DIV_MODE_WIDTH                             0x1
+#define OMAP54XX_CLKSEL_ABE_DIV_MODE_MASK                              (1 << 26)
+
+/* Used by CM_ABE_AESS_CLKCTRL */
+#define OMAP54XX_CLKSEL_AESS_FCLK_SHIFT                                        24
+#define OMAP54XX_CLKSEL_AESS_FCLK_WIDTH                                        0x1
+#define OMAP54XX_CLKSEL_AESS_FCLK_MASK                                 (1 << 24)
+
+/* Used by CM_L3INIT_MMC1_CLKCTRL, CM_L3INIT_MMC2_CLKCTRL */
+#define OMAP54XX_CLKSEL_DIV_SHIFT                                      25
+#define OMAP54XX_CLKSEL_DIV_WIDTH                                      0x1
+#define OMAP54XX_CLKSEL_DIV_MASK                                       (1 << 25)
+
+/* Used by CM_MPU_MPU_CLKCTRL */
+#define OMAP54XX_CLKSEL_EMIF_DIV_MODE_SHIFT                            24
+#define OMAP54XX_CLKSEL_EMIF_DIV_MODE_WIDTH                            0x2
+#define OMAP54XX_CLKSEL_EMIF_DIV_MODE_MASK                             (0x3 << 24)
+
+/* Used by CM_CAM_FDIF_CLKCTRL */
+#define OMAP54XX_CLKSEL_FCLK_SHIFT                                     24
+#define OMAP54XX_CLKSEL_FCLK_WIDTH                                     0x1
+#define OMAP54XX_CLKSEL_FCLK_MASK                                      (1 << 24)
+
+/* Used by CM_GPU_GPU_CLKCTRL */
+#define OMAP54XX_CLKSEL_GPU_CORE_GCLK_SHIFT                            24
+#define OMAP54XX_CLKSEL_GPU_CORE_GCLK_WIDTH                            0x1
+#define OMAP54XX_CLKSEL_GPU_CORE_GCLK_MASK                             (1 << 24)
+
+/* Used by CM_GPU_GPU_CLKCTRL */
+#define OMAP54XX_CLKSEL_GPU_HYD_GCLK_SHIFT                             25
+#define OMAP54XX_CLKSEL_GPU_HYD_GCLK_WIDTH                             0x1
+#define OMAP54XX_CLKSEL_GPU_HYD_GCLK_MASK                              (1 << 25)
+
+/* Used by CM_GPU_GPU_CLKCTRL */
+#define OMAP54XX_CLKSEL_GPU_SYS_CLK_SHIFT                              26
+#define OMAP54XX_CLKSEL_GPU_SYS_CLK_WIDTH                              0x1
+#define OMAP54XX_CLKSEL_GPU_SYS_CLK_MASK                               (1 << 26)
+
+/*
+ * Used by CM_ABE_DMIC_CLKCTRL, CM_ABE_MCASP_CLKCTRL, CM_ABE_MCBSP1_CLKCTRL,
+ * CM_ABE_MCBSP2_CLKCTRL, CM_ABE_MCBSP3_CLKCTRL
+ */
+#define OMAP54XX_CLKSEL_INTERNAL_SOURCE_SHIFT                          26
+#define OMAP54XX_CLKSEL_INTERNAL_SOURCE_WIDTH                          0x2
+#define OMAP54XX_CLKSEL_INTERNAL_SOURCE_MASK                           (0x3 << 26)
+
+/* Used by CM_CLKSEL_CORE */
+#define OMAP54XX_CLKSEL_L3_SHIFT                                       4
+#define OMAP54XX_CLKSEL_L3_WIDTH                                       0x1
+#define OMAP54XX_CLKSEL_L3_MASK                                                (1 << 4)
+
+/* Renamed from CLKSEL_L3 Used by CM_SHADOW_FREQ_CONFIG2 */
+#define OMAP54XX_CLKSEL_L3_1_1_SHIFT                                   1
+#define OMAP54XX_CLKSEL_L3_1_1_WIDTH                                   0x1
+#define OMAP54XX_CLKSEL_L3_1_1_MASK                                    (1 << 1)
+
+/* Used by CM_CLKSEL_CORE */
+#define OMAP54XX_CLKSEL_L4_SHIFT                                       8
+#define OMAP54XX_CLKSEL_L4_WIDTH                                       0x1
+#define OMAP54XX_CLKSEL_L4_MASK                                                (1 << 8)
+
+/* Used by CM_EMIF_EMIF1_CLKCTRL */
+#define OMAP54XX_CLKSEL_LL_SHIFT                                       24
+#define OMAP54XX_CLKSEL_LL_WIDTH                                       0x1
+#define OMAP54XX_CLKSEL_LL_MASK                                                (1 << 24)
+
+/* Used by CM_CLKSEL_ABE */
+#define OMAP54XX_CLKSEL_OPP_SHIFT                                      0
+#define OMAP54XX_CLKSEL_OPP_WIDTH                                      0x2
+#define OMAP54XX_CLKSEL_OPP_MASK                                       (0x3 << 0)
+
+/* Renamed from CLKSEL_OPP Used by CM_L3INIT_UNIPRO2_CLKCTRL */
+#define OMAP54XX_CLKSEL_OPP_24_24_SHIFT                                        24
+#define OMAP54XX_CLKSEL_OPP_24_24_WIDTH                                        0x1
+#define OMAP54XX_CLKSEL_OPP_24_24_MASK                                 (1 << 24)
+
+/*
+ * Used by CM_ABE_DMIC_CLKCTRL, CM_ABE_MCASP_CLKCTRL, CM_ABE_MCBSP1_CLKCTRL,
+ * CM_ABE_MCBSP2_CLKCTRL, CM_ABE_MCBSP3_CLKCTRL
+ */
+#define OMAP54XX_CLKSEL_SOURCE_SHIFT                                   24
+#define OMAP54XX_CLKSEL_SOURCE_WIDTH                                   0x2
+#define OMAP54XX_CLKSEL_SOURCE_MASK                                    (0x3 << 24)
+
+/*
+ * Renamed from CLKSEL_SOURCE Used by CM_L3INIT_MMC1_CLKCTRL,
+ * CM_L3INIT_MMC2_CLKCTRL
+ */
+#define OMAP54XX_CLKSEL_SOURCE_L3INIT_MMC1_SHIFT                       24
+#define OMAP54XX_CLKSEL_SOURCE_L3INIT_MMC1_WIDTH                       0x1
+#define OMAP54XX_CLKSEL_SOURCE_L3INIT_MMC1_MASK                                (1 << 24)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_CLKSEL_UTMI_P1_SHIFT                                  24
+#define OMAP54XX_CLKSEL_UTMI_P1_WIDTH                                  0x1
+#define OMAP54XX_CLKSEL_UTMI_P1_MASK                                   (1 << 24)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_CLKSEL_UTMI_P2_SHIFT                                  25
+#define OMAP54XX_CLKSEL_UTMI_P2_WIDTH                                  0x1
+#define OMAP54XX_CLKSEL_UTMI_P2_MASK                                   (1 << 25)
+
+/*
+ * Used by CM_DIV_H11_DPLL_CORE, CM_DIV_H11_DPLL_IVA, CM_DIV_H11_DPLL_PER,
+ * CM_DIV_H12_DPLL_CORE, CM_DIV_H12_DPLL_IVA, CM_DIV_H12_DPLL_PER,
+ * CM_DIV_H13_DPLL_CORE, CM_DIV_H13_DPLL_PER, CM_DIV_H14_DPLL_CORE,
+ * CM_DIV_H14_DPLL_PER, CM_DIV_H21_DPLL_CORE, CM_DIV_H22_DPLL_CORE,
+ * CM_DIV_H23_DPLL_CORE, CM_DIV_H24_DPLL_CORE, CM_DIV_M2_DPLL_ABE,
+ * CM_DIV_M2_DPLL_CORE, CM_DIV_M2_DPLL_MPU, CM_DIV_M2_DPLL_PER,
+ * CM_DIV_M2_DPLL_UNIPRO1, CM_DIV_M2_DPLL_UNIPRO2, CM_DIV_M2_DPLL_USB,
+ * CM_DIV_M3_DPLL_ABE, CM_DIV_M3_DPLL_CORE, CM_DIV_M3_DPLL_PER
+ */
+#define OMAP54XX_CLKST_SHIFT                                           9
+#define OMAP54XX_CLKST_WIDTH                                           0x1
+#define OMAP54XX_CLKST_MASK                                            (1 << 9)
+
+/*
+ * Used by CM_ABE_CLKSTCTRL, CM_C2C_CLKSTCTRL, CM_CAM_CLKSTCTRL,
+ * CM_COREAON_CLKSTCTRL, CM_CUSTEFUSE_CLKSTCTRL, CM_DMA_CLKSTCTRL,
+ * CM_DSP_CLKSTCTRL, CM_DSS_CLKSTCTRL, CM_EMIF_CLKSTCTRL, CM_EMU_CLKSTCTRL,
+ * CM_GPU_CLKSTCTRL, CM_IPU_CLKSTCTRL, CM_IVA_CLKSTCTRL, CM_L3INIT_CLKSTCTRL,
+ * CM_L3INSTR_CLKSTCTRL, CM_L3MAIN1_CLKSTCTRL, CM_L3MAIN2_CLKSTCTRL,
+ * CM_L4CFG_CLKSTCTRL, CM_L4PER_CLKSTCTRL, CM_L4SEC_CLKSTCTRL,
+ * CM_MIPIEXT_CLKSTCTRL, CM_MPU_CLKSTCTRL, CM_WKUPAON_CLKSTCTRL
+ */
+#define OMAP54XX_CLKTRCTRL_SHIFT                                       0
+#define OMAP54XX_CLKTRCTRL_WIDTH                                       0x2
+#define OMAP54XX_CLKTRCTRL_MASK                                                (0x3 << 0)
+
+/* Used by CM_DIV_M2_DPLL_ABE, CM_DIV_M2_DPLL_PER */
+#define OMAP54XX_CLKX2ST_SHIFT                                         11
+#define OMAP54XX_CLKX2ST_WIDTH                                         0x1
+#define OMAP54XX_CLKX2ST_MASK                                          (1 << 11)
+
+/* Used by CM_L4CFG_DYNAMICDEP */
+#define OMAP54XX_COREAON_DYNDEP_SHIFT                                  16
+#define OMAP54XX_COREAON_DYNDEP_WIDTH                                  0x1
+#define OMAP54XX_COREAON_DYNDEP_MASK                                   (1 << 16)
+
+/* Used by CM_DSP_STATICDEP, CM_IPU_STATICDEP, CM_MPU_STATICDEP */
+#define OMAP54XX_COREAON_STATDEP_SHIFT                                 16
+#define OMAP54XX_COREAON_STATDEP_WIDTH                                 0x1
+#define OMAP54XX_COREAON_STATDEP_MASK                                  (1 << 16)
+
+/* Used by CM_L4CFG_DYNAMICDEP */
+#define OMAP54XX_CUSTEFUSE_DYNDEP_SHIFT                                        17
+#define OMAP54XX_CUSTEFUSE_DYNDEP_WIDTH                                        0x1
+#define OMAP54XX_CUSTEFUSE_DYNDEP_MASK                                 (1 << 17)
+
+/* Used by CM_DSP_STATICDEP, CM_IPU_STATICDEP, CM_MPU_STATICDEP */
+#define OMAP54XX_CUSTEFUSE_STATDEP_SHIFT                               17
+#define OMAP54XX_CUSTEFUSE_STATDEP_WIDTH                               0x1
+#define OMAP54XX_CUSTEFUSE_STATDEP_MASK                                        (1 << 17)
+
+/* Used by REVISION_CM_CORE, REVISION_CM_CORE_AON */
+#define OMAP54XX_CUSTOM_SHIFT                                          6
+#define OMAP54XX_CUSTOM_WIDTH                                          0x2
+#define OMAP54XX_CUSTOM_MASK                                           (0x3 << 6)
+
+/*
+ * Used by CM_CLKSEL_DPLL_ABE, CM_CLKSEL_DPLL_CORE, CM_CLKSEL_DPLL_IVA,
+ * CM_CLKSEL_DPLL_MPU, CM_CLKSEL_DPLL_PER, CM_CLKSEL_DPLL_UNIPRO1,
+ * CM_CLKSEL_DPLL_UNIPRO2, CM_CLKSEL_DPLL_USB
+ */
+#define OMAP54XX_DCC_EN_SHIFT                                          22
+#define OMAP54XX_DCC_EN_WIDTH                                          0x1
+#define OMAP54XX_DCC_EN_MASK                                           (1 << 22)
+
+/*
+ * Used by CM_CORE_AON_DEBUG_CM_CORE_AON_FD_TRANS,
+ * CM_CORE_AON_DEBUG_DSS_FD_TRANS, CM_CORE_AON_DEBUG_EMIF_FD_TRANS,
+ * CM_CORE_AON_DEBUG_L4SEC_FD_TRANS
+ */
+#define OMAP54XX_CM_DEBUG_OUT_SHIFT                                    0
+#define OMAP54XX_CM_DEBUG_OUT_WIDTH                                    0xd
+#define OMAP54XX_CM_DEBUG_OUT_MASK                                     (0x1fff << 0)
+
+/*
+ * Renamed from DEBUG_OUT Used by CM_CORE_AON_DEBUG_ABE_FD_TRANS,
+ * CM_CORE_AON_DEBUG_L3INIT_FD_TRANS, CM_CORE_AON_DEBUG_L4PER_FD_TRANS
+ */
+#define OMAP54XX_DEBUG_OUT_0_31_SHIFT                                  0
+#define OMAP54XX_DEBUG_OUT_0_31_WIDTH                                  0x20
+#define OMAP54XX_DEBUG_OUT_0_31_MASK                                   (0xffffffff << 0)
+
+/*
+ * Renamed from DEBUG_OUT Used by CM_CORE_AON_DEBUG_C2C_FD_TRANS,
+ * CM_CORE_AON_DEBUG_COREAON_FD_TRANS, CM_CORE_AON_DEBUG_L4CFG_FD_TRANS
+ */
+#define OMAP54XX_DEBUG_OUT_0_8_SHIFT                                   0
+#define OMAP54XX_DEBUG_OUT_0_8_WIDTH                                   0x9
+#define OMAP54XX_DEBUG_OUT_0_8_MASK                                    (0x1ff << 0)
+
+/*
+ * Renamed from DEBUG_OUT Used by CM_CORE_AON_DEBUG_CUSTEFUSE_FD_TRANS,
+ * CM_CORE_AON_DEBUG_DMA_FD_TRANS, CM_CORE_AON_DEBUG_L3MAIN1_FD_TRANS
+ */
+#define OMAP54XX_DEBUG_OUT_0_4_SHIFT                                   0
+#define OMAP54XX_DEBUG_OUT_0_4_WIDTH                                   0x5
+#define OMAP54XX_DEBUG_OUT_0_4_MASK                                    (0x1f << 0)
+
+/*
+ * Renamed from DEBUG_OUT Used by CM_CORE_AON_DEBUG_DSP_FD_TRANS,
+ * CM_CORE_AON_DEBUG_IPU_FD_TRANS, CM_CORE_AON_DEBUG_MPU_FD_TRANS
+ */
+#define OMAP54XX_DEBUG_OUT_0_5_SHIFT                                   0
+#define OMAP54XX_DEBUG_OUT_0_5_WIDTH                                   0x6
+#define OMAP54XX_DEBUG_OUT_0_5_MASK                                    (0x3f << 0)
+
+/*
+ * Renamed from DEBUG_OUT Used by CM_CORE_AON_DEBUG_CAM_FD_TRANS,
+ * CM_CORE_AON_DEBUG_MIPIEXT_FD_TRANS
+ */
+#define OMAP54XX_DEBUG_OUT_0_10_SHIFT                                  0
+#define OMAP54XX_DEBUG_OUT_0_10_WIDTH                                  0xb
+#define OMAP54XX_DEBUG_OUT_0_10_MASK                                   (0x7ff << 0)
+
+/*
+ * Renamed from DEBUG_OUT Used by CM_CORE_AON_DEBUG_IVA_FD_TRANS,
+ * CM_CORE_AON_DEBUG_L3MAIN2_FD_TRANS
+ */
+#define OMAP54XX_DEBUG_OUT_0_6_SHIFT                                   0
+#define OMAP54XX_DEBUG_OUT_0_6_WIDTH                                   0x7
+#define OMAP54XX_DEBUG_OUT_0_6_MASK                                    (0x7f << 0)
+
+/* Renamed from DEBUG_OUT Used by CM_CORE_AON_DEBUG_ABE_FD_TRANS2 */
+#define OMAP54XX_DEBUG_OUT_0_19_SHIFT                                  0
+#define OMAP54XX_DEBUG_OUT_0_19_WIDTH                                  0x14
+#define OMAP54XX_DEBUG_OUT_0_19_MASK                                   (0xfffff << 0)
+
+/* Renamed from DEBUG_OUT Used by CM_CORE_AON_DEBUG_GPU_FD_TRANS */
+#define OMAP54XX_DEBUG_OUT_0_9_SHIFT                                   0
+#define OMAP54XX_DEBUG_OUT_0_9_WIDTH                                   0xa
+#define OMAP54XX_DEBUG_OUT_0_9_MASK                                    (0x3ff << 0)
+
+/* Renamed from DEBUG_OUT Used by CM_CORE_AON_DEBUG_L3INIT_FD_TRANS2 */
+#define OMAP54XX_DEBUG_OUT_0_26_SHIFT                                  0
+#define OMAP54XX_DEBUG_OUT_0_26_WIDTH                                  0x1b
+#define OMAP54XX_DEBUG_OUT_0_26_MASK                                   (0x7ffffff << 0)
+
+/* Renamed from DEBUG_OUT Used by CM_CORE_AON_DEBUG_L3INSTR_FD_TRANS */
+#define OMAP54XX_DEBUG_OUT_0_13_SHIFT                                  0
+#define OMAP54XX_DEBUG_OUT_0_13_WIDTH                                  0xe
+#define OMAP54XX_DEBUG_OUT_0_13_MASK                                   (0x3fff << 0)
+
+/* Renamed from DEBUG_OUT Used by CM_CORE_AON_DEBUG_L4PER_FD_TRANS2 */
+#define OMAP54XX_DEBUG_OUT_0_21_SHIFT                                  0
+#define OMAP54XX_DEBUG_OUT_0_21_WIDTH                                  0x16
+#define OMAP54XX_DEBUG_OUT_0_21_MASK                                   (0x3fffff << 0)
+
+/*
+ * Used by CM_SSC_DELTAMSTEP_DPLL_ABE, CM_SSC_DELTAMSTEP_DPLL_CORE,
+ * CM_SSC_DELTAMSTEP_DPLL_IVA, CM_SSC_DELTAMSTEP_DPLL_MPU,
+ * CM_SSC_DELTAMSTEP_DPLL_PER
+ */
+#define OMAP54XX_DELTAMSTEP_SHIFT                                      0
+#define OMAP54XX_DELTAMSTEP_WIDTH                                      0x14
+#define OMAP54XX_DELTAMSTEP_MASK                                       (0xfffff << 0)
+
+/*
+ * Renamed from DELTAMSTEP Used by CM_SSC_DELTAMSTEP_DPLL_UNIPRO1,
+ * CM_SSC_DELTAMSTEP_DPLL_UNIPRO2, CM_SSC_DELTAMSTEP_DPLL_USB
+ */
+#define OMAP54XX_DELTAMSTEP_0_20_SHIFT                                 0
+#define OMAP54XX_DELTAMSTEP_0_20_WIDTH                                 0x15
+#define OMAP54XX_DELTAMSTEP_0_20_MASK                                  (0x1fffff << 0)
+
+/*
+ * Used by CM_DIV_H11_DPLL_CORE, CM_DIV_H11_DPLL_IVA, CM_DIV_H11_DPLL_PER,
+ * CM_DIV_H12_DPLL_CORE, CM_DIV_H12_DPLL_IVA, CM_DIV_H12_DPLL_PER,
+ * CM_DIV_H13_DPLL_CORE, CM_DIV_H13_DPLL_PER, CM_DIV_H14_DPLL_CORE,
+ * CM_DIV_H14_DPLL_PER, CM_DIV_H21_DPLL_CORE, CM_DIV_H22_DPLL_CORE,
+ * CM_DIV_H23_DPLL_CORE, CM_DIV_H24_DPLL_CORE
+ */
+#define OMAP54XX_DIVHS_SHIFT                                           0
+#define OMAP54XX_DIVHS_WIDTH                                           0x6
+#define OMAP54XX_DIVHS_MASK                                            (0x3f << 0)
+
+/*
+ * Renamed from DIVHS Used by CM_DIV_M2_DPLL_ABE, CM_DIV_M2_DPLL_CORE,
+ * CM_DIV_M2_DPLL_MPU, CM_DIV_M2_DPLL_PER, CM_DIV_M3_DPLL_ABE,
+ * CM_DIV_M3_DPLL_CORE, CM_DIV_M3_DPLL_PER
+ */
+#define OMAP54XX_DIVHS_0_4_SHIFT                                       0
+#define OMAP54XX_DIVHS_0_4_WIDTH                                       0x5
+#define OMAP54XX_DIVHS_0_4_MASK                                                (0x1f << 0)
+
+/*
+ * Renamed from DIVHS Used by CM_DIV_M2_DPLL_UNIPRO1, CM_DIV_M2_DPLL_UNIPRO2,
+ * CM_DIV_M2_DPLL_USB
+ */
+#define OMAP54XX_DIVHS_0_6_SHIFT                                       0
+#define OMAP54XX_DIVHS_0_6_WIDTH                                       0x7
+#define OMAP54XX_DIVHS_0_6_MASK                                                (0x7f << 0)
+
+/* Used by CM_DLL_CTRL */
+#define OMAP54XX_DLL_OVERRIDE_SHIFT                                    0
+#define OMAP54XX_DLL_OVERRIDE_WIDTH                                    0x1
+#define OMAP54XX_DLL_OVERRIDE_MASK                                     (1 << 0)
+
+/* Renamed from DLL_OVERRIDE Used by CM_SHADOW_FREQ_CONFIG1 */
+#define OMAP54XX_DLL_OVERRIDE_2_2_SHIFT                                        2
+#define OMAP54XX_DLL_OVERRIDE_2_2_WIDTH                                        0x1
+#define OMAP54XX_DLL_OVERRIDE_2_2_MASK                                 (1 << 2)
+
+/* Used by CM_SHADOW_FREQ_CONFIG1 */
+#define OMAP54XX_DLL_RESET_SHIFT                                       3
+#define OMAP54XX_DLL_RESET_WIDTH                                       0x1
+#define OMAP54XX_DLL_RESET_MASK                                                (1 << 3)
+
+/*
+ * Used by CM_CLKSEL_DPLL_ABE, CM_CLKSEL_DPLL_CORE, CM_CLKSEL_DPLL_IVA,
+ * CM_CLKSEL_DPLL_MPU, CM_CLKSEL_DPLL_PER, CM_CLKSEL_DPLL_UNIPRO1,
+ * CM_CLKSEL_DPLL_UNIPRO2, CM_CLKSEL_DPLL_USB
+ */
+#define OMAP54XX_DPLL_BYP_CLKSEL_SHIFT                                 23
+#define OMAP54XX_DPLL_BYP_CLKSEL_WIDTH                                 0x1
+#define OMAP54XX_DPLL_BYP_CLKSEL_MASK                                  (1 << 23)
+
+/* Used by CM_CLKSEL_DPLL_CORE */
+#define OMAP54XX_DPLL_CLKOUTHIF_CLKSEL_SHIFT                           20
+#define OMAP54XX_DPLL_CLKOUTHIF_CLKSEL_WIDTH                           0x1
+#define OMAP54XX_DPLL_CLKOUTHIF_CLKSEL_MASK                            (1 << 20)
+
+/* Used by CM_SHADOW_FREQ_CONFIG1 */
+#define OMAP54XX_DPLL_CORE_DPLL_EN_SHIFT                               8
+#define OMAP54XX_DPLL_CORE_DPLL_EN_WIDTH                               0x3
+#define OMAP54XX_DPLL_CORE_DPLL_EN_MASK                                        (0x7 << 8)
+
+/* Used by CM_SHADOW_FREQ_CONFIG2 */
+#define OMAP54XX_DPLL_CORE_H12_DIV_SHIFT                               2
+#define OMAP54XX_DPLL_CORE_H12_DIV_WIDTH                               0x6
+#define OMAP54XX_DPLL_CORE_H12_DIV_MASK                                        (0x3f << 2)
+
+/* Used by CM_SHADOW_FREQ_CONFIG1 */
+#define OMAP54XX_DPLL_CORE_M2_DIV_SHIFT                                        11
+#define OMAP54XX_DPLL_CORE_M2_DIV_WIDTH                                        0x5
+#define OMAP54XX_DPLL_CORE_M2_DIV_MASK                                 (0x1f << 11)
+
+/*
+ * Used by CM_CLKSEL_DPLL_ABE, CM_CLKSEL_DPLL_CORE, CM_CLKSEL_DPLL_IVA,
+ * CM_CLKSEL_DPLL_MPU, CM_CLKSEL_DPLL_PER
+ */
+#define OMAP54XX_DPLL_DIV_SHIFT                                                0
+#define OMAP54XX_DPLL_DIV_WIDTH                                                0x7
+#define OMAP54XX_DPLL_DIV_MASK                                         (0x7f << 0)
+
+/*
+ * Renamed from DPLL_DIV Used by CM_CLKSEL_DPLL_UNIPRO1,
+ * CM_CLKSEL_DPLL_UNIPRO2, CM_CLKSEL_DPLL_USB
+ */
+#define OMAP54XX_DPLL_DIV_0_7_SHIFT                                    0
+#define OMAP54XX_DPLL_DIV_0_7_WIDTH                                    0x8
+#define OMAP54XX_DPLL_DIV_0_7_MASK                                     (0xff << 0)
+
+/*
+ * Used by CM_CLKMODE_DPLL_ABE, CM_CLKMODE_DPLL_CORE, CM_CLKMODE_DPLL_IVA,
+ * CM_CLKMODE_DPLL_MPU, CM_CLKMODE_DPLL_PER
+ */
+#define OMAP54XX_DPLL_DRIFTGUARD_EN_SHIFT                              8
+#define OMAP54XX_DPLL_DRIFTGUARD_EN_WIDTH                              0x1
+#define OMAP54XX_DPLL_DRIFTGUARD_EN_MASK                               (1 << 8)
+
+/*
+ * Used by CM_CLKMODE_DPLL_ABE, CM_CLKMODE_DPLL_CORE, CM_CLKMODE_DPLL_IVA,
+ * CM_CLKMODE_DPLL_MPU, CM_CLKMODE_DPLL_PER, CM_CLKMODE_DPLL_UNIPRO1,
+ * CM_CLKMODE_DPLL_UNIPRO2, CM_CLKMODE_DPLL_USB
+ */
+#define OMAP54XX_DPLL_EN_SHIFT                                         0
+#define OMAP54XX_DPLL_EN_WIDTH                                         0x3
+#define OMAP54XX_DPLL_EN_MASK                                          (0x7 << 0)
+
+/*
+ * Used by CM_CLKMODE_DPLL_ABE, CM_CLKMODE_DPLL_CORE, CM_CLKMODE_DPLL_IVA,
+ * CM_CLKMODE_DPLL_MPU, CM_CLKMODE_DPLL_PER
+ */
+#define OMAP54XX_DPLL_LPMODE_EN_SHIFT                                  10
+#define OMAP54XX_DPLL_LPMODE_EN_WIDTH                                  0x1
+#define OMAP54XX_DPLL_LPMODE_EN_MASK                                   (1 << 10)
+
+/*
+ * Used by CM_CLKSEL_DPLL_ABE, CM_CLKSEL_DPLL_CORE, CM_CLKSEL_DPLL_IVA,
+ * CM_CLKSEL_DPLL_MPU, CM_CLKSEL_DPLL_PER
+ */
+#define OMAP54XX_DPLL_MULT_SHIFT                                       8
+#define OMAP54XX_DPLL_MULT_WIDTH                                       0xb
+#define OMAP54XX_DPLL_MULT_MASK                                                (0x7ff << 8)
+
+/*
+ * Renamed from DPLL_MULT Used by CM_CLKSEL_DPLL_UNIPRO1,
+ * CM_CLKSEL_DPLL_UNIPRO2, CM_CLKSEL_DPLL_USB
+ */
+#define OMAP54XX_DPLL_MULT_UNIPRO1_SHIFT                               8
+#define OMAP54XX_DPLL_MULT_UNIPRO1_WIDTH                               0xc
+#define OMAP54XX_DPLL_MULT_UNIPRO1_MASK                                        (0xfff << 8)
+
+/*
+ * Used by CM_CLKMODE_DPLL_ABE, CM_CLKMODE_DPLL_CORE, CM_CLKMODE_DPLL_IVA,
+ * CM_CLKMODE_DPLL_MPU, CM_CLKMODE_DPLL_PER
+ */
+#define OMAP54XX_DPLL_REGM4XEN_SHIFT                                   11
+#define OMAP54XX_DPLL_REGM4XEN_WIDTH                                   0x1
+#define OMAP54XX_DPLL_REGM4XEN_MASK                                    (1 << 11)
+
+/* Used by CM_CLKSEL_DPLL_UNIPRO1, CM_CLKSEL_DPLL_UNIPRO2, CM_CLKSEL_DPLL_USB */
+#define OMAP54XX_DPLL_SD_DIV_SHIFT                                     24
+#define OMAP54XX_DPLL_SD_DIV_WIDTH                                     0x8
+#define OMAP54XX_DPLL_SD_DIV_MASK                                      (0xff << 24)
+
+/* Used by CM_CLKSEL_DPLL_UNIPRO1, CM_CLKSEL_DPLL_UNIPRO2, CM_CLKSEL_DPLL_USB */
+#define OMAP54XX_DPLL_SELFREQDCO_SHIFT                                 21
+#define OMAP54XX_DPLL_SELFREQDCO_WIDTH                                 0x1
+#define OMAP54XX_DPLL_SELFREQDCO_MASK                                  (1 << 21)
+
+/*
+ * Used by CM_CLKMODE_DPLL_ABE, CM_CLKMODE_DPLL_CORE, CM_CLKMODE_DPLL_IVA,
+ * CM_CLKMODE_DPLL_MPU, CM_CLKMODE_DPLL_PER, CM_CLKMODE_DPLL_UNIPRO1,
+ * CM_CLKMODE_DPLL_UNIPRO2, CM_CLKMODE_DPLL_USB
+ */
+#define OMAP54XX_DPLL_SSC_ACK_SHIFT                                    13
+#define OMAP54XX_DPLL_SSC_ACK_WIDTH                                    0x1
+#define OMAP54XX_DPLL_SSC_ACK_MASK                                     (1 << 13)
+
+/*
+ * Used by CM_CLKMODE_DPLL_ABE, CM_CLKMODE_DPLL_CORE, CM_CLKMODE_DPLL_IVA,
+ * CM_CLKMODE_DPLL_MPU, CM_CLKMODE_DPLL_PER, CM_CLKMODE_DPLL_UNIPRO1,
+ * CM_CLKMODE_DPLL_UNIPRO2, CM_CLKMODE_DPLL_USB
+ */
+#define OMAP54XX_DPLL_SSC_DOWNSPREAD_SHIFT                             14
+#define OMAP54XX_DPLL_SSC_DOWNSPREAD_WIDTH                             0x1
+#define OMAP54XX_DPLL_SSC_DOWNSPREAD_MASK                              (1 << 14)
+
+/*
+ * Used by CM_CLKMODE_DPLL_ABE, CM_CLKMODE_DPLL_CORE, CM_CLKMODE_DPLL_IVA,
+ * CM_CLKMODE_DPLL_MPU, CM_CLKMODE_DPLL_PER, CM_CLKMODE_DPLL_UNIPRO1,
+ * CM_CLKMODE_DPLL_UNIPRO2, CM_CLKMODE_DPLL_USB
+ */
+#define OMAP54XX_DPLL_SSC_EN_SHIFT                                     12
+#define OMAP54XX_DPLL_SSC_EN_WIDTH                                     0x1
+#define OMAP54XX_DPLL_SSC_EN_MASK                                      (1 << 12)
+
+/* Used by CM_L4CFG_DYNAMICDEP */
+#define OMAP54XX_DSP_DYNDEP_SHIFT                                      1
+#define OMAP54XX_DSP_DYNDEP_WIDTH                                      0x1
+#define OMAP54XX_DSP_DYNDEP_MASK                                       (1 << 1)
+
+/* Used by CM_IPU_STATICDEP, CM_MPU_STATICDEP */
+#define OMAP54XX_DSP_STATDEP_SHIFT                                     1
+#define OMAP54XX_DSP_STATDEP_WIDTH                                     0x1
+#define OMAP54XX_DSP_STATDEP_MASK                                      (1 << 1)
+
+/* Used by CM_L3MAIN2_DYNAMICDEP, CM_L4PER_DYNAMICDEP */
+#define OMAP54XX_DSS_DYNDEP_SHIFT                                      8
+#define OMAP54XX_DSS_DYNDEP_WIDTH                                      0x1
+#define OMAP54XX_DSS_DYNDEP_MASK                                       (1 << 8)
+
+/* Used by CM_DMA_STATICDEP, CM_IPU_STATICDEP, CM_MPU_STATICDEP */
+#define OMAP54XX_DSS_STATDEP_SHIFT                                     8
+#define OMAP54XX_DSS_STATDEP_WIDTH                                     0x1
+#define OMAP54XX_DSS_STATDEP_MASK                                      (1 << 8)
+
+/*
+ * Used by CM_C2C_DYNAMICDEP, CM_L3MAIN1_DYNAMICDEP, CM_L4CFG_DYNAMICDEP,
+ * CM_MIPIEXT_DYNAMICDEP, CM_MPU_DYNAMICDEP
+ */
+#define OMAP54XX_EMIF_DYNDEP_SHIFT                                     4
+#define OMAP54XX_EMIF_DYNDEP_WIDTH                                     0x1
+#define OMAP54XX_EMIF_DYNDEP_MASK                                      (1 << 4)
+
+/*
+ * Used by CM_C2C_STATICDEP, CM_CAM_STATICDEP, CM_DMA_STATICDEP,
+ * CM_DSP_STATICDEP, CM_DSS_STATICDEP, CM_GPU_STATICDEP, CM_IPU_STATICDEP,
+ * CM_IVA_STATICDEP, CM_L3INIT_STATICDEP, CM_L4SEC_STATICDEP,
+ * CM_MIPIEXT_STATICDEP, CM_MPU_STATICDEP
+ */
+#define OMAP54XX_EMIF_STATDEP_SHIFT                                    4
+#define OMAP54XX_EMIF_STATDEP_WIDTH                                    0x1
+#define OMAP54XX_EMIF_STATDEP_MASK                                     (1 << 4)
+
+/* Used by CM_SHADOW_FREQ_CONFIG1 */
+#define OMAP54XX_FREQ_UPDATE_SHIFT                                     0
+#define OMAP54XX_FREQ_UPDATE_WIDTH                                     0x1
+#define OMAP54XX_FREQ_UPDATE_MASK                                      (1 << 0)
+
+/* Used by REVISION_CM_CORE, REVISION_CM_CORE_AON */
+#define OMAP54XX_FUNC_SHIFT                                            16
+#define OMAP54XX_FUNC_WIDTH                                            0xc
+#define OMAP54XX_FUNC_MASK                                             (0xfff << 16)
+
+/* Used by CM_SHADOW_FREQ_CONFIG2 */
+#define OMAP54XX_GPMC_FREQ_UPDATE_SHIFT                                        0
+#define OMAP54XX_GPMC_FREQ_UPDATE_WIDTH                                        0x1
+#define OMAP54XX_GPMC_FREQ_UPDATE_MASK                                 (1 << 0)
+
+/* Used by CM_L3MAIN2_DYNAMICDEP */
+#define OMAP54XX_GPU_DYNDEP_SHIFT                                      10
+#define OMAP54XX_GPU_DYNDEP_WIDTH                                      0x1
+#define OMAP54XX_GPU_DYNDEP_MASK                                       (1 << 10)
+
+/* Used by CM_IPU_STATICDEP, CM_MPU_STATICDEP */
+#define OMAP54XX_GPU_STATDEP_SHIFT                                     10
+#define OMAP54XX_GPU_STATDEP_WIDTH                                     0x1
+#define OMAP54XX_GPU_STATDEP_MASK                                      (1 << 10)
+
+/*
+ * Used by CM_ABE_AESS_CLKCTRL, CM_ABE_DMIC_CLKCTRL, CM_ABE_L4_ABE_CLKCTRL,
+ * CM_ABE_MCASP_CLKCTRL, CM_ABE_MCBSP1_CLKCTRL, CM_ABE_MCBSP2_CLKCTRL,
+ * CM_ABE_MCBSP3_CLKCTRL, CM_ABE_MCPDM_CLKCTRL, CM_ABE_SLIMBUS1_CLKCTRL,
+ * CM_ABE_TIMER5_CLKCTRL, CM_ABE_TIMER6_CLKCTRL, CM_ABE_TIMER7_CLKCTRL,
+ * CM_ABE_TIMER8_CLKCTRL, CM_ABE_WD_TIMER3_CLKCTRL, CM_C2C_C2C_CLKCTRL,
+ * CM_C2C_C2C_OCP_FW_CLKCTRL, CM_C2C_MODEM_ICR_CLKCTRL, CM_CAM_CAL_CLKCTRL,
+ * CM_CAM_FDIF_CLKCTRL, CM_CAM_ISS_CLKCTRL, CM_CM_CORE_AON_PROFILING_CLKCTRL,
+ * CM_CM_CORE_PROFILING_CLKCTRL, CM_COREAON_SMARTREFLEX_CORE_CLKCTRL,
+ * CM_COREAON_SMARTREFLEX_MM_CLKCTRL, CM_COREAON_SMARTREFLEX_MPU_CLKCTRL,
+ * CM_CUSTEFUSE_EFUSE_CTRL_CUST_CLKCTRL, CM_DMA_DMA_SYSTEM_CLKCTRL,
+ * CM_DSP_DSP_CLKCTRL, CM_DSS_BB2D_CLKCTRL, CM_DSS_DSS_CLKCTRL,
+ * CM_EMIF_DMM_CLKCTRL, CM_EMIF_EMIF1_CLKCTRL, CM_EMIF_EMIF2_CLKCTRL,
+ * CM_EMIF_EMIF_OCP_FW_CLKCTRL, CM_EMU_DEBUGSS_CLKCTRL,
+ * CM_EMU_MPU_EMU_DBG_CLKCTRL, CM_GPU_GPU_CLKCTRL, CM_IPU_IPU_CLKCTRL,
+ * CM_IVA_IVA_CLKCTRL, CM_IVA_SL2_CLKCTRL, CM_L3INIT_HSI_CLKCTRL,
+ * CM_L3INIT_IEEE1500_2_OCP_CLKCTRL, CM_L3INIT_MMC1_CLKCTRL,
+ * CM_L3INIT_MMC2_CLKCTRL, CM_L3INIT_MPHY_UNIPRO2_CLKCTRL,
+ * CM_L3INIT_OCP2SCP1_CLKCTRL, CM_L3INIT_OCP2SCP3_CLKCTRL,
+ * CM_L3INIT_SATA_CLKCTRL, CM_L3INIT_UNIPRO2_CLKCTRL,
+ * CM_L3INIT_USB_HOST_HS_CLKCTRL, CM_L3INIT_USB_OTG_SS_CLKCTRL,
+ * CM_L3INIT_USB_TLL_HS_CLKCTRL, CM_L3INSTR_CTRL_MODULE_BANDGAP_CLKCTRL,
+ * CM_L3INSTR_DLL_AGING_CLKCTRL, CM_L3INSTR_L3_INSTR_CLKCTRL,
+ * CM_L3INSTR_L3_MAIN_3_CLKCTRL, CM_L3INSTR_OCP_WP_NOC_CLKCTRL,
+ * CM_L3MAIN1_L3_MAIN_1_CLKCTRL, CM_L3MAIN2_GPMC_CLKCTRL,
+ * CM_L3MAIN2_L3_MAIN_2_CLKCTRL, CM_L3MAIN2_OCMC_RAM_CLKCTRL,
+ * CM_L4CFG_L4_CFG_CLKCTRL, CM_L4CFG_MAILBOX_CLKCTRL,
+ * CM_L4CFG_OCP2SCP2_CLKCTRL, CM_L4CFG_SAR_ROM_CLKCTRL,
+ * CM_L4CFG_SPINLOCK_CLKCTRL, CM_L4PER_ELM_CLKCTRL, CM_L4PER_GPIO2_CLKCTRL,
+ * CM_L4PER_GPIO3_CLKCTRL, CM_L4PER_GPIO4_CLKCTRL, CM_L4PER_GPIO5_CLKCTRL,
+ * CM_L4PER_GPIO6_CLKCTRL, CM_L4PER_GPIO7_CLKCTRL, CM_L4PER_GPIO8_CLKCTRL,
+ * CM_L4PER_HDQ1W_CLKCTRL, CM_L4PER_I2C1_CLKCTRL, CM_L4PER_I2C2_CLKCTRL,
+ * CM_L4PER_I2C3_CLKCTRL, CM_L4PER_I2C4_CLKCTRL, CM_L4PER_I2C5_CLKCTRL,
+ * CM_L4PER_L4_PER_CLKCTRL, CM_L4PER_MCSPI1_CLKCTRL, CM_L4PER_MCSPI2_CLKCTRL,
+ * CM_L4PER_MCSPI3_CLKCTRL, CM_L4PER_MCSPI4_CLKCTRL, CM_L4PER_MMC3_CLKCTRL,
+ * CM_L4PER_MMC4_CLKCTRL, CM_L4PER_MMC5_CLKCTRL, CM_L4PER_TIMER10_CLKCTRL,
+ * CM_L4PER_TIMER11_CLKCTRL, CM_L4PER_TIMER2_CLKCTRL, CM_L4PER_TIMER3_CLKCTRL,
+ * CM_L4PER_TIMER4_CLKCTRL, CM_L4PER_TIMER9_CLKCTRL, CM_L4PER_UART1_CLKCTRL,
+ * CM_L4PER_UART2_CLKCTRL, CM_L4PER_UART3_CLKCTRL, CM_L4PER_UART4_CLKCTRL,
+ * CM_L4PER_UART5_CLKCTRL, CM_L4PER_UART6_CLKCTRL, CM_L4SEC_AES1_CLKCTRL,
+ * CM_L4SEC_AES2_CLKCTRL, CM_L4SEC_DES3DES_CLKCTRL,
+ * CM_L4SEC_DMA_CRYPTO_CLKCTRL, CM_L4SEC_FPKA_CLKCTRL, CM_L4SEC_RNG_CLKCTRL,
+ * CM_L4SEC_SHA2MD5_CLKCTRL, CM_MIPIEXT_LLI_CLKCTRL,
+ * CM_MIPIEXT_LLI_OCP_FW_CLKCTRL, CM_MIPIEXT_MPHY_CLKCTRL, CM_MPU_MPU_CLKCTRL,
+ * CM_MPU_MPU_MPU_DBG_CLKCTRL, CM_WKUPAON_COUNTER_32K_CLKCTRL,
+ * CM_WKUPAON_GPIO1_CLKCTRL, CM_WKUPAON_KBD_CLKCTRL,
+ * CM_WKUPAON_L4_WKUP_CLKCTRL, CM_WKUPAON_SAR_RAM_CLKCTRL,
+ * CM_WKUPAON_TIMER12_CLKCTRL, CM_WKUPAON_TIMER1_CLKCTRL,
+ * CM_WKUPAON_WD_TIMER1_CLKCTRL, CM_WKUPAON_WD_TIMER2_CLKCTRL
+ */
+#define OMAP54XX_IDLEST_SHIFT                                          16
+#define OMAP54XX_IDLEST_WIDTH                                          0x2
+#define OMAP54XX_IDLEST_MASK                                           (0x3 << 16)
+
+/* Used by CM_L3MAIN2_DYNAMICDEP */
+#define OMAP54XX_IPU_DYNDEP_SHIFT                                      0
+#define OMAP54XX_IPU_DYNDEP_WIDTH                                      0x1
+#define OMAP54XX_IPU_DYNDEP_MASK                                       (1 << 0)
+
+/* Used by CM_DMA_STATICDEP, CM_MPU_STATICDEP */
+#define OMAP54XX_IPU_STATDEP_SHIFT                                     0
+#define OMAP54XX_IPU_STATDEP_WIDTH                                     0x1
+#define OMAP54XX_IPU_STATDEP_MASK                                      (1 << 0)
+
+/* Used by CM_DSP_DYNAMICDEP, CM_L3MAIN2_DYNAMICDEP */
+#define OMAP54XX_IVA_DYNDEP_SHIFT                                      2
+#define OMAP54XX_IVA_DYNDEP_WIDTH                                      0x1
+#define OMAP54XX_IVA_DYNDEP_MASK                                       (1 << 2)
+
+/*
+ * Used by CM_C2C_STATICDEP, CM_CAM_STATICDEP, CM_DMA_STATICDEP,
+ * CM_DSP_STATICDEP, CM_DSS_STATICDEP, CM_GPU_STATICDEP, CM_IPU_STATICDEP,
+ * CM_L3INIT_STATICDEP, CM_MIPIEXT_STATICDEP, CM_MPU_STATICDEP
+ */
+#define OMAP54XX_IVA_STATDEP_SHIFT                                     2
+#define OMAP54XX_IVA_STATDEP_WIDTH                                     0x1
+#define OMAP54XX_IVA_STATDEP_MASK                                      (1 << 2)
+
+/* Used by CM_L4CFG_DYNAMICDEP, CM_L4PER_DYNAMICDEP */
+#define OMAP54XX_L3INIT_DYNDEP_SHIFT                                   7
+#define OMAP54XX_L3INIT_DYNDEP_WIDTH                                   0x1
+#define OMAP54XX_L3INIT_DYNDEP_MASK                                    (1 << 7)
+
+/*
+ * Used by CM_C2C_STATICDEP, CM_DMA_STATICDEP, CM_DSP_STATICDEP,
+ * CM_IPU_STATICDEP, CM_MIPIEXT_STATICDEP, CM_MPU_STATICDEP
+ */
+#define OMAP54XX_L3INIT_STATDEP_SHIFT                                  7
+#define OMAP54XX_L3INIT_STATDEP_WIDTH                                  0x1
+#define OMAP54XX_L3INIT_STATDEP_MASK                                   (1 << 7)
+
+/*
+ * Used by CM_DSP_DYNAMICDEP, CM_DSS_DYNAMICDEP, CM_L3INIT_DYNAMICDEP,
+ * CM_L3MAIN2_DYNAMICDEP, CM_L4CFG_DYNAMICDEP, CM_MPU_DYNAMICDEP
+ */
+#define OMAP54XX_L3MAIN1_DYNDEP_SHIFT                                  5
+#define OMAP54XX_L3MAIN1_DYNDEP_WIDTH                                  0x1
+#define OMAP54XX_L3MAIN1_DYNDEP_MASK                                   (1 << 5)
+
+/*
+ * Used by CM_C2C_STATICDEP, CM_CAM_STATICDEP, CM_DMA_STATICDEP,
+ * CM_DSP_STATICDEP, CM_DSS_STATICDEP, CM_GPU_STATICDEP, CM_IPU_STATICDEP,
+ * CM_IVA_STATICDEP, CM_L3INIT_STATICDEP, CM_L4SEC_STATICDEP,
+ * CM_MIPIEXT_STATICDEP, CM_MPU_STATICDEP
+ */
+#define OMAP54XX_L3MAIN1_STATDEP_SHIFT                                 5
+#define OMAP54XX_L3MAIN1_STATDEP_WIDTH                                 0x1
+#define OMAP54XX_L3MAIN1_STATDEP_MASK                                  (1 << 5)
+
+/*
+ * Used by CM_C2C_DYNAMICDEP, CM_CAM_DYNAMICDEP, CM_DMA_DYNAMICDEP,
+ * CM_DSS_DYNAMICDEP, CM_EMU_DYNAMICDEP, CM_GPU_DYNAMICDEP, CM_IPU_DYNAMICDEP,
+ * CM_IVA_DYNAMICDEP, CM_L3INIT_DYNAMICDEP, CM_L3MAIN1_DYNAMICDEP,
+ * CM_L4CFG_DYNAMICDEP, CM_L4SEC_DYNAMICDEP, CM_MIPIEXT_DYNAMICDEP
+ */
+#define OMAP54XX_L3MAIN2_DYNDEP_SHIFT                                  6
+#define OMAP54XX_L3MAIN2_DYNDEP_WIDTH                                  0x1
+#define OMAP54XX_L3MAIN2_DYNDEP_MASK                                   (1 << 6)
+
+/*
+ * Used by CM_C2C_STATICDEP, CM_CAM_STATICDEP, CM_DMA_STATICDEP,
+ * CM_DSP_STATICDEP, CM_DSS_STATICDEP, CM_GPU_STATICDEP, CM_IPU_STATICDEP,
+ * CM_IVA_STATICDEP, CM_L3INIT_STATICDEP, CM_L4SEC_STATICDEP,
+ * CM_MIPIEXT_STATICDEP, CM_MPU_STATICDEP
+ */
+#define OMAP54XX_L3MAIN2_STATDEP_SHIFT                                 6
+#define OMAP54XX_L3MAIN2_STATDEP_WIDTH                                 0x1
+#define OMAP54XX_L3MAIN2_STATDEP_MASK                                  (1 << 6)
+
+/* Used by CM_L3MAIN1_DYNAMICDEP */
+#define OMAP54XX_L4CFG_DYNDEP_SHIFT                                    12
+#define OMAP54XX_L4CFG_DYNDEP_WIDTH                                    0x1
+#define OMAP54XX_L4CFG_DYNDEP_MASK                                     (1 << 12)
+
+/*
+ * Used by CM_C2C_STATICDEP, CM_DMA_STATICDEP, CM_DSP_STATICDEP,
+ * CM_IPU_STATICDEP, CM_L3INIT_STATICDEP, CM_MIPIEXT_STATICDEP, CM_MPU_STATICDEP
+ */
+#define OMAP54XX_L4CFG_STATDEP_SHIFT                                   12
+#define OMAP54XX_L4CFG_STATDEP_WIDTH                                   0x1
+#define OMAP54XX_L4CFG_STATDEP_MASK                                    (1 << 12)
+
+/* Used by CM_L3MAIN2_DYNAMICDEP */
+#define OMAP54XX_L4PER_DYNDEP_SHIFT                                    13
+#define OMAP54XX_L4PER_DYNDEP_WIDTH                                    0x1
+#define OMAP54XX_L4PER_DYNDEP_MASK                                     (1 << 13)
+
+/*
+ * Used by CM_C2C_STATICDEP, CM_DMA_STATICDEP, CM_DSP_STATICDEP,
+ * CM_IPU_STATICDEP, CM_L3INIT_STATICDEP, CM_L4SEC_STATICDEP,
+ * CM_MIPIEXT_STATICDEP, CM_MPU_STATICDEP
+ */
+#define OMAP54XX_L4PER_STATDEP_SHIFT                                   13
+#define OMAP54XX_L4PER_STATDEP_WIDTH                                   0x1
+#define OMAP54XX_L4PER_STATDEP_MASK                                    (1 << 13)
+
+/* Used by CM_L3MAIN2_DYNAMICDEP, CM_L4PER_DYNAMICDEP */
+#define OMAP54XX_L4SEC_DYNDEP_SHIFT                                    14
+#define OMAP54XX_L4SEC_DYNDEP_WIDTH                                    0x1
+#define OMAP54XX_L4SEC_DYNDEP_MASK                                     (1 << 14)
+
+/*
+ * Used by CM_DMA_STATICDEP, CM_IPU_STATICDEP, CM_L3INIT_STATICDEP,
+ * CM_MPU_STATICDEP
+ */
+#define OMAP54XX_L4SEC_STATDEP_SHIFT                                   14
+#define OMAP54XX_L4SEC_STATDEP_WIDTH                                   0x1
+#define OMAP54XX_L4SEC_STATDEP_MASK                                    (1 << 14)
+
+/* Used by CM_L3MAIN2_DYNAMICDEP, CM_L4CFG_DYNAMICDEP */
+#define OMAP54XX_MIPIEXT_DYNDEP_SHIFT                                  21
+#define OMAP54XX_MIPIEXT_DYNDEP_WIDTH                                  0x1
+#define OMAP54XX_MIPIEXT_DYNDEP_MASK                                   (1 << 21)
+
+/* Used by CM_MPU_STATICDEP */
+#define OMAP54XX_MIPIEXT_STATDEP_SHIFT                                 21
+#define OMAP54XX_MIPIEXT_STATDEP_WIDTH                                 0x1
+#define OMAP54XX_MIPIEXT_STATDEP_MASK                                  (1 << 21)
+
+/*
+ * Used by CM_SSC_MODFREQDIV_DPLL_ABE, CM_SSC_MODFREQDIV_DPLL_CORE,
+ * CM_SSC_MODFREQDIV_DPLL_IVA, CM_SSC_MODFREQDIV_DPLL_MPU,
+ * CM_SSC_MODFREQDIV_DPLL_PER, CM_SSC_MODFREQDIV_DPLL_UNIPRO1,
+ * CM_SSC_MODFREQDIV_DPLL_UNIPRO2, CM_SSC_MODFREQDIV_DPLL_USB
+ */
+#define OMAP54XX_MODFREQDIV_EXPONENT_SHIFT                             8
+#define OMAP54XX_MODFREQDIV_EXPONENT_WIDTH                             0x3
+#define OMAP54XX_MODFREQDIV_EXPONENT_MASK                              (0x7 << 8)
+
+/*
+ * Used by CM_SSC_MODFREQDIV_DPLL_ABE, CM_SSC_MODFREQDIV_DPLL_CORE,
+ * CM_SSC_MODFREQDIV_DPLL_IVA, CM_SSC_MODFREQDIV_DPLL_MPU,
+ * CM_SSC_MODFREQDIV_DPLL_PER, CM_SSC_MODFREQDIV_DPLL_UNIPRO1,
+ * CM_SSC_MODFREQDIV_DPLL_UNIPRO2, CM_SSC_MODFREQDIV_DPLL_USB
+ */
+#define OMAP54XX_MODFREQDIV_MANTISSA_SHIFT                             0
+#define OMAP54XX_MODFREQDIV_MANTISSA_WIDTH                             0x7
+#define OMAP54XX_MODFREQDIV_MANTISSA_MASK                              (0x7f << 0)
+
+/*
+ * Used by CM_ABE_AESS_CLKCTRL, CM_ABE_DMIC_CLKCTRL, CM_ABE_L4_ABE_CLKCTRL,
+ * CM_ABE_MCASP_CLKCTRL, CM_ABE_MCBSP1_CLKCTRL, CM_ABE_MCBSP2_CLKCTRL,
+ * CM_ABE_MCBSP3_CLKCTRL, CM_ABE_MCPDM_CLKCTRL, CM_ABE_SLIMBUS1_CLKCTRL,
+ * CM_ABE_TIMER5_CLKCTRL, CM_ABE_TIMER6_CLKCTRL, CM_ABE_TIMER7_CLKCTRL,
+ * CM_ABE_TIMER8_CLKCTRL, CM_ABE_WD_TIMER3_CLKCTRL, CM_C2C_C2C_CLKCTRL,
+ * CM_C2C_C2C_OCP_FW_CLKCTRL, CM_C2C_MODEM_ICR_CLKCTRL, CM_CAM_CAL_CLKCTRL,
+ * CM_CAM_FDIF_CLKCTRL, CM_CAM_ISS_CLKCTRL, CM_CM_CORE_AON_PROFILING_CLKCTRL,
+ * CM_CM_CORE_PROFILING_CLKCTRL, CM_COREAON_SMARTREFLEX_CORE_CLKCTRL,
+ * CM_COREAON_SMARTREFLEX_MM_CLKCTRL, CM_COREAON_SMARTREFLEX_MPU_CLKCTRL,
+ * CM_CUSTEFUSE_EFUSE_CTRL_CUST_CLKCTRL, CM_DMA_DMA_SYSTEM_CLKCTRL,
+ * CM_DSP_DSP_CLKCTRL, CM_DSS_BB2D_CLKCTRL, CM_DSS_DSS_CLKCTRL,
+ * CM_EMIF_DMM_CLKCTRL, CM_EMIF_EMIF1_CLKCTRL, CM_EMIF_EMIF2_CLKCTRL,
+ * CM_EMIF_EMIF_OCP_FW_CLKCTRL, CM_EMU_DEBUGSS_CLKCTRL,
+ * CM_EMU_MPU_EMU_DBG_CLKCTRL, CM_GPU_GPU_CLKCTRL, CM_IPU_IPU_CLKCTRL,
+ * CM_IVA_IVA_CLKCTRL, CM_IVA_SL2_CLKCTRL, CM_L3INIT_HSI_CLKCTRL,
+ * CM_L3INIT_IEEE1500_2_OCP_CLKCTRL, CM_L3INIT_MMC1_CLKCTRL,
+ * CM_L3INIT_MMC2_CLKCTRL, CM_L3INIT_MPHY_UNIPRO2_CLKCTRL,
+ * CM_L3INIT_OCP2SCP1_CLKCTRL, CM_L3INIT_OCP2SCP3_CLKCTRL,
+ * CM_L3INIT_SATA_CLKCTRL, CM_L3INIT_UNIPRO2_CLKCTRL,
+ * CM_L3INIT_USB_HOST_HS_CLKCTRL, CM_L3INIT_USB_OTG_SS_CLKCTRL,
+ * CM_L3INIT_USB_TLL_HS_CLKCTRL, CM_L3INSTR_CTRL_MODULE_BANDGAP_CLKCTRL,
+ * CM_L3INSTR_DLL_AGING_CLKCTRL, CM_L3INSTR_L3_INSTR_CLKCTRL,
+ * CM_L3INSTR_L3_MAIN_3_CLKCTRL, CM_L3INSTR_OCP_WP_NOC_CLKCTRL,
+ * CM_L3MAIN1_L3_MAIN_1_CLKCTRL, CM_L3MAIN2_GPMC_CLKCTRL,
+ * CM_L3MAIN2_L3_MAIN_2_CLKCTRL, CM_L3MAIN2_OCMC_RAM_CLKCTRL,
+ * CM_L4CFG_L4_CFG_CLKCTRL, CM_L4CFG_MAILBOX_CLKCTRL,
+ * CM_L4CFG_OCP2SCP2_CLKCTRL, CM_L4CFG_SAR_ROM_CLKCTRL,
+ * CM_L4CFG_SPINLOCK_CLKCTRL, CM_L4PER_ELM_CLKCTRL, CM_L4PER_GPIO2_CLKCTRL,
+ * CM_L4PER_GPIO3_CLKCTRL, CM_L4PER_GPIO4_CLKCTRL, CM_L4PER_GPIO5_CLKCTRL,
+ * CM_L4PER_GPIO6_CLKCTRL, CM_L4PER_GPIO7_CLKCTRL, CM_L4PER_GPIO8_CLKCTRL,
+ * CM_L4PER_HDQ1W_CLKCTRL, CM_L4PER_I2C1_CLKCTRL, CM_L4PER_I2C2_CLKCTRL,
+ * CM_L4PER_I2C3_CLKCTRL, CM_L4PER_I2C4_CLKCTRL, CM_L4PER_I2C5_CLKCTRL,
+ * CM_L4PER_L4_PER_CLKCTRL, CM_L4PER_MCSPI1_CLKCTRL, CM_L4PER_MCSPI2_CLKCTRL,
+ * CM_L4PER_MCSPI3_CLKCTRL, CM_L4PER_MCSPI4_CLKCTRL, CM_L4PER_MMC3_CLKCTRL,
+ * CM_L4PER_MMC4_CLKCTRL, CM_L4PER_MMC5_CLKCTRL, CM_L4PER_TIMER10_CLKCTRL,
+ * CM_L4PER_TIMER11_CLKCTRL, CM_L4PER_TIMER2_CLKCTRL, CM_L4PER_TIMER3_CLKCTRL,
+ * CM_L4PER_TIMER4_CLKCTRL, CM_L4PER_TIMER9_CLKCTRL, CM_L4PER_UART1_CLKCTRL,
+ * CM_L4PER_UART2_CLKCTRL, CM_L4PER_UART3_CLKCTRL, CM_L4PER_UART4_CLKCTRL,
+ * CM_L4PER_UART5_CLKCTRL, CM_L4PER_UART6_CLKCTRL, CM_L4SEC_AES1_CLKCTRL,
+ * CM_L4SEC_AES2_CLKCTRL, CM_L4SEC_DES3DES_CLKCTRL,
+ * CM_L4SEC_DMA_CRYPTO_CLKCTRL, CM_L4SEC_FPKA_CLKCTRL, CM_L4SEC_RNG_CLKCTRL,
+ * CM_L4SEC_SHA2MD5_CLKCTRL, CM_MIPIEXT_LLI_CLKCTRL,
+ * CM_MIPIEXT_LLI_OCP_FW_CLKCTRL, CM_MIPIEXT_MPHY_CLKCTRL, CM_MPU_MPU_CLKCTRL,
+ * CM_MPU_MPU_MPU_DBG_CLKCTRL, CM_WKUPAON_COUNTER_32K_CLKCTRL,
+ * CM_WKUPAON_GPIO1_CLKCTRL, CM_WKUPAON_KBD_CLKCTRL,
+ * CM_WKUPAON_L4_WKUP_CLKCTRL, CM_WKUPAON_SAR_RAM_CLKCTRL,
+ * CM_WKUPAON_TIMER12_CLKCTRL, CM_WKUPAON_TIMER1_CLKCTRL,
+ * CM_WKUPAON_WD_TIMER1_CLKCTRL, CM_WKUPAON_WD_TIMER2_CLKCTRL
+ */
+#define OMAP54XX_MODULEMODE_SHIFT                                      0
+#define OMAP54XX_MODULEMODE_WIDTH                                      0x2
+#define OMAP54XX_MODULEMODE_MASK                                       (0x3 << 0)
+
+/* Used by CM_L4CFG_DYNAMICDEP */
+#define OMAP54XX_MPU_DYNDEP_SHIFT                                      19
+#define OMAP54XX_MPU_DYNDEP_WIDTH                                      0x1
+#define OMAP54XX_MPU_DYNDEP_MASK                                       (1 << 19)
+
+/* Used by CM_DSS_DSS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_32KHZ_CLK_SHIFT                             11
+#define OMAP54XX_OPTFCLKEN_32KHZ_CLK_WIDTH                             0x1
+#define OMAP54XX_OPTFCLKEN_32KHZ_CLK_MASK                              (1 << 11)
+
+/* Renamed from OPTFCLKEN_32KHZ_CLK Used by CM_L3INIT_MMC1_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_32KHZ_CLK_8_8_SHIFT                         8
+#define OMAP54XX_OPTFCLKEN_32KHZ_CLK_8_8_WIDTH                         0x1
+#define OMAP54XX_OPTFCLKEN_32KHZ_CLK_8_8_MASK                          (1 << 8)
+
+/* Used by CM_DSS_DSS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_48MHZ_CLK_SHIFT                             9
+#define OMAP54XX_OPTFCLKEN_48MHZ_CLK_WIDTH                             0x1
+#define OMAP54XX_OPTFCLKEN_48MHZ_CLK_MASK                              (1 << 9)
+
+/* Used by CM_COREAON_USB_PHY_CORE_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_CLK32K_SHIFT                                        8
+#define OMAP54XX_OPTFCLKEN_CLK32K_WIDTH                                        0x1
+#define OMAP54XX_OPTFCLKEN_CLK32K_MASK                                 (1 << 8)
+
+/* Used by CM_CAM_ISS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_CTRLCLK_SHIFT                               8
+#define OMAP54XX_OPTFCLKEN_CTRLCLK_WIDTH                               0x1
+#define OMAP54XX_OPTFCLKEN_CTRLCLK_MASK                                        (1 << 8)
+
+/*
+ * Used by CM_L4PER_GPIO2_CLKCTRL, CM_L4PER_GPIO3_CLKCTRL,
+ * CM_L4PER_GPIO4_CLKCTRL, CM_L4PER_GPIO5_CLKCTRL, CM_L4PER_GPIO6_CLKCTRL,
+ * CM_L4PER_GPIO7_CLKCTRL, CM_L4PER_GPIO8_CLKCTRL, CM_WKUPAON_GPIO1_CLKCTRL
+ */
+#define OMAP54XX_OPTFCLKEN_DBCLK_SHIFT                                 8
+#define OMAP54XX_OPTFCLKEN_DBCLK_WIDTH                                 0x1
+#define OMAP54XX_OPTFCLKEN_DBCLK_MASK                                  (1 << 8)
+
+/* Used by CM_EMIF_EMIF_DLL_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_DLL_CLK_SHIFT                               8
+#define OMAP54XX_OPTFCLKEN_DLL_CLK_WIDTH                               0x1
+#define OMAP54XX_OPTFCLKEN_DLL_CLK_MASK                                        (1 << 8)
+
+/* Used by CM_DSS_DSS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_DSSCLK_SHIFT                                        8
+#define OMAP54XX_OPTFCLKEN_DSSCLK_WIDTH                                        0x1
+#define OMAP54XX_OPTFCLKEN_DSSCLK_MASK                                 (1 << 8)
+
+/* Used by CM_ABE_SLIMBUS1_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_FCLK0_SHIFT                                 8
+#define OMAP54XX_OPTFCLKEN_FCLK0_WIDTH                                 0x1
+#define OMAP54XX_OPTFCLKEN_FCLK0_MASK                                  (1 << 8)
+
+/* Used by CM_ABE_SLIMBUS1_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_FCLK1_SHIFT                                 9
+#define OMAP54XX_OPTFCLKEN_FCLK1_WIDTH                                 0x1
+#define OMAP54XX_OPTFCLKEN_FCLK1_MASK                                  (1 << 9)
+
+/* Used by CM_ABE_SLIMBUS1_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_FCLK2_SHIFT                                 10
+#define OMAP54XX_OPTFCLKEN_FCLK2_WIDTH                                 0x1
+#define OMAP54XX_OPTFCLKEN_FCLK2_MASK                                  (1 << 10)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_FUNC48M_CLK_SHIFT                           15
+#define OMAP54XX_OPTFCLKEN_FUNC48M_CLK_WIDTH                           0x1
+#define OMAP54XX_OPTFCLKEN_FUNC48M_CLK_MASK                            (1 << 15)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_HSIC480M_P1_CLK_SHIFT                       13
+#define OMAP54XX_OPTFCLKEN_HSIC480M_P1_CLK_WIDTH                       0x1
+#define OMAP54XX_OPTFCLKEN_HSIC480M_P1_CLK_MASK                                (1 << 13)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_HSIC480M_P2_CLK_SHIFT                       14
+#define OMAP54XX_OPTFCLKEN_HSIC480M_P2_CLK_WIDTH                       0x1
+#define OMAP54XX_OPTFCLKEN_HSIC480M_P2_CLK_MASK                                (1 << 14)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_HSIC480M_P3_CLK_SHIFT                       7
+#define OMAP54XX_OPTFCLKEN_HSIC480M_P3_CLK_WIDTH                       0x1
+#define OMAP54XX_OPTFCLKEN_HSIC480M_P3_CLK_MASK                                (1 << 7)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_HSIC60M_P1_CLK_SHIFT                                11
+#define OMAP54XX_OPTFCLKEN_HSIC60M_P1_CLK_WIDTH                                0x1
+#define OMAP54XX_OPTFCLKEN_HSIC60M_P1_CLK_MASK                         (1 << 11)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_HSIC60M_P2_CLK_SHIFT                                12
+#define OMAP54XX_OPTFCLKEN_HSIC60M_P2_CLK_WIDTH                                0x1
+#define OMAP54XX_OPTFCLKEN_HSIC60M_P2_CLK_MASK                         (1 << 12)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_HSIC60M_P3_CLK_SHIFT                                6
+#define OMAP54XX_OPTFCLKEN_HSIC60M_P3_CLK_WIDTH                                0x1
+#define OMAP54XX_OPTFCLKEN_HSIC60M_P3_CLK_MASK                         (1 << 6)
+
+/* Used by CM_L3INIT_USB_OTG_SS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_REFCLK960M_SHIFT                            8
+#define OMAP54XX_OPTFCLKEN_REFCLK960M_WIDTH                            0x1
+#define OMAP54XX_OPTFCLKEN_REFCLK960M_MASK                             (1 << 8)
+
+/* Used by CM_L3INIT_SATA_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_REF_CLK_SHIFT                               8
+#define OMAP54XX_OPTFCLKEN_REF_CLK_WIDTH                               0x1
+#define OMAP54XX_OPTFCLKEN_REF_CLK_MASK                                        (1 << 8)
+
+/* Used by CM_WKUPAON_SCRM_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_SCRM_CORE_SHIFT                             8
+#define OMAP54XX_OPTFCLKEN_SCRM_CORE_WIDTH                             0x1
+#define OMAP54XX_OPTFCLKEN_SCRM_CORE_MASK                              (1 << 8)
+
+/* Used by CM_WKUPAON_SCRM_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_SCRM_PER_SHIFT                              9
+#define OMAP54XX_OPTFCLKEN_SCRM_PER_WIDTH                              0x1
+#define OMAP54XX_OPTFCLKEN_SCRM_PER_MASK                               (1 << 9)
+
+/* Used by CM_ABE_SLIMBUS1_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_SLIMBUS_CLK_SHIFT                           11
+#define OMAP54XX_OPTFCLKEN_SLIMBUS_CLK_WIDTH                           0x1
+#define OMAP54XX_OPTFCLKEN_SLIMBUS_CLK_MASK                            (1 << 11)
+
+/* Used by CM_DSS_DSS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_SYS_CLK_SHIFT                               10
+#define OMAP54XX_OPTFCLKEN_SYS_CLK_WIDTH                               0x1
+#define OMAP54XX_OPTFCLKEN_SYS_CLK_MASK                                        (1 << 10)
+
+/* Used by CM_MIPIEXT_LLI_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_TXPHY_CLK_SHIFT                             8
+#define OMAP54XX_OPTFCLKEN_TXPHY_CLK_WIDTH                             0x1
+#define OMAP54XX_OPTFCLKEN_TXPHY_CLK_MASK                              (1 << 8)
+
+/* Used by CM_MIPIEXT_LLI_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_TXPHY_LS_CLK_SHIFT                          9
+#define OMAP54XX_OPTFCLKEN_TXPHY_LS_CLK_WIDTH                          0x1
+#define OMAP54XX_OPTFCLKEN_TXPHY_LS_CLK_MASK                           (1 << 9)
+
+/* Used by CM_L3INIT_USB_TLL_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_USB_CH0_CLK_SHIFT                           8
+#define OMAP54XX_OPTFCLKEN_USB_CH0_CLK_WIDTH                           0x1
+#define OMAP54XX_OPTFCLKEN_USB_CH0_CLK_MASK                            (1 << 8)
+
+/* Used by CM_L3INIT_USB_TLL_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_USB_CH1_CLK_SHIFT                           9
+#define OMAP54XX_OPTFCLKEN_USB_CH1_CLK_WIDTH                           0x1
+#define OMAP54XX_OPTFCLKEN_USB_CH1_CLK_MASK                            (1 << 9)
+
+/* Used by CM_L3INIT_USB_TLL_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_USB_CH2_CLK_SHIFT                           10
+#define OMAP54XX_OPTFCLKEN_USB_CH2_CLK_WIDTH                           0x1
+#define OMAP54XX_OPTFCLKEN_USB_CH2_CLK_MASK                            (1 << 10)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_UTMI_P1_CLK_SHIFT                           8
+#define OMAP54XX_OPTFCLKEN_UTMI_P1_CLK_WIDTH                           0x1
+#define OMAP54XX_OPTFCLKEN_UTMI_P1_CLK_MASK                            (1 << 8)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_UTMI_P2_CLK_SHIFT                           9
+#define OMAP54XX_OPTFCLKEN_UTMI_P2_CLK_WIDTH                           0x1
+#define OMAP54XX_OPTFCLKEN_UTMI_P2_CLK_MASK                            (1 << 9)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL */
+#define OMAP54XX_OPTFCLKEN_UTMI_P3_CLK_SHIFT                           10
+#define OMAP54XX_OPTFCLKEN_UTMI_P3_CLK_WIDTH                           0x1
+#define OMAP54XX_OPTFCLKEN_UTMI_P3_CLK_MASK                            (1 << 10)
+
+/* Used by CM_CORE_AON_DEBUG_OUT, CM_CORE_DEBUG_OUT */
+#define OMAP54XX_OUTPUT_SHIFT                                          0
+#define OMAP54XX_OUTPUT_WIDTH                                          0x20
+#define OMAP54XX_OUTPUT_MASK                                           (0xffffffff << 0)
+
+/* Used by CM_CLKSEL_ABE */
+#define OMAP54XX_PAD_CLKS_GATE_SHIFT                                   8
+#define OMAP54XX_PAD_CLKS_GATE_WIDTH                                   0x1
+#define OMAP54XX_PAD_CLKS_GATE_MASK                                    (1 << 8)
+
+/* Used by CM_RESTORE_ST */
+#define OMAP54XX_PHASE1_COMPLETED_SHIFT                                        0
+#define OMAP54XX_PHASE1_COMPLETED_WIDTH                                        0x1
+#define OMAP54XX_PHASE1_COMPLETED_MASK                                 (1 << 0)
+
+/* Used by CM_RESTORE_ST */
+#define OMAP54XX_PHASE2A_COMPLETED_SHIFT                               1
+#define OMAP54XX_PHASE2A_COMPLETED_WIDTH                               0x1
+#define OMAP54XX_PHASE2A_COMPLETED_MASK                                        (1 << 1)
+
+/* Used by CM_RESTORE_ST */
+#define OMAP54XX_PHASE2B_COMPLETED_SHIFT                               2
+#define OMAP54XX_PHASE2B_COMPLETED_WIDTH                               0x1
+#define OMAP54XX_PHASE2B_COMPLETED_MASK                                        (1 << 2)
+
+/* Used by CM_DYN_DEP_PRESCAL */
+#define OMAP54XX_PRESCAL_SHIFT                                         0
+#define OMAP54XX_PRESCAL_WIDTH                                         0x6
+#define OMAP54XX_PRESCAL_MASK                                          (0x3f << 0)
+
+/* Used by REVISION_CM_CORE, REVISION_CM_CORE_AON */
+#define OMAP54XX_R_RTL_SHIFT                                           11
+#define OMAP54XX_R_RTL_WIDTH                                           0x5
+#define OMAP54XX_R_RTL_MASK                                            (0x1f << 11)
+
+/* Used by CM_L3INIT_USB_HOST_HS_CLKCTRL, CM_L3INIT_USB_TLL_HS_CLKCTRL */
+#define OMAP54XX_SAR_MODE_SHIFT                                                4
+#define OMAP54XX_SAR_MODE_WIDTH                                                0x1
+#define OMAP54XX_SAR_MODE_MASK                                         (1 << 4)
+
+/* Used by REVISION_CM_CORE, REVISION_CM_CORE_AON */
+#define OMAP54XX_SCHEME_SHIFT                                          30
+#define OMAP54XX_SCHEME_WIDTH                                          0x2
+#define OMAP54XX_SCHEME_MASK                                           (0x3 << 30)
+
+/* Used by CM_L4CFG_DYNAMICDEP */
+#define OMAP54XX_SDMA_DYNDEP_SHIFT                                     11
+#define OMAP54XX_SDMA_DYNDEP_WIDTH                                     0x1
+#define OMAP54XX_SDMA_DYNDEP_MASK                                      (1 << 11)
+
+/* Used by CM_IPU_STATICDEP, CM_MPU_STATICDEP */
+#define OMAP54XX_SDMA_STATDEP_SHIFT                                    11
+#define OMAP54XX_SDMA_STATDEP_WIDTH                                    0x1
+#define OMAP54XX_SDMA_STATDEP_MASK                                     (1 << 11)
+
+/* Used by CM_CORE_AON_DEBUG_CFG */
+#define OMAP54XX_SEL0_SHIFT                                            0
+#define OMAP54XX_SEL0_WIDTH                                            0x7
+#define OMAP54XX_SEL0_MASK                                             (0x7f << 0)
+
+/* Renamed from SEL0 Used by CM_CORE_DEBUG_CFG */
+#define OMAP54XX_SEL0_0_7_SHIFT                                                0
+#define OMAP54XX_SEL0_0_7_WIDTH                                                0x8
+#define OMAP54XX_SEL0_0_7_MASK                                         (0xff << 0)
+
+/* Used by CM_CORE_AON_DEBUG_CFG */
+#define OMAP54XX_SEL1_SHIFT                                            8
+#define OMAP54XX_SEL1_WIDTH                                            0x7
+#define OMAP54XX_SEL1_MASK                                             (0x7f << 8)
+
+/* Renamed from SEL1 Used by CM_CORE_DEBUG_CFG */
+#define OMAP54XX_SEL1_CORE_DEBUG_CFG_SHIFT                             8
+#define OMAP54XX_SEL1_CORE_DEBUG_CFG_WIDTH                             0x8
+#define OMAP54XX_SEL1_CORE_DEBUG_CFG_MASK                              (0xff << 8)
+
+/* Used by CM_CORE_AON_DEBUG_CFG */
+#define OMAP54XX_SEL2_SHIFT                                            16
+#define OMAP54XX_SEL2_WIDTH                                            0x7
+#define OMAP54XX_SEL2_MASK                                             (0x7f << 16)
+
+/* Renamed from SEL2 Used by CM_CORE_DEBUG_CFG */
+#define OMAP54XX_SEL2_CORE_DEBUG_CFG_SHIFT                             16
+#define OMAP54XX_SEL2_CORE_DEBUG_CFG_WIDTH                             0x8
+#define OMAP54XX_SEL2_CORE_DEBUG_CFG_MASK                              (0xff << 16)
+
+/* Used by CM_CORE_AON_DEBUG_CFG */
+#define OMAP54XX_SEL3_SHIFT                                            24
+#define OMAP54XX_SEL3_WIDTH                                            0x7
+#define OMAP54XX_SEL3_MASK                                             (0x7f << 24)
+
+/* Renamed from SEL3 Used by CM_CORE_DEBUG_CFG */
+#define OMAP54XX_SEL3_CORE_DEBUG_CFG_SHIFT                             24
+#define OMAP54XX_SEL3_CORE_DEBUG_CFG_WIDTH                             0x8
+#define OMAP54XX_SEL3_CORE_DEBUG_CFG_MASK                              (0xff << 24)
+
+/* Used by CM_CLKSEL_ABE */
+#define OMAP54XX_SLIMBUS1_CLK_GATE_SHIFT                               10
+#define OMAP54XX_SLIMBUS1_CLK_GATE_WIDTH                               0x1
+#define OMAP54XX_SLIMBUS1_CLK_GATE_MASK                                        (1 << 10)
+
+/*
+ * Used by CM_ABE_AESS_CLKCTRL, CM_C2C_C2C_CLKCTRL, CM_CAM_FDIF_CLKCTRL,
+ * CM_CAM_ISS_CLKCTRL, CM_DMA_DMA_SYSTEM_CLKCTRL, CM_DSP_DSP_CLKCTRL,
+ * CM_DSS_BB2D_CLKCTRL, CM_DSS_DSS_CLKCTRL, CM_EMU_DEBUGSS_CLKCTRL,
+ * CM_GPU_GPU_CLKCTRL, CM_IPU_IPU_CLKCTRL, CM_IVA_IVA_CLKCTRL,
+ * CM_L3INIT_HSI_CLKCTRL, CM_L3INIT_IEEE1500_2_OCP_CLKCTRL,
+ * CM_L3INIT_MMC1_CLKCTRL, CM_L3INIT_MMC2_CLKCTRL, CM_L3INIT_SATA_CLKCTRL,
+ * CM_L3INIT_UNIPRO2_CLKCTRL, CM_L3INIT_USB_HOST_HS_CLKCTRL,
+ * CM_L3INIT_USB_OTG_SS_CLKCTRL, CM_L4SEC_DMA_CRYPTO_CLKCTRL,
+ * CM_MIPIEXT_LLI_CLKCTRL, CM_MPU_MPU_CLKCTRL
+ */
+#define OMAP54XX_STBYST_SHIFT                                          18
+#define OMAP54XX_STBYST_WIDTH                                          0x1
+#define OMAP54XX_STBYST_MASK                                           (1 << 18)
+
+/*
+ * Used by CM_IDLEST_DPLL_ABE, CM_IDLEST_DPLL_CORE, CM_IDLEST_DPLL_IVA,
+ * CM_IDLEST_DPLL_MPU, CM_IDLEST_DPLL_PER, CM_IDLEST_DPLL_UNIPRO1,
+ * CM_IDLEST_DPLL_UNIPRO2, CM_IDLEST_DPLL_USB
+ */
+#define OMAP54XX_ST_DPLL_CLK_SHIFT                                     0
+#define OMAP54XX_ST_DPLL_CLK_WIDTH                                     0x1
+#define OMAP54XX_ST_DPLL_CLK_MASK                                      (1 << 0)
+
+/*
+ * Used by CM_CLKDCOLDO_DPLL_UNIPRO1, CM_CLKDCOLDO_DPLL_UNIPRO2,
+ * CM_CLKDCOLDO_DPLL_USB
+ */
+#define OMAP54XX_ST_DPLL_CLKDCOLDO_SHIFT                               9
+#define OMAP54XX_ST_DPLL_CLKDCOLDO_WIDTH                               0x1
+#define OMAP54XX_ST_DPLL_CLKDCOLDO_MASK                                        (1 << 9)
+
+/*
+ * Used by CM_IDLEST_DPLL_ABE, CM_IDLEST_DPLL_CORE, CM_IDLEST_DPLL_IVA,
+ * CM_IDLEST_DPLL_MPU, CM_IDLEST_DPLL_PER, CM_IDLEST_DPLL_UNIPRO1,
+ * CM_IDLEST_DPLL_UNIPRO2, CM_IDLEST_DPLL_USB
+ */
+#define OMAP54XX_ST_DPLL_INIT_SHIFT                                    4
+#define OMAP54XX_ST_DPLL_INIT_WIDTH                                    0x1
+#define OMAP54XX_ST_DPLL_INIT_MASK                                     (1 << 4)
+
+/*
+ * Used by CM_IDLEST_DPLL_ABE, CM_IDLEST_DPLL_CORE, CM_IDLEST_DPLL_IVA,
+ * CM_IDLEST_DPLL_MPU, CM_IDLEST_DPLL_PER, CM_IDLEST_DPLL_UNIPRO1,
+ * CM_IDLEST_DPLL_UNIPRO2, CM_IDLEST_DPLL_USB
+ */
+#define OMAP54XX_ST_DPLL_MODE_SHIFT                                    1
+#define OMAP54XX_ST_DPLL_MODE_WIDTH                                    0x3
+#define OMAP54XX_ST_DPLL_MODE_MASK                                     (0x7 << 1)
+
+/* Used by CM_CLKSEL_SYS */
+#define OMAP54XX_SYS_CLKSEL_SHIFT                                      0
+#define OMAP54XX_SYS_CLKSEL_WIDTH                                      0x3
+#define OMAP54XX_SYS_CLKSEL_MASK                                       (0x7 << 0)
+
+/*
+ * Used by CM_C2C_DYNAMICDEP, CM_DSP_DYNAMICDEP, CM_EMU_DYNAMICDEP,
+ * CM_IPU_DYNAMICDEP, CM_L3MAIN1_DYNAMICDEP, CM_L3MAIN2_DYNAMICDEP,
+ * CM_L4CFG_DYNAMICDEP, CM_L4PER_DYNAMICDEP, CM_MIPIEXT_DYNAMICDEP,
+ * CM_MPU_DYNAMICDEP
+ */
+#define OMAP54XX_WINDOWSIZE_SHIFT                                      24
+#define OMAP54XX_WINDOWSIZE_WIDTH                                      0x4
+#define OMAP54XX_WINDOWSIZE_MASK                                       (0xf << 24)
+
+/* Used by CM_L3MAIN1_DYNAMICDEP */
+#define OMAP54XX_WKUPAON_DYNDEP_SHIFT                                  15
+#define OMAP54XX_WKUPAON_DYNDEP_WIDTH                                  0x1
+#define OMAP54XX_WKUPAON_DYNDEP_MASK                                   (1 << 15)
+
+/*
+ * Used by CM_DMA_STATICDEP, CM_DSP_STATICDEP, CM_IPU_STATICDEP,
+ * CM_L3INIT_STATICDEP, CM_MPU_STATICDEP
+ */
+#define OMAP54XX_WKUPAON_STATDEP_SHIFT                                 15
+#define OMAP54XX_WKUPAON_STATDEP_WIDTH                                 0x1
+#define OMAP54XX_WKUPAON_STATDEP_MASK                                  (1 << 15)
+
+/* Used by REVISION_CM_CORE, REVISION_CM_CORE_AON */
+#define OMAP54XX_X_MAJOR_SHIFT                                         8
+#define OMAP54XX_X_MAJOR_WIDTH                                         0x3
+#define OMAP54XX_X_MAJOR_MASK                                          (0x7 << 8)
+
+/* Used by REVISION_CM_CORE, REVISION_CM_CORE_AON */
+#define OMAP54XX_Y_MINOR_SHIFT                                         0
+#define OMAP54XX_Y_MINOR_WIDTH                                         0x6
+#define OMAP54XX_Y_MINOR_MASK                                          (0x3f << 0)
+#endif
diff --git a/arch/arm/mach-omap2/cm1_54xx.h b/arch/arm/mach-omap2/cm1_54xx.h
new file mode 100644 (file)
index 0000000..a36e00e
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * OMAP54xx CM1 instance offset macros
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Paul Walmsley (paul@pwsan.com)
+ * Rajendra Nayak (rnayak@ti.com)
+ * Benoit Cousson (b-cousson@ti.com)
+ *
+ * This file is automatically generated from the OMAP hardware databases.
+ * We respectfully ask that any modifications to this file be coordinated
+ * with the public linux-omap@vger.kernel.org mailing list and the
+ * authors above to ensure that the autogeneration scripts are kept
+ * up-to-date with the file contents.
+ *
+ * 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 __ARCH_ARM_MACH_OMAP2_CM1_54XX_H
+#define __ARCH_ARM_MACH_OMAP2_CM1_54XX_H
+
+/* CM1 base address */
+#define OMAP54XX_CM_CORE_AON_BASE              0x4a004000
+
+#define OMAP54XX_CM_CORE_AON_REGADDR(inst, reg)                                \
+       OMAP2_L4_IO_ADDRESS(OMAP54XX_CM_CORE_AON_BASE + (inst) + (reg))
+
+/* CM_CORE_AON instances */
+#define OMAP54XX_CM_CORE_AON_OCP_SOCKET_INST   0x0000
+#define OMAP54XX_CM_CORE_AON_CKGEN_INST                0x0100
+#define OMAP54XX_CM_CORE_AON_MPU_INST          0x0300
+#define OMAP54XX_CM_CORE_AON_DSP_INST          0x0400
+#define OMAP54XX_CM_CORE_AON_ABE_INST          0x0500
+#define OMAP54XX_CM_CORE_AON_RESTORE_INST      0x0e00
+#define OMAP54XX_CM_CORE_AON_INSTR_INST                0x0f00
+
+/* CM_CORE_AON clockdomain register offsets (from instance start) */
+#define OMAP54XX_CM_CORE_AON_MPU_MPU_CDOFFS    0x0000
+#define OMAP54XX_CM_CORE_AON_DSP_DSP_CDOFFS    0x0000
+#define OMAP54XX_CM_CORE_AON_ABE_ABE_CDOFFS    0x0000
+
+/* CM_CORE_AON */
+
+/* CM_CORE_AON.OCP_SOCKET_CM_CORE_AON register offsets */
+#define OMAP54XX_REVISION_CM_CORE_AON_OFFSET                   0x0000
+#define OMAP54XX_CM_CM_CORE_AON_PROFILING_CLKCTRL_OFFSET       0x0040
+#define OMAP54XX_CM_CM_CORE_AON_PROFILING_CLKCTRL              OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_OCP_SOCKET_INST, 0x0040)
+#define OMAP54XX_CM_CORE_AON_DEBUG_CFG_OFFSET                  0x0080
+#define OMAP54XX_CM_CORE_AON_DEBUG_OUT_OFFSET                  0x0084
+#define OMAP54XX_CM_CORE_AON_DEBUG_MPU_FD_TRANS_OFFSET         0x0090
+#define OMAP54XX_CM_CORE_AON_DEBUG_DSP_FD_TRANS_OFFSET         0x0094
+#define OMAP54XX_CM_CORE_AON_DEBUG_ABE_FD_TRANS_OFFSET         0x0098
+#define OMAP54XX_CM_CORE_AON_DEBUG_ABE_FD_TRANS2_OFFSET                0x009c
+#define OMAP54XX_CM_CORE_AON_DEBUG_CM_CORE_AON_FD_TRANS_OFFSET 0x00a0
+#define OMAP54XX_CM_CORE_AON_DEBUG_C2C_FD_TRANS_OFFSET         0x00a4
+#define OMAP54XX_CM_CORE_AON_DEBUG_CAM_FD_TRANS_OFFSET         0x00a8
+#define OMAP54XX_CM_CORE_AON_DEBUG_COREAON_FD_TRANS_OFFSET     0x00ac
+#define OMAP54XX_CM_CORE_AON_DEBUG_CUSTEFUSE_FD_TRANS_OFFSET   0x00b0
+#define OMAP54XX_CM_CORE_AON_DEBUG_DMA_FD_TRANS_OFFSET         0x00b4
+#define OMAP54XX_CM_CORE_AON_DEBUG_DSS_FD_TRANS_OFFSET         0x00b8
+#define OMAP54XX_CM_CORE_AON_DEBUG_EMIF_FD_TRANS_OFFSET                0x00bc
+#define OMAP54XX_CM_CORE_AON_DEBUG_GPU_FD_TRANS_OFFSET         0x00c0
+#define OMAP54XX_CM_CORE_AON_DEBUG_IPU_FD_TRANS_OFFSET         0x00c4
+#define OMAP54XX_CM_CORE_AON_DEBUG_IVA_FD_TRANS_OFFSET         0x00c8
+#define OMAP54XX_CM_CORE_AON_DEBUG_L3INIT_FD_TRANS_OFFSET      0x00cc
+#define OMAP54XX_CM_CORE_AON_DEBUG_L3INIT_FD_TRANS2_OFFSET     0x00d0
+#define OMAP54XX_CM_CORE_AON_DEBUG_L3INSTR_FD_TRANS_OFFSET     0x00d4
+#define OMAP54XX_CM_CORE_AON_DEBUG_L3MAIN1_FD_TRANS_OFFSET     0x00d8
+#define OMAP54XX_CM_CORE_AON_DEBUG_L3MAIN2_FD_TRANS_OFFSET     0x00dc
+#define OMAP54XX_CM_CORE_AON_DEBUG_L4CFG_FD_TRANS_OFFSET       0x00e0
+#define OMAP54XX_CM_CORE_AON_DEBUG_L4PER_FD_TRANS_OFFSET       0x00e4
+#define OMAP54XX_CM_CORE_AON_DEBUG_L4PER_FD_TRANS2_OFFSET      0x00e8
+#define OMAP54XX_CM_CORE_AON_DEBUG_L4SEC_FD_TRANS_OFFSET       0x00ec
+#define OMAP54XX_CM_CORE_AON_DEBUG_MIPIEXT_FD_TRANS_OFFSET     0x00f0
+
+/* CM_CORE_AON.CKGEN_CM_CORE_AON register offsets */
+#define OMAP54XX_CM_CLKSEL_CORE_OFFSET                         0x0000
+#define OMAP54XX_CM_CLKSEL_CORE                                        OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0000)
+#define OMAP54XX_CM_CLKSEL_ABE_OFFSET                          0x0008
+#define OMAP54XX_CM_CLKSEL_ABE                                 OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0008)
+#define OMAP54XX_CM_DLL_CTRL_OFFSET                            0x0010
+#define OMAP54XX_CM_CLKMODE_DPLL_CORE_OFFSET                   0x0020
+#define OMAP54XX_CM_CLKMODE_DPLL_CORE                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0020)
+#define OMAP54XX_CM_IDLEST_DPLL_CORE_OFFSET                    0x0024
+#define OMAP54XX_CM_IDLEST_DPLL_CORE                           OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0024)
+#define OMAP54XX_CM_AUTOIDLE_DPLL_CORE_OFFSET                  0x0028
+#define OMAP54XX_CM_AUTOIDLE_DPLL_CORE                         OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0028)
+#define OMAP54XX_CM_CLKSEL_DPLL_CORE_OFFSET                    0x002c
+#define OMAP54XX_CM_CLKSEL_DPLL_CORE                           OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x002c)
+#define OMAP54XX_CM_DIV_M2_DPLL_CORE_OFFSET                    0x0030
+#define OMAP54XX_CM_DIV_M2_DPLL_CORE                           OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0030)
+#define OMAP54XX_CM_DIV_M3_DPLL_CORE_OFFSET                    0x0034
+#define OMAP54XX_CM_DIV_M3_DPLL_CORE                           OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0034)
+#define OMAP54XX_CM_DIV_H11_DPLL_CORE_OFFSET                   0x0038
+#define OMAP54XX_CM_DIV_H11_DPLL_CORE                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0038)
+#define OMAP54XX_CM_DIV_H12_DPLL_CORE_OFFSET                   0x003c
+#define OMAP54XX_CM_DIV_H12_DPLL_CORE                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x003c)
+#define OMAP54XX_CM_DIV_H13_DPLL_CORE_OFFSET                   0x0040
+#define OMAP54XX_CM_DIV_H13_DPLL_CORE                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0040)
+#define OMAP54XX_CM_DIV_H14_DPLL_CORE_OFFSET                   0x0044
+#define OMAP54XX_CM_DIV_H14_DPLL_CORE                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0044)
+#define OMAP54XX_CM_SSC_DELTAMSTEP_DPLL_CORE_OFFSET            0x0048
+#define OMAP54XX_CM_SSC_MODFREQDIV_DPLL_CORE_OFFSET            0x004c
+#define OMAP54XX_CM_DIV_H21_DPLL_CORE_OFFSET                   0x0050
+#define OMAP54XX_CM_DIV_H21_DPLL_CORE                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0050)
+#define OMAP54XX_CM_DIV_H22_DPLL_CORE_OFFSET                   0x0054
+#define OMAP54XX_CM_DIV_H22_DPLL_CORE                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0054)
+#define OMAP54XX_CM_DIV_H23_DPLL_CORE_OFFSET                   0x0058
+#define OMAP54XX_CM_DIV_H23_DPLL_CORE                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0058)
+#define OMAP54XX_CM_DIV_H24_DPLL_CORE_OFFSET                   0x005c
+#define OMAP54XX_CM_DIV_H24_DPLL_CORE                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x005c)
+#define OMAP54XX_CM_CLKMODE_DPLL_MPU_OFFSET                    0x0060
+#define OMAP54XX_CM_CLKMODE_DPLL_MPU                           OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0060)
+#define OMAP54XX_CM_IDLEST_DPLL_MPU_OFFSET                     0x0064
+#define OMAP54XX_CM_IDLEST_DPLL_MPU                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0064)
+#define OMAP54XX_CM_AUTOIDLE_DPLL_MPU_OFFSET                   0x0068
+#define OMAP54XX_CM_AUTOIDLE_DPLL_MPU                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0068)
+#define OMAP54XX_CM_CLKSEL_DPLL_MPU_OFFSET                     0x006c
+#define OMAP54XX_CM_CLKSEL_DPLL_MPU                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x006c)
+#define OMAP54XX_CM_DIV_M2_DPLL_MPU_OFFSET                     0x0070
+#define OMAP54XX_CM_DIV_M2_DPLL_MPU                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x0070)
+#define OMAP54XX_CM_SSC_DELTAMSTEP_DPLL_MPU_OFFSET             0x0088
+#define OMAP54XX_CM_SSC_MODFREQDIV_DPLL_MPU_OFFSET             0x008c
+#define OMAP54XX_CM_BYPCLK_DPLL_MPU_OFFSET                     0x009c
+#define OMAP54XX_CM_BYPCLK_DPLL_MPU                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x009c)
+#define OMAP54XX_CM_CLKMODE_DPLL_IVA_OFFSET                    0x00a0
+#define OMAP54XX_CM_CLKMODE_DPLL_IVA                           OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00a0)
+#define OMAP54XX_CM_IDLEST_DPLL_IVA_OFFSET                     0x00a4
+#define OMAP54XX_CM_IDLEST_DPLL_IVA                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00a4)
+#define OMAP54XX_CM_AUTOIDLE_DPLL_IVA_OFFSET                   0x00a8
+#define OMAP54XX_CM_AUTOIDLE_DPLL_IVA                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00a8)
+#define OMAP54XX_CM_CLKSEL_DPLL_IVA_OFFSET                     0x00ac
+#define OMAP54XX_CM_CLKSEL_DPLL_IVA                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00ac)
+#define OMAP54XX_CM_DIV_H11_DPLL_IVA_OFFSET                    0x00b8
+#define OMAP54XX_CM_DIV_H11_DPLL_IVA                           OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00b8)
+#define OMAP54XX_CM_DIV_H12_DPLL_IVA_OFFSET                    0x00bc
+#define OMAP54XX_CM_DIV_H12_DPLL_IVA                           OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00bc)
+#define OMAP54XX_CM_SSC_DELTAMSTEP_DPLL_IVA_OFFSET             0x00c8
+#define OMAP54XX_CM_SSC_MODFREQDIV_DPLL_IVA_OFFSET             0x00cc
+#define OMAP54XX_CM_BYPCLK_DPLL_IVA_OFFSET                     0x00dc
+#define OMAP54XX_CM_BYPCLK_DPLL_IVA                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00dc)
+#define OMAP54XX_CM_CLKMODE_DPLL_ABE_OFFSET                    0x00e0
+#define OMAP54XX_CM_CLKMODE_DPLL_ABE                           OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00e0)
+#define OMAP54XX_CM_IDLEST_DPLL_ABE_OFFSET                     0x00e4
+#define OMAP54XX_CM_IDLEST_DPLL_ABE                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00e4)
+#define OMAP54XX_CM_AUTOIDLE_DPLL_ABE_OFFSET                   0x00e8
+#define OMAP54XX_CM_AUTOIDLE_DPLL_ABE                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00e8)
+#define OMAP54XX_CM_CLKSEL_DPLL_ABE_OFFSET                     0x00ec
+#define OMAP54XX_CM_CLKSEL_DPLL_ABE                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00ec)
+#define OMAP54XX_CM_DIV_M2_DPLL_ABE_OFFSET                     0x00f0
+#define OMAP54XX_CM_DIV_M2_DPLL_ABE                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00f0)
+#define OMAP54XX_CM_DIV_M3_DPLL_ABE_OFFSET                     0x00f4
+#define OMAP54XX_CM_DIV_M3_DPLL_ABE                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_CKGEN_INST, 0x00f4)
+#define OMAP54XX_CM_SSC_DELTAMSTEP_DPLL_ABE_OFFSET             0x0108
+#define OMAP54XX_CM_SSC_MODFREQDIV_DPLL_ABE_OFFSET             0x010c
+#define OMAP54XX_CM_SHADOW_FREQ_CONFIG1_OFFSET                 0x0160
+#define OMAP54XX_CM_SHADOW_FREQ_CONFIG2_OFFSET                 0x0164
+#define OMAP54XX_CM_DYN_DEP_PRESCAL_OFFSET                     0x0170
+#define OMAP54XX_CM_RESTORE_ST_OFFSET                          0x0180
+
+/* CM_CORE_AON.MPU_CM_CORE_AON register offsets */
+#define OMAP54XX_CM_MPU_CLKSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_CM_MPU_STATICDEP_OFFSET                       0x0004
+#define OMAP54XX_CM_MPU_DYNAMICDEP_OFFSET                      0x0008
+#define OMAP54XX_CM_MPU_MPU_CLKCTRL_OFFSET                     0x0020
+#define OMAP54XX_CM_MPU_MPU_CLKCTRL                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_MPU_INST, 0x0020)
+#define OMAP54XX_CM_MPU_MPU_MPU_DBG_CLKCTRL_OFFSET             0x0028
+#define OMAP54XX_CM_MPU_MPU_MPU_DBG_CLKCTRL                    OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_MPU_INST, 0x0028)
+
+/* CM_CORE_AON.DSP_CM_CORE_AON register offsets */
+#define OMAP54XX_CM_DSP_CLKSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_CM_DSP_STATICDEP_OFFSET                       0x0004
+#define OMAP54XX_CM_DSP_DYNAMICDEP_OFFSET                      0x0008
+#define OMAP54XX_CM_DSP_DSP_CLKCTRL_OFFSET                     0x0020
+#define OMAP54XX_CM_DSP_DSP_CLKCTRL                            OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_DSP_INST, 0x0020)
+
+/* CM_CORE_AON.ABE_CM_CORE_AON register offsets */
+#define OMAP54XX_CM_ABE_CLKSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_CM_ABE_L4_ABE_CLKCTRL_OFFSET                  0x0020
+#define OMAP54XX_CM_ABE_L4_ABE_CLKCTRL                         OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0020)
+#define OMAP54XX_CM_ABE_AESS_CLKCTRL_OFFSET                    0x0028
+#define OMAP54XX_CM_ABE_AESS_CLKCTRL                           OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0028)
+#define OMAP54XX_CM_ABE_MCPDM_CLKCTRL_OFFSET                   0x0030
+#define OMAP54XX_CM_ABE_MCPDM_CLKCTRL                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0030)
+#define OMAP54XX_CM_ABE_DMIC_CLKCTRL_OFFSET                    0x0038
+#define OMAP54XX_CM_ABE_DMIC_CLKCTRL                           OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0038)
+#define OMAP54XX_CM_ABE_MCASP_CLKCTRL_OFFSET                   0x0040
+#define OMAP54XX_CM_ABE_MCASP_CLKCTRL                          OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0040)
+#define OMAP54XX_CM_ABE_MCBSP1_CLKCTRL_OFFSET                  0x0048
+#define OMAP54XX_CM_ABE_MCBSP1_CLKCTRL                         OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0048)
+#define OMAP54XX_CM_ABE_MCBSP2_CLKCTRL_OFFSET                  0x0050
+#define OMAP54XX_CM_ABE_MCBSP2_CLKCTRL                         OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0050)
+#define OMAP54XX_CM_ABE_MCBSP3_CLKCTRL_OFFSET                  0x0058
+#define OMAP54XX_CM_ABE_MCBSP3_CLKCTRL                         OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0058)
+#define OMAP54XX_CM_ABE_SLIMBUS1_CLKCTRL_OFFSET                        0x0060
+#define OMAP54XX_CM_ABE_SLIMBUS1_CLKCTRL                       OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0060)
+#define OMAP54XX_CM_ABE_TIMER5_CLKCTRL_OFFSET                  0x0068
+#define OMAP54XX_CM_ABE_TIMER5_CLKCTRL                         OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0068)
+#define OMAP54XX_CM_ABE_TIMER6_CLKCTRL_OFFSET                  0x0070
+#define OMAP54XX_CM_ABE_TIMER6_CLKCTRL                         OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0070)
+#define OMAP54XX_CM_ABE_TIMER7_CLKCTRL_OFFSET                  0x0078
+#define OMAP54XX_CM_ABE_TIMER7_CLKCTRL                         OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0078)
+#define OMAP54XX_CM_ABE_TIMER8_CLKCTRL_OFFSET                  0x0080
+#define OMAP54XX_CM_ABE_TIMER8_CLKCTRL                         OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0080)
+#define OMAP54XX_CM_ABE_WD_TIMER3_CLKCTRL_OFFSET               0x0088
+#define OMAP54XX_CM_ABE_WD_TIMER3_CLKCTRL                      OMAP54XX_CM_CORE_AON_REGADDR(OMAP54XX_CM_CORE_AON_ABE_INST, 0x0088)
+
+/* Function prototypes */
+extern u32 omap4_cm1_read_inst_reg(s16 inst, u16 idx);
+extern void omap4_cm1_write_inst_reg(u32 val, s16 inst, u16 idx);
+extern u32 omap4_cm1_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);
+
+#endif
diff --git a/arch/arm/mach-omap2/cm2_54xx.h b/arch/arm/mach-omap2/cm2_54xx.h
new file mode 100644 (file)
index 0000000..b6c0830
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * OMAP54xx CM2 instance offset macros
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Paul Walmsley (paul@pwsan.com)
+ * Rajendra Nayak (rnayak@ti.com)
+ * Benoit Cousson (b-cousson@ti.com)
+ *
+ * This file is automatically generated from the OMAP hardware databases.
+ * We respectfully ask that any modifications to this file be coordinated
+ * with the public linux-omap@vger.kernel.org mailing list and the
+ * authors above to ensure that the autogeneration scripts are kept
+ * up-to-date with the file contents.
+ *
+ * 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 __ARCH_ARM_MACH_OMAP2_CM2_54XX_H
+#define __ARCH_ARM_MACH_OMAP2_CM2_54XX_H
+
+/* CM2 base address */
+#define OMAP54XX_CM_CORE_BASE          0x4a008000
+
+#define OMAP54XX_CM_CORE_REGADDR(inst, reg)                            \
+       OMAP2_L4_IO_ADDRESS(OMAP54XX_CM_CORE_BASE + (inst) + (reg))
+
+/* CM_CORE instances */
+#define OMAP54XX_CM_CORE_OCP_SOCKET_INST       0x0000
+#define OMAP54XX_CM_CORE_CKGEN_INST            0x0100
+#define OMAP54XX_CM_CORE_COREAON_INST          0x0600
+#define OMAP54XX_CM_CORE_CORE_INST             0x0700
+#define OMAP54XX_CM_CORE_IVA_INST              0x1200
+#define OMAP54XX_CM_CORE_CAM_INST              0x1300
+#define OMAP54XX_CM_CORE_DSS_INST              0x1400
+#define OMAP54XX_CM_CORE_GPU_INST              0x1500
+#define OMAP54XX_CM_CORE_L3INIT_INST           0x1600
+#define OMAP54XX_CM_CORE_CUSTEFUSE_INST                0x1700
+#define OMAP54XX_CM_CORE_RESTORE_INST          0x1e00
+#define OMAP54XX_CM_CORE_INSTR_INST            0x1f00
+
+/* CM_CORE clockdomain register offsets (from instance start) */
+#define OMAP54XX_CM_CORE_COREAON_COREAON_CDOFFS                0x0000
+#define OMAP54XX_CM_CORE_CORE_L3MAIN1_CDOFFS           0x0000
+#define OMAP54XX_CM_CORE_CORE_L3MAIN2_CDOFFS           0x0100
+#define OMAP54XX_CM_CORE_CORE_IPU_CDOFFS               0x0200
+#define OMAP54XX_CM_CORE_CORE_DMA_CDOFFS               0x0300
+#define OMAP54XX_CM_CORE_CORE_EMIF_CDOFFS              0x0400
+#define OMAP54XX_CM_CORE_CORE_C2C_CDOFFS               0x0500
+#define OMAP54XX_CM_CORE_CORE_L4CFG_CDOFFS             0x0600
+#define OMAP54XX_CM_CORE_CORE_L3INSTR_CDOFFS           0x0700
+#define OMAP54XX_CM_CORE_CORE_MIPIEXT_CDOFFS           0x0800
+#define OMAP54XX_CM_CORE_CORE_L4PER_CDOFFS             0x0900
+#define OMAP54XX_CM_CORE_CORE_L4SEC_CDOFFS             0x0a80
+#define OMAP54XX_CM_CORE_IVA_IVA_CDOFFS                        0x0000
+#define OMAP54XX_CM_CORE_CAM_CAM_CDOFFS                        0x0000
+#define OMAP54XX_CM_CORE_DSS_DSS_CDOFFS                        0x0000
+#define OMAP54XX_CM_CORE_GPU_GPU_CDOFFS                        0x0000
+#define OMAP54XX_CM_CORE_L3INIT_L3INIT_CDOFFS          0x0000
+#define OMAP54XX_CM_CORE_CUSTEFUSE_CUSTEFUSE_CDOFFS    0x0000
+
+/* CM_CORE */
+
+/* CM_CORE.OCP_SOCKET_CM_CORE register offsets */
+#define OMAP54XX_REVISION_CM_CORE_OFFSET                       0x0000
+#define OMAP54XX_CM_CM_CORE_PROFILING_CLKCTRL_OFFSET           0x0040
+#define OMAP54XX_CM_CM_CORE_PROFILING_CLKCTRL                  OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_OCP_SOCKET_INST, 0x0040)
+#define OMAP54XX_CM_CORE_DEBUG_CFG_OFFSET                      0x0080
+#define OMAP54XX_CM_CORE_DEBUG_OUT_OFFSET                      0x0084
+
+/* CM_CORE.CKGEN_CM_CORE register offsets */
+#define OMAP54XX_CM_CLKSEL_USB_60MHZ_OFFSET                    0x0004
+#define OMAP54XX_CM_CLKSEL_USB_60MHZ                           OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0004)
+#define OMAP54XX_CM_CLKMODE_DPLL_PER_OFFSET                    0x0040
+#define OMAP54XX_CM_CLKMODE_DPLL_PER                           OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0040)
+#define OMAP54XX_CM_IDLEST_DPLL_PER_OFFSET                     0x0044
+#define OMAP54XX_CM_IDLEST_DPLL_PER                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0044)
+#define OMAP54XX_CM_AUTOIDLE_DPLL_PER_OFFSET                   0x0048
+#define OMAP54XX_CM_AUTOIDLE_DPLL_PER                          OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0048)
+#define OMAP54XX_CM_CLKSEL_DPLL_PER_OFFSET                     0x004c
+#define OMAP54XX_CM_CLKSEL_DPLL_PER                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x004c)
+#define OMAP54XX_CM_DIV_M2_DPLL_PER_OFFSET                     0x0050
+#define OMAP54XX_CM_DIV_M2_DPLL_PER                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0050)
+#define OMAP54XX_CM_DIV_M3_DPLL_PER_OFFSET                     0x0054
+#define OMAP54XX_CM_DIV_M3_DPLL_PER                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0054)
+#define OMAP54XX_CM_DIV_H11_DPLL_PER_OFFSET                    0x0058
+#define OMAP54XX_CM_DIV_H11_DPLL_PER                           OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0058)
+#define OMAP54XX_CM_DIV_H12_DPLL_PER_OFFSET                    0x005c
+#define OMAP54XX_CM_DIV_H12_DPLL_PER                           OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x005c)
+#define OMAP54XX_CM_DIV_H13_DPLL_PER_OFFSET                    0x0060
+#define OMAP54XX_CM_DIV_H13_DPLL_PER                           OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0060)
+#define OMAP54XX_CM_DIV_H14_DPLL_PER_OFFSET                    0x0064
+#define OMAP54XX_CM_DIV_H14_DPLL_PER                           OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0064)
+#define OMAP54XX_CM_SSC_DELTAMSTEP_DPLL_PER_OFFSET             0x0068
+#define OMAP54XX_CM_SSC_MODFREQDIV_DPLL_PER_OFFSET             0x006c
+#define OMAP54XX_CM_CLKMODE_DPLL_USB_OFFSET                    0x0080
+#define OMAP54XX_CM_CLKMODE_DPLL_USB                           OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0080)
+#define OMAP54XX_CM_IDLEST_DPLL_USB_OFFSET                     0x0084
+#define OMAP54XX_CM_IDLEST_DPLL_USB                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0084)
+#define OMAP54XX_CM_AUTOIDLE_DPLL_USB_OFFSET                   0x0088
+#define OMAP54XX_CM_AUTOIDLE_DPLL_USB                          OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0088)
+#define OMAP54XX_CM_CLKSEL_DPLL_USB_OFFSET                     0x008c
+#define OMAP54XX_CM_CLKSEL_DPLL_USB                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x008c)
+#define OMAP54XX_CM_DIV_M2_DPLL_USB_OFFSET                     0x0090
+#define OMAP54XX_CM_DIV_M2_DPLL_USB                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0090)
+#define OMAP54XX_CM_SSC_DELTAMSTEP_DPLL_USB_OFFSET             0x00a8
+#define OMAP54XX_CM_SSC_MODFREQDIV_DPLL_USB_OFFSET             0x00ac
+#define OMAP54XX_CM_CLKDCOLDO_DPLL_USB_OFFSET                  0x00b4
+#define OMAP54XX_CM_CLKDCOLDO_DPLL_USB                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x00b4)
+#define OMAP54XX_CM_CLKMODE_DPLL_UNIPRO2_OFFSET                        0x00c0
+#define OMAP54XX_CM_CLKMODE_DPLL_UNIPRO2                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x00c0)
+#define OMAP54XX_CM_IDLEST_DPLL_UNIPRO2_OFFSET                 0x00c4
+#define OMAP54XX_CM_IDLEST_DPLL_UNIPRO2                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x00c4)
+#define OMAP54XX_CM_AUTOIDLE_DPLL_UNIPRO2_OFFSET               0x00c8
+#define OMAP54XX_CM_AUTOIDLE_DPLL_UNIPRO2                      OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x00c8)
+#define OMAP54XX_CM_CLKSEL_DPLL_UNIPRO2_OFFSET                 0x00cc
+#define OMAP54XX_CM_CLKSEL_DPLL_UNIPRO2                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x00cc)
+#define OMAP54XX_CM_DIV_M2_DPLL_UNIPRO2_OFFSET                 0x00d0
+#define OMAP54XX_CM_DIV_M2_DPLL_UNIPRO2                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x00d0)
+#define OMAP54XX_CM_SSC_DELTAMSTEP_DPLL_UNIPRO2_OFFSET         0x00e8
+#define OMAP54XX_CM_SSC_MODFREQDIV_DPLL_UNIPRO2_OFFSET         0x00ec
+#define OMAP54XX_CM_CLKDCOLDO_DPLL_UNIPRO2_OFFSET              0x00f4
+#define OMAP54XX_CM_CLKDCOLDO_DPLL_UNIPRO2                     OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x00f4)
+#define OMAP54XX_CM_CLKMODE_DPLL_UNIPRO1_OFFSET                        0x0100
+#define OMAP54XX_CM_CLKMODE_DPLL_UNIPRO1                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0100)
+#define OMAP54XX_CM_IDLEST_DPLL_UNIPRO1_OFFSET                 0x0104
+#define OMAP54XX_CM_IDLEST_DPLL_UNIPRO1                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0104)
+#define OMAP54XX_CM_AUTOIDLE_DPLL_UNIPRO1_OFFSET               0x0108
+#define OMAP54XX_CM_AUTOIDLE_DPLL_UNIPRO1                      OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0108)
+#define OMAP54XX_CM_CLKSEL_DPLL_UNIPRO1_OFFSET                 0x010c
+#define OMAP54XX_CM_CLKSEL_DPLL_UNIPRO1                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x010c)
+#define OMAP54XX_CM_DIV_M2_DPLL_UNIPRO1_OFFSET                 0x0110
+#define OMAP54XX_CM_DIV_M2_DPLL_UNIPRO1                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0110)
+#define OMAP54XX_CM_SSC_DELTAMSTEP_DPLL_UNIPRO1_OFFSET         0x0128
+#define OMAP54XX_CM_SSC_MODFREQDIV_DPLL_UNIPRO1_OFFSET         0x012c
+#define OMAP54XX_CM_CLKDCOLDO_DPLL_UNIPRO1_OFFSET              0x0134
+#define OMAP54XX_CM_CLKDCOLDO_DPLL_UNIPRO1                     OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CKGEN_INST, 0x0134)
+
+/* CM_CORE.COREAON_CM_CORE register offsets */
+#define OMAP54XX_CM_COREAON_CLKSTCTRL_OFFSET                   0x0000
+#define OMAP54XX_CM_COREAON_SMARTREFLEX_MPU_CLKCTRL_OFFSET     0x0028
+#define OMAP54XX_CM_COREAON_SMARTREFLEX_MPU_CLKCTRL            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_COREAON_INST, 0x0028)
+#define OMAP54XX_CM_COREAON_SMARTREFLEX_MM_CLKCTRL_OFFSET      0x0030
+#define OMAP54XX_CM_COREAON_SMARTREFLEX_MM_CLKCTRL             OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_COREAON_INST, 0x0030)
+#define OMAP54XX_CM_COREAON_SMARTREFLEX_CORE_CLKCTRL_OFFSET    0x0038
+#define OMAP54XX_CM_COREAON_SMARTREFLEX_CORE_CLKCTRL           OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_COREAON_INST, 0x0038)
+#define OMAP54XX_CM_COREAON_USB_PHY_CORE_CLKCTRL_OFFSET                0x0040
+#define OMAP54XX_CM_COREAON_USB_PHY_CORE_CLKCTRL               OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_COREAON_INST, 0x0040)
+#define OMAP54XX_CM_COREAON_IO_SRCOMP_CLKCTRL_OFFSET           0x0050
+#define OMAP54XX_CM_COREAON_IO_SRCOMP_CLKCTRL                  OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_COREAON_INST, 0x0050)
+
+/* CM_CORE.CORE_CM_CORE register offsets */
+#define OMAP54XX_CM_L3MAIN1_CLKSTCTRL_OFFSET                   0x0000
+#define OMAP54XX_CM_L3MAIN1_DYNAMICDEP_OFFSET                  0x0008
+#define OMAP54XX_CM_L3MAIN1_L3_MAIN_1_CLKCTRL_OFFSET           0x0020
+#define OMAP54XX_CM_L3MAIN1_L3_MAIN_1_CLKCTRL                  OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0020)
+#define OMAP54XX_CM_L3MAIN2_CLKSTCTRL_OFFSET                   0x0100
+#define OMAP54XX_CM_L3MAIN2_DYNAMICDEP_OFFSET                  0x0108
+#define OMAP54XX_CM_L3MAIN2_L3_MAIN_2_CLKCTRL_OFFSET           0x0120
+#define OMAP54XX_CM_L3MAIN2_L3_MAIN_2_CLKCTRL                  OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0120)
+#define OMAP54XX_CM_L3MAIN2_GPMC_CLKCTRL_OFFSET                        0x0128
+#define OMAP54XX_CM_L3MAIN2_GPMC_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0128)
+#define OMAP54XX_CM_L3MAIN2_OCMC_RAM_CLKCTRL_OFFSET            0x0130
+#define OMAP54XX_CM_L3MAIN2_OCMC_RAM_CLKCTRL                   OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0130)
+#define OMAP54XX_CM_IPU_CLKSTCTRL_OFFSET                       0x0200
+#define OMAP54XX_CM_IPU_STATICDEP_OFFSET                       0x0204
+#define OMAP54XX_CM_IPU_DYNAMICDEP_OFFSET                      0x0208
+#define OMAP54XX_CM_IPU_IPU_CLKCTRL_OFFSET                     0x0220
+#define OMAP54XX_CM_IPU_IPU_CLKCTRL                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0220)
+#define OMAP54XX_CM_DMA_CLKSTCTRL_OFFSET                       0x0300
+#define OMAP54XX_CM_DMA_STATICDEP_OFFSET                       0x0304
+#define OMAP54XX_CM_DMA_DYNAMICDEP_OFFSET                      0x0308
+#define OMAP54XX_CM_DMA_DMA_SYSTEM_CLKCTRL_OFFSET              0x0320
+#define OMAP54XX_CM_DMA_DMA_SYSTEM_CLKCTRL                     OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0320)
+#define OMAP54XX_CM_EMIF_CLKSTCTRL_OFFSET                      0x0400
+#define OMAP54XX_CM_EMIF_DMM_CLKCTRL_OFFSET                    0x0420
+#define OMAP54XX_CM_EMIF_DMM_CLKCTRL                           OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0420)
+#define OMAP54XX_CM_EMIF_EMIF_OCP_FW_CLKCTRL_OFFSET            0x0428
+#define OMAP54XX_CM_EMIF_EMIF_OCP_FW_CLKCTRL                   OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0428)
+#define OMAP54XX_CM_EMIF_EMIF1_CLKCTRL_OFFSET                  0x0430
+#define OMAP54XX_CM_EMIF_EMIF1_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0430)
+#define OMAP54XX_CM_EMIF_EMIF2_CLKCTRL_OFFSET                  0x0438
+#define OMAP54XX_CM_EMIF_EMIF2_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0438)
+#define OMAP54XX_CM_EMIF_EMIF_DLL_CLKCTRL_OFFSET               0x0440
+#define OMAP54XX_CM_EMIF_EMIF_DLL_CLKCTRL                      OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0440)
+#define OMAP54XX_CM_C2C_CLKSTCTRL_OFFSET                       0x0500
+#define OMAP54XX_CM_C2C_STATICDEP_OFFSET                       0x0504
+#define OMAP54XX_CM_C2C_DYNAMICDEP_OFFSET                      0x0508
+#define OMAP54XX_CM_C2C_C2C_CLKCTRL_OFFSET                     0x0520
+#define OMAP54XX_CM_C2C_C2C_CLKCTRL                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0520)
+#define OMAP54XX_CM_C2C_MODEM_ICR_CLKCTRL_OFFSET               0x0528
+#define OMAP54XX_CM_C2C_MODEM_ICR_CLKCTRL                      OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0528)
+#define OMAP54XX_CM_C2C_C2C_OCP_FW_CLKCTRL_OFFSET              0x0530
+#define OMAP54XX_CM_C2C_C2C_OCP_FW_CLKCTRL                     OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0530)
+#define OMAP54XX_CM_L4CFG_CLKSTCTRL_OFFSET                     0x0600
+#define OMAP54XX_CM_L4CFG_DYNAMICDEP_OFFSET                    0x0608
+#define OMAP54XX_CM_L4CFG_L4_CFG_CLKCTRL_OFFSET                        0x0620
+#define OMAP54XX_CM_L4CFG_L4_CFG_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0620)
+#define OMAP54XX_CM_L4CFG_SPINLOCK_CLKCTRL_OFFSET              0x0628
+#define OMAP54XX_CM_L4CFG_SPINLOCK_CLKCTRL                     OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0628)
+#define OMAP54XX_CM_L4CFG_MAILBOX_CLKCTRL_OFFSET               0x0630
+#define OMAP54XX_CM_L4CFG_MAILBOX_CLKCTRL                      OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0630)
+#define OMAP54XX_CM_L4CFG_SAR_ROM_CLKCTRL_OFFSET               0x0638
+#define OMAP54XX_CM_L4CFG_SAR_ROM_CLKCTRL                      OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0638)
+#define OMAP54XX_CM_L4CFG_OCP2SCP2_CLKCTRL_OFFSET              0x0640
+#define OMAP54XX_CM_L4CFG_OCP2SCP2_CLKCTRL                     OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0640)
+#define OMAP54XX_CM_L3INSTR_CLKSTCTRL_OFFSET                   0x0700
+#define OMAP54XX_CM_L3INSTR_L3_MAIN_3_CLKCTRL_OFFSET           0x0720
+#define OMAP54XX_CM_L3INSTR_L3_MAIN_3_CLKCTRL                  OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0720)
+#define OMAP54XX_CM_L3INSTR_L3_INSTR_CLKCTRL_OFFSET            0x0728
+#define OMAP54XX_CM_L3INSTR_L3_INSTR_CLKCTRL                   OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0728)
+#define OMAP54XX_CM_L3INSTR_OCP_WP_NOC_CLKCTRL_OFFSET          0x0740
+#define OMAP54XX_CM_L3INSTR_OCP_WP_NOC_CLKCTRL                 OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0740)
+#define OMAP54XX_CM_L3INSTR_DLL_AGING_CLKCTRL_OFFSET           0x0748
+#define OMAP54XX_CM_L3INSTR_DLL_AGING_CLKCTRL                  OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0748)
+#define OMAP54XX_CM_L3INSTR_CTRL_MODULE_BANDGAP_CLKCTRL_OFFSET 0x0750
+#define OMAP54XX_CM_L3INSTR_CTRL_MODULE_BANDGAP_CLKCTRL                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0750)
+#define OMAP54XX_CM_MIPIEXT_CLKSTCTRL_OFFSET                   0x0800
+#define OMAP54XX_CM_MIPIEXT_STATICDEP_OFFSET                   0x0804
+#define OMAP54XX_CM_MIPIEXT_DYNAMICDEP_OFFSET                  0x0808
+#define OMAP54XX_CM_MIPIEXT_LLI_CLKCTRL_OFFSET                 0x0820
+#define OMAP54XX_CM_MIPIEXT_LLI_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0820)
+#define OMAP54XX_CM_MIPIEXT_LLI_OCP_FW_CLKCTRL_OFFSET          0x0828
+#define OMAP54XX_CM_MIPIEXT_LLI_OCP_FW_CLKCTRL                 OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0828)
+#define OMAP54XX_CM_MIPIEXT_MPHY_CLKCTRL_OFFSET                        0x0830
+#define OMAP54XX_CM_MIPIEXT_MPHY_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0830)
+#define OMAP54XX_CM_L4PER_CLKSTCTRL_OFFSET                     0x0900
+#define OMAP54XX_CM_L4PER_DYNAMICDEP_OFFSET                    0x0908
+#define OMAP54XX_CM_L4PER_TIMER10_CLKCTRL_OFFSET               0x0928
+#define OMAP54XX_CM_L4PER_TIMER10_CLKCTRL                      OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0928)
+#define OMAP54XX_CM_L4PER_TIMER11_CLKCTRL_OFFSET               0x0930
+#define OMAP54XX_CM_L4PER_TIMER11_CLKCTRL                      OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0930)
+#define OMAP54XX_CM_L4PER_TIMER2_CLKCTRL_OFFSET                        0x0938
+#define OMAP54XX_CM_L4PER_TIMER2_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0938)
+#define OMAP54XX_CM_L4PER_TIMER3_CLKCTRL_OFFSET                        0x0940
+#define OMAP54XX_CM_L4PER_TIMER3_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0940)
+#define OMAP54XX_CM_L4PER_TIMER4_CLKCTRL_OFFSET                        0x0948
+#define OMAP54XX_CM_L4PER_TIMER4_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0948)
+#define OMAP54XX_CM_L4PER_TIMER9_CLKCTRL_OFFSET                        0x0950
+#define OMAP54XX_CM_L4PER_TIMER9_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0950)
+#define OMAP54XX_CM_L4PER_ELM_CLKCTRL_OFFSET                   0x0958
+#define OMAP54XX_CM_L4PER_ELM_CLKCTRL                          OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0958)
+#define OMAP54XX_CM_L4PER_GPIO2_CLKCTRL_OFFSET                 0x0960
+#define OMAP54XX_CM_L4PER_GPIO2_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0960)
+#define OMAP54XX_CM_L4PER_GPIO3_CLKCTRL_OFFSET                 0x0968
+#define OMAP54XX_CM_L4PER_GPIO3_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0968)
+#define OMAP54XX_CM_L4PER_GPIO4_CLKCTRL_OFFSET                 0x0970
+#define OMAP54XX_CM_L4PER_GPIO4_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0970)
+#define OMAP54XX_CM_L4PER_GPIO5_CLKCTRL_OFFSET                 0x0978
+#define OMAP54XX_CM_L4PER_GPIO5_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0978)
+#define OMAP54XX_CM_L4PER_GPIO6_CLKCTRL_OFFSET                 0x0980
+#define OMAP54XX_CM_L4PER_GPIO6_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0980)
+#define OMAP54XX_CM_L4PER_HDQ1W_CLKCTRL_OFFSET                 0x0988
+#define OMAP54XX_CM_L4PER_HDQ1W_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0988)
+#define OMAP54XX_CM_L4PER_I2C1_CLKCTRL_OFFSET                  0x09a0
+#define OMAP54XX_CM_L4PER_I2C1_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x09a0)
+#define OMAP54XX_CM_L4PER_I2C2_CLKCTRL_OFFSET                  0x09a8
+#define OMAP54XX_CM_L4PER_I2C2_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x09a8)
+#define OMAP54XX_CM_L4PER_I2C3_CLKCTRL_OFFSET                  0x09b0
+#define OMAP54XX_CM_L4PER_I2C3_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x09b0)
+#define OMAP54XX_CM_L4PER_I2C4_CLKCTRL_OFFSET                  0x09b8
+#define OMAP54XX_CM_L4PER_I2C4_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x09b8)
+#define OMAP54XX_CM_L4PER_L4_PER_CLKCTRL_OFFSET                        0x09c0
+#define OMAP54XX_CM_L4PER_L4_PER_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x09c0)
+#define OMAP54XX_CM_L4PER_MCSPI1_CLKCTRL_OFFSET                        0x09f0
+#define OMAP54XX_CM_L4PER_MCSPI1_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x09f0)
+#define OMAP54XX_CM_L4PER_MCSPI2_CLKCTRL_OFFSET                        0x09f8
+#define OMAP54XX_CM_L4PER_MCSPI2_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x09f8)
+#define OMAP54XX_CM_L4PER_MCSPI3_CLKCTRL_OFFSET                        0x0a00
+#define OMAP54XX_CM_L4PER_MCSPI3_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a00)
+#define OMAP54XX_CM_L4PER_MCSPI4_CLKCTRL_OFFSET                        0x0a08
+#define OMAP54XX_CM_L4PER_MCSPI4_CLKCTRL                       OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a08)
+#define OMAP54XX_CM_L4PER_GPIO7_CLKCTRL_OFFSET                 0x0a10
+#define OMAP54XX_CM_L4PER_GPIO7_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a10)
+#define OMAP54XX_CM_L4PER_GPIO8_CLKCTRL_OFFSET                 0x0a18
+#define OMAP54XX_CM_L4PER_GPIO8_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a18)
+#define OMAP54XX_CM_L4PER_MMC3_CLKCTRL_OFFSET                  0x0a20
+#define OMAP54XX_CM_L4PER_MMC3_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a20)
+#define OMAP54XX_CM_L4PER_MMC4_CLKCTRL_OFFSET                  0x0a28
+#define OMAP54XX_CM_L4PER_MMC4_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a28)
+#define OMAP54XX_CM_L4PER_UART1_CLKCTRL_OFFSET                 0x0a40
+#define OMAP54XX_CM_L4PER_UART1_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a40)
+#define OMAP54XX_CM_L4PER_UART2_CLKCTRL_OFFSET                 0x0a48
+#define OMAP54XX_CM_L4PER_UART2_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a48)
+#define OMAP54XX_CM_L4PER_UART3_CLKCTRL_OFFSET                 0x0a50
+#define OMAP54XX_CM_L4PER_UART3_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a50)
+#define OMAP54XX_CM_L4PER_UART4_CLKCTRL_OFFSET                 0x0a58
+#define OMAP54XX_CM_L4PER_UART4_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a58)
+#define OMAP54XX_CM_L4PER_MMC5_CLKCTRL_OFFSET                  0x0a60
+#define OMAP54XX_CM_L4PER_MMC5_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a60)
+#define OMAP54XX_CM_L4PER_I2C5_CLKCTRL_OFFSET                  0x0a68
+#define OMAP54XX_CM_L4PER_I2C5_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a68)
+#define OMAP54XX_CM_L4PER_UART5_CLKCTRL_OFFSET                 0x0a70
+#define OMAP54XX_CM_L4PER_UART5_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a70)
+#define OMAP54XX_CM_L4PER_UART6_CLKCTRL_OFFSET                 0x0a78
+#define OMAP54XX_CM_L4PER_UART6_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0a78)
+#define OMAP54XX_CM_L4SEC_CLKSTCTRL_OFFSET                     0x0a80
+#define OMAP54XX_CM_L4SEC_STATICDEP_OFFSET                     0x0a84
+#define OMAP54XX_CM_L4SEC_DYNAMICDEP_OFFSET                    0x0a88
+#define OMAP54XX_CM_L4SEC_AES1_CLKCTRL_OFFSET                  0x0aa0
+#define OMAP54XX_CM_L4SEC_AES1_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0aa0)
+#define OMAP54XX_CM_L4SEC_AES2_CLKCTRL_OFFSET                  0x0aa8
+#define OMAP54XX_CM_L4SEC_AES2_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0aa8)
+#define OMAP54XX_CM_L4SEC_DES3DES_CLKCTRL_OFFSET               0x0ab0
+#define OMAP54XX_CM_L4SEC_DES3DES_CLKCTRL                      OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0ab0)
+#define OMAP54XX_CM_L4SEC_FPKA_CLKCTRL_OFFSET                  0x0ab8
+#define OMAP54XX_CM_L4SEC_FPKA_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0ab8)
+#define OMAP54XX_CM_L4SEC_RNG_CLKCTRL_OFFSET                   0x0ac0
+#define OMAP54XX_CM_L4SEC_RNG_CLKCTRL                          OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0ac0)
+#define OMAP54XX_CM_L4SEC_SHA2MD5_CLKCTRL_OFFSET               0x0ac8
+#define OMAP54XX_CM_L4SEC_SHA2MD5_CLKCTRL                      OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0ac8)
+#define OMAP54XX_CM_L4SEC_DMA_CRYPTO_CLKCTRL_OFFSET            0x0ad8
+#define OMAP54XX_CM_L4SEC_DMA_CRYPTO_CLKCTRL                   OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CORE_INST, 0x0ad8)
+
+/* CM_CORE.IVA_CM_CORE register offsets */
+#define OMAP54XX_CM_IVA_CLKSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_CM_IVA_STATICDEP_OFFSET                       0x0004
+#define OMAP54XX_CM_IVA_DYNAMICDEP_OFFSET                      0x0008
+#define OMAP54XX_CM_IVA_IVA_CLKCTRL_OFFSET                     0x0020
+#define OMAP54XX_CM_IVA_IVA_CLKCTRL                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_IVA_INST, 0x0020)
+#define OMAP54XX_CM_IVA_SL2_CLKCTRL_OFFSET                     0x0028
+#define OMAP54XX_CM_IVA_SL2_CLKCTRL                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_IVA_INST, 0x0028)
+
+/* CM_CORE.CAM_CM_CORE register offsets */
+#define OMAP54XX_CM_CAM_CLKSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_CM_CAM_STATICDEP_OFFSET                       0x0004
+#define OMAP54XX_CM_CAM_DYNAMICDEP_OFFSET                      0x0008
+#define OMAP54XX_CM_CAM_ISS_CLKCTRL_OFFSET                     0x0020
+#define OMAP54XX_CM_CAM_ISS_CLKCTRL                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CAM_INST, 0x0020)
+#define OMAP54XX_CM_CAM_FDIF_CLKCTRL_OFFSET                    0x0028
+#define OMAP54XX_CM_CAM_FDIF_CLKCTRL                           OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CAM_INST, 0x0028)
+#define OMAP54XX_CM_CAM_CAL_CLKCTRL_OFFSET                     0x0030
+#define OMAP54XX_CM_CAM_CAL_CLKCTRL                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CAM_INST, 0x0030)
+
+/* CM_CORE.DSS_CM_CORE register offsets */
+#define OMAP54XX_CM_DSS_CLKSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_CM_DSS_STATICDEP_OFFSET                       0x0004
+#define OMAP54XX_CM_DSS_DYNAMICDEP_OFFSET                      0x0008
+#define OMAP54XX_CM_DSS_DSS_CLKCTRL_OFFSET                     0x0020
+#define OMAP54XX_CM_DSS_DSS_CLKCTRL                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_DSS_INST, 0x0020)
+#define OMAP54XX_CM_DSS_BB2D_CLKCTRL_OFFSET                    0x0030
+#define OMAP54XX_CM_DSS_BB2D_CLKCTRL                           OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_DSS_INST, 0x0030)
+
+/* CM_CORE.GPU_CM_CORE register offsets */
+#define OMAP54XX_CM_GPU_CLKSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_CM_GPU_STATICDEP_OFFSET                       0x0004
+#define OMAP54XX_CM_GPU_DYNAMICDEP_OFFSET                      0x0008
+#define OMAP54XX_CM_GPU_GPU_CLKCTRL_OFFSET                     0x0020
+#define OMAP54XX_CM_GPU_GPU_CLKCTRL                            OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_GPU_INST, 0x0020)
+
+/* CM_CORE.L3INIT_CM_CORE register offsets */
+#define OMAP54XX_CM_L3INIT_CLKSTCTRL_OFFSET                    0x0000
+#define OMAP54XX_CM_L3INIT_STATICDEP_OFFSET                    0x0004
+#define OMAP54XX_CM_L3INIT_DYNAMICDEP_OFFSET                   0x0008
+#define OMAP54XX_CM_L3INIT_MMC1_CLKCTRL_OFFSET                 0x0028
+#define OMAP54XX_CM_L3INIT_MMC1_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x0028)
+#define OMAP54XX_CM_L3INIT_MMC2_CLKCTRL_OFFSET                 0x0030
+#define OMAP54XX_CM_L3INIT_MMC2_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x0030)
+#define OMAP54XX_CM_L3INIT_HSI_CLKCTRL_OFFSET                  0x0038
+#define OMAP54XX_CM_L3INIT_HSI_CLKCTRL                         OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x0038)
+#define OMAP54XX_CM_L3INIT_UNIPRO2_CLKCTRL_OFFSET              0x0040
+#define OMAP54XX_CM_L3INIT_UNIPRO2_CLKCTRL                     OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x0040)
+#define OMAP54XX_CM_L3INIT_MPHY_UNIPRO2_CLKCTRL_OFFSET         0x0048
+#define OMAP54XX_CM_L3INIT_MPHY_UNIPRO2_CLKCTRL                        OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x0048)
+#define OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL_OFFSET          0x0058
+#define OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL                 OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x0058)
+#define OMAP54XX_CM_L3INIT_USB_TLL_HS_CLKCTRL_OFFSET           0x0068
+#define OMAP54XX_CM_L3INIT_USB_TLL_HS_CLKCTRL                  OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x0068)
+#define OMAP54XX_CM_L3INIT_IEEE1500_2_OCP_CLKCTRL_OFFSET       0x0078
+#define OMAP54XX_CM_L3INIT_IEEE1500_2_OCP_CLKCTRL              OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x0078)
+#define OMAP54XX_CM_L3INIT_SATA_CLKCTRL_OFFSET                 0x0088
+#define OMAP54XX_CM_L3INIT_SATA_CLKCTRL                                OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x0088)
+#define OMAP54XX_CM_L3INIT_OCP2SCP1_CLKCTRL_OFFSET             0x00e0
+#define OMAP54XX_CM_L3INIT_OCP2SCP1_CLKCTRL                    OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x00e0)
+#define OMAP54XX_CM_L3INIT_OCP2SCP3_CLKCTRL_OFFSET             0x00e8
+#define OMAP54XX_CM_L3INIT_OCP2SCP3_CLKCTRL                    OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x00e8)
+#define OMAP54XX_CM_L3INIT_USB_OTG_SS_CLKCTRL_OFFSET           0x00f0
+#define OMAP54XX_CM_L3INIT_USB_OTG_SS_CLKCTRL                  OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_L3INIT_INST, 0x00f0)
+
+/* CM_CORE.CUSTEFUSE_CM_CORE register offsets */
+#define OMAP54XX_CM_CUSTEFUSE_CLKSTCTRL_OFFSET                 0x0000
+#define OMAP54XX_CM_CUSTEFUSE_EFUSE_CTRL_CUST_CLKCTRL_OFFSET   0x0020
+#define OMAP54XX_CM_CUSTEFUSE_EFUSE_CTRL_CUST_CLKCTRL          OMAP54XX_CM_CORE_REGADDR(OMAP54XX_CM_CORE_CUSTEFUSE_INST, 0x0020)
+
+/* Function prototypes */
+extern u32 omap4_cm2_read_inst_reg(s16 inst, u16 idx);
+extern void omap4_cm2_write_inst_reg(u32 val, s16 inst, u16 idx);
+extern u32 omap4_cm2_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);
+
+#endif
index db650690e9d0a64595fabdd6e37078567710c9b6..8ca13cd325cc54126491768b96c9f26b2decfec4 100644 (file)
@@ -273,9 +273,6 @@ int omap2xxx_cm_wait_module_ready(s16 prcm_mod, u8 idlest_id, u8 idlest_shift)
 
 static void omap2xxx_clkdm_allow_idle(struct clockdomain *clkdm)
 {
-       if (atomic_read(&clkdm->usecount) > 0)
-               _clkdm_add_autodeps(clkdm);
-
        omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                       clkdm->clktrctrl_mask);
 }
@@ -284,57 +281,20 @@ static void omap2xxx_clkdm_deny_idle(struct clockdomain *clkdm)
 {
        omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                        clkdm->clktrctrl_mask);
-
-       if (atomic_read(&clkdm->usecount) > 0)
-               _clkdm_del_autodeps(clkdm);
 }
 
 static int omap2xxx_clkdm_clk_enable(struct clockdomain *clkdm)
 {
-       bool hwsup = false;
-
-       if (!clkdm->clktrctrl_mask)
-               return 0;
-
-       hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
-                                             clkdm->clktrctrl_mask);
-
-       if (hwsup) {
-               /* Disable HW transitions when we are changing deps */
-               omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
-                                               clkdm->clktrctrl_mask);
-               _clkdm_add_autodeps(clkdm);
-               omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
-                                              clkdm->clktrctrl_mask);
-       } else {
-               if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
-                       omap2xxx_clkdm_wakeup(clkdm);
-       }
+       if (!clkdm_in_hwsup(clkdm) && clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
+               omap2xxx_clkdm_wakeup(clkdm);
 
        return 0;
 }
 
 static int omap2xxx_clkdm_clk_disable(struct clockdomain *clkdm)
 {
-       bool hwsup = false;
-
-       if (!clkdm->clktrctrl_mask)
-               return 0;
-
-       hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
-                                             clkdm->clktrctrl_mask);
-
-       if (hwsup) {
-               /* Disable HW transitions when we are changing deps */
-               omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
-                                               clkdm->clktrctrl_mask);
-               _clkdm_del_autodeps(clkdm);
-               omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
-                                              clkdm->clktrctrl_mask);
-       } else {
-               if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
-                       omap2xxx_clkdm_sleep(clkdm);
-       }
+       if (!clkdm_in_hwsup(clkdm) && clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
+               omap2xxx_clkdm_sleep(clkdm);
 
        return 0;
 }
index 058ce3c0873ecb7aa0f4356d4d596c152aea5f03..325a515765766c1757c5fd858e9bd8b3c226f44e 100644 (file)
@@ -241,9 +241,6 @@ int am33xx_cm_wait_module_ready(u16 inst, s16 cdoffs, u16 clkctrl_offs)
 {
        int i = 0;
 
-       if (!clkctrl_offs)
-               return 0;
-
        omap_test_timeout(_is_module_ready(inst, cdoffs, clkctrl_offs),
                          MAX_MODULE_READY_TIME, i);
 
index 5fa0b62e1a797954d7b8b4cb92eec25b88d0dde5..64f4bafe7bd9bb25a0054bf27139196ed2ce6256 100644 (file)
 #ifndef __ARCH_ARM_MACH_OMAP2_CM_33XX_H
 #define __ARCH_ARM_MACH_OMAP2_CM_33XX_H
 
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/io.h>
-
 #include "common.h"
 
 #include "cm.h"
 #include "cm-regbits-33xx.h"
-#include "cm33xx.h"
+#include "iomap.h"
 
 /* CM base address */
 #define AM33XX_CM_BASE         0x44e00000
 #define AM33XX_CM_CEFUSE_CEFUSE_CLKCTRL                        AM33XX_CM_REGADDR(AM33XX_CM_CEFUSE_MOD, 0x0020)
 
 
+#ifndef __ASSEMBLER__
 extern bool am33xx_cm_is_clkdm_in_hwsup(s16 inst, u16 cdoffs);
 extern void am33xx_cm_clkdm_enable_hwsup(s16 inst, u16 cdoffs);
 extern void am33xx_cm_clkdm_disable_hwsup(s16 inst, u16 cdoffs);
@@ -417,4 +413,5 @@ static inline int am33xx_cm_wait_module_ready(u16 inst, s16 cdoffs,
 }
 #endif
 
+#endif /* ASSEMBLER */
 #endif
index c2086f2e86b6f310fa579e5759851fae8bb73cd6..758721341d05a9f54435fec96f362342edc2044c 100644 (file)
@@ -186,7 +186,7 @@ static int omap3xxx_clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
                        continue; /* only happens if data is erroneous */
 
                mask |= 1 << cd->clkdm->dep_bit;
-               atomic_set(&cd->sleepdep_usecount, 0);
+               cd->sleepdep_usecount = 0;
        }
        omap2_cm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs,
                                    OMAP3430_CM_SLEEPDEP);
@@ -209,8 +209,8 @@ static int omap3xxx_clkdm_wakeup(struct clockdomain *clkdm)
 
 static void omap3xxx_clkdm_allow_idle(struct clockdomain *clkdm)
 {
-       if (atomic_read(&clkdm->usecount) > 0)
-               _clkdm_add_autodeps(clkdm);
+       if (clkdm->usecount > 0)
+               clkdm_add_autodeps(clkdm);
 
        omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                       clkdm->clktrctrl_mask);
@@ -221,14 +221,12 @@ static void omap3xxx_clkdm_deny_idle(struct clockdomain *clkdm)
        omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                        clkdm->clktrctrl_mask);
 
-       if (atomic_read(&clkdm->usecount) > 0)
-               _clkdm_del_autodeps(clkdm);
+       if (clkdm->usecount > 0)
+               clkdm_del_autodeps(clkdm);
 }
 
 static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm)
 {
-       bool hwsup = false;
-
        if (!clkdm->clktrctrl_mask)
                return 0;
 
@@ -243,14 +241,11 @@ static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm)
                return 0;
        }
 
-       hwsup = omap3xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
-                                             clkdm->clktrctrl_mask);
-
-       if (hwsup) {
+       if (clkdm_in_hwsup(clkdm)) {
                /* Disable HW transitions when we are changing deps */
                omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                                clkdm->clktrctrl_mask);
-               _clkdm_add_autodeps(clkdm);
+               clkdm_add_autodeps(clkdm);
                omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                               clkdm->clktrctrl_mask);
        } else {
@@ -263,8 +258,6 @@ static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm)
 
 static int omap3xxx_clkdm_clk_disable(struct clockdomain *clkdm)
 {
-       bool hwsup = false;
-
        if (!clkdm->clktrctrl_mask)
                return 0;
 
@@ -280,14 +273,11 @@ static int omap3xxx_clkdm_clk_disable(struct clockdomain *clkdm)
                return 0;
        }
 
-       hwsup = omap3xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
-                                             clkdm->clktrctrl_mask);
-
-       if (hwsup) {
+       if (clkdm_in_hwsup(clkdm)) {
                /* Disable HW transitions when we are changing deps */
                omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                                clkdm->clktrctrl_mask);
-               _clkdm_del_autodeps(clkdm);
+               clkdm_del_autodeps(clkdm);
                omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                               clkdm->clktrctrl_mask);
        } else {
index 7f9a464f01e987f62f7d98239357402687ad0486..f0290f5566fe0115cfc2bdf8476c85f84d000b3f 100644 (file)
@@ -393,7 +393,7 @@ static int omap4_clkdm_clear_all_wkup_sleep_deps(struct clockdomain *clkdm)
                        continue; /* only happens if data is erroneous */
 
                mask |= 1 << cd->clkdm->dep_bit;
-               atomic_set(&cd->wkdep_usecount, 0);
+               cd->wkdep_usecount = 0;
        }
 
        omap4_cminst_clear_inst_reg_bits(mask, clkdm->prcm_partition,
index 948bcaa82eb67e1030c3d6958c33c69f365203b1..e90beee19dd76544f1dba19fd1d01ebb871bb2ba 100644 (file)
@@ -59,7 +59,7 @@ static inline int omap3_pm_init(void)
 }
 #endif
 
-#if defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_PM) && (defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5))
 int omap4_pm_init(void);
 #else
 static inline int omap4_pm_init(void)
@@ -68,6 +68,15 @@ static inline int omap4_pm_init(void)
 }
 #endif
 
+#if defined(CONFIG_PM) && defined(CONFIG_SOC_AM33XX)
+int am33xx_pm_init(void);
+#else
+static inline int am33xx_pm_init(void)
+{
+       return 0;
+}
+#endif
+
 #ifdef CONFIG_OMAP_MUX
 int omap_mux_late_init(void);
 #else
@@ -106,9 +115,10 @@ void omap2430_init_late(void);
 void omap3430_init_late(void);
 void omap35xx_init_late(void);
 void omap3630_init_late(void);
+void am33xx_init_late(void);
 void am35xx_init_late(void);
 void ti81xx_init_late(void);
-void omap4430_init_late(void);
+void omap5_init_late(void);
 int omap2_common_pm_late_init(void);
 
 #if defined(CONFIG_SOC_OMAP2420) || defined(CONFIG_SOC_OMAP2430)
@@ -119,6 +129,14 @@ static inline void omap2xxx_restart(char mode, const char *cmd)
 }
 #endif
 
+#ifdef CONFIG_SOC_AM33XX
+void am33xx_restart(char mode, const char *cmd);
+#else
+static inline void am33xx_restart(char mode, const char *cmd)
+{
+}
+#endif
+
 #ifdef CONFIG_ARCH_OMAP3
 void omap3xxx_restart(char mode, const char *cmd);
 #else
@@ -238,14 +256,12 @@ extern void omap5_secondary_startup(void);
 
 #if defined(CONFIG_SMP) && defined(CONFIG_PM)
 extern int omap4_mpuss_init(void);
-extern int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state);
+extern int omap4_mpuss_enter_lowpower(unsigned int cpu, u8 fpwrst);
 extern int omap4_finish_suspend(unsigned long cpu_state);
 extern void omap4_cpu_resume(void);
-extern int omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state);
-extern u32 omap4_mpuss_read_prev_context_state(void);
+extern int omap4_mpuss_hotplug_cpu(unsigned int cpu, u8 fpwrst);
 #else
-static inline int omap4_enter_lowpower(unsigned int cpu,
-                                       unsigned int power_state)
+static inline int omap4_mpuss_enter_lowpower(unsigned int cpu, u8 fpwrst)
 {
        cpu_do_idle();
        return 0;
@@ -270,10 +286,6 @@ static inline int omap4_finish_suspend(unsigned long cpu_state)
 static inline void omap4_cpu_resume(void)
 {}
 
-static inline u32 omap4_mpuss_read_prev_context_state(void)
-{
-       return 0;
-}
 #endif
 
 struct omap_sdrc_params;
@@ -285,6 +297,12 @@ extern void omap_reserve(void);
 
 struct omap_hwmod;
 extern int omap_dss_reset(struct omap_hwmod *);
+int __init omapdss_init_of(void);
+
+/* AXI ERROR DEFINES */
+#define AXI_L2_ERROR (1 << 30)
+#define AXI_ASYNC_ERROR (1 << 29)
+#define AXI_ERROR (AXI_L2_ERROR | AXI_ASYNC_ERROR)
 
 #endif /* __ASSEMBLER__ */
 #endif /* __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H */
index 2adb2683f074de2e5846d06a0b370b8ff2b99a38..c5d54ae129af84694d6f56f88e730cf05f5fd385 100644 (file)
@@ -604,3 +604,23 @@ int omap3_ctrl_save_padconf(void)
 }
 
 #endif /* CONFIG_ARCH_OMAP3 && CONFIG_PM */
+
+#if defined(CONFIG_SOC_AM33XX) && defined(CONFIG_PM)
+void am33xx_txev_eoi(void)
+{
+       omap_ctrl_writel(AM33XX_M3_TXEV_ACK, AM33XX_CONTROL_M3_TXEV_EOI);
+}
+
+void am33xx_txev_enable(void)
+{
+       omap_ctrl_writel(AM33XX_M3_TXEV_ENABLE, AM33XX_CONTROL_M3_TXEV_EOI);
+}
+
+void am33xx_wkup_m3_ipc_cmd(struct am33xx_ipc_data *data)
+{
+       omap_ctrl_writel(data->resume_addr, AM33XX_CONTROL_IPC_MSG_REG0);
+       omap_ctrl_writel(data->sleep_mode, AM33XX_CONTROL_IPC_MSG_REG1);
+       omap_ctrl_writel(data->param1, AM33XX_CONTROL_IPC_MSG_REG2);
+       omap_ctrl_writel(data->param2, AM33XX_CONTROL_IPC_MSG_REG3);
+}
+#endif /* CONFIG_SOC_AM33XX && CONFIG_PM */
index e6c328128a0a3fbf0fc7745c5d3895b9c61c9759..371e9aeb6ae654974f7181ba65a9c2f397006911 100644 (file)
 
 /* CONTROL_STATUS bits */
 #define OMAP2_DEVICETYPE_MASK          (0x7 << 8)
+#define OMAP2_SYSBOOT_7_MASK           (1 << 7)
+#define OMAP2_SYSBOOT_6_MASK           (1 << 6)
 #define OMAP2_SYSBOOT_5_MASK           (1 << 5)
 #define OMAP2_SYSBOOT_4_MASK           (1 << 4)
 #define OMAP2_SYSBOOT_3_MASK           (1 << 3)
 #define AM33XX_CONTROL_STATUS_SYSBOOT1_WIDTH           0x2
 #define AM33XX_CONTROL_STATUS_SYSBOOT1_MASK            (0x3 << 22)
 
+#define AM33XX_DDR_IO_CTRL             0x0E04
+#define AM33XX_VTP0_CTRL_REG           0x0E0C
+
+/* AM33XX VTP0_CTRL_REG bits */
+#define AM33XX_VTP_CTRL_START_EN       (1 << 0)
+#define AM33XX_VTP_CTRL_LOCK_EN                (1 << 4)
+#define AM33XX_VTP_CTRL_READY          (1 << 5)
+#define AM33XX_VTP_CTRL_ENABLE         (1 << 6)
+
+/* AM33XX M3_TXEV_EOI register */
+#define AM33XX_CONTROL_M3_TXEV_EOI     0x1324
+
+#define AM33XX_M3_TXEV_ACK             (0x1 << 0)
+#define AM33XX_M3_TXEV_ENABLE          (0x0 << 0)
+
+/* AM33XX IPC message registers */
+#define AM33XX_CONTROL_IPC_MSG_REG0    0x1328
+#define AM33XX_CONTROL_IPC_MSG_REG1    0x132C
+#define AM33XX_CONTROL_IPC_MSG_REG2    0x1330
+#define AM33XX_CONTROL_IPC_MSG_REG3    0x1334
+#define AM33XX_CONTROL_IPC_MSG_REG4    0x1338
+#define AM33XX_CONTROL_IPC_MSG_REG5    0x133C
+#define AM33XX_CONTROL_IPC_MSG_REG6    0x1340
+#define AM33XX_CONTROL_IPC_MSG_REG7    0x1344
+
+#define AM33XX_DDR_CMD0_IOCTRL         0x1404
+#define AM33XX_DDR_CMD1_IOCTRL         0x1408
+#define AM33XX_DDR_CMD2_IOCTRL         0x140C
+#define AM33XX_DDR_DATA0_IOCTRL                0x1440
+#define AM33XX_DDR_DATA1_IOCTRL                0x1444
+
 /* CONTROL OMAP STATUS register to identify OMAP3 features */
 #define OMAP3_CONTROL_OMAP_STATUS      0x044c
 
@@ -417,6 +450,16 @@ extern void omap3630_ctrl_disable_rta(void);
 extern int omap3_ctrl_save_padconf(void);
 extern void omap2_set_globals_control(void __iomem *ctrl,
                                      void __iomem *ctrl_pad);
+struct am33xx_ipc_data {
+       u32 resume_addr;
+       u32 param1;
+       u32 param2;
+       u32 sleep_mode;
+};
+extern void am33xx_wkup_m3_ipc_cmd(struct am33xx_ipc_data *data);
+extern void am33xx_txev_eoi(void);
+extern void am33xx_txev_enable(void);
+
 #else
 #define omap_ctrl_base_get()           0
 #define omap_ctrl_readb(x)             0
index 22590dbe8f1453d2bf26058288a21e2070533af3..41652015fda9e03d0ad8da61272a5219b37d291d 100644 (file)
 
 /* Mach specific information to be recorded in the C-state driver_data */
 struct omap3_idle_statedata {
-       u32 mpu_state;
-       u32 core_state;
+       u8 mpu_fpwrst;
+       u8 core_fpwrst;
+       u8 per_min_fpwrst;
+       u8 flags;
 };
 
+/*
+ * Possible flag bits for struct omap3_idle_statedata.flags:
+ *
+ * OMAP_CPUIDLE_CX_NO_CLKDM_IDLE: don't allow the MPU clockdomain to go
+ *    inactive.  This in turn prevents the MPU DPLL from entering autoidle
+ *    mode, so wakeup latency is greatly reduced, at the cost of additional
+ *    energy consumption.  This also prevents the CORE clockdomain from
+ *    entering idle.
+ */
+#define OMAP_CPUIDLE_CX_NO_CLKDM_IDLE          BIT(0)
+
 static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd;
 
+/*
+ * Prevent PER OFF if CORE is not in RETention or OFF as this would
+ * disable PER wakeups completely.
+ */
 static struct omap3_idle_statedata omap3_idle_data[] = {
        {
-               .mpu_state = PWRDM_POWER_ON,
-               .core_state = PWRDM_POWER_ON,
+               .mpu_fpwrst = PWRDM_FUNC_PWRST_ON,
+               .core_fpwrst = PWRDM_FUNC_PWRST_ON,
+               /* In C1 do not allow PER state lower than CORE state */
+               .per_min_fpwrst = PWRDM_FUNC_PWRST_ON,
+               .flags = OMAP_CPUIDLE_CX_NO_CLKDM_IDLE,
        },
        {
-               .mpu_state = PWRDM_POWER_ON,
-               .core_state = PWRDM_POWER_ON,
+               .mpu_fpwrst = PWRDM_FUNC_PWRST_ON,
+               .core_fpwrst = PWRDM_FUNC_PWRST_ON,
+               .per_min_fpwrst = PWRDM_FUNC_PWRST_CSWR,
        },
        {
-               .mpu_state = PWRDM_POWER_RET,
-               .core_state = PWRDM_POWER_ON,
+               .mpu_fpwrst = PWRDM_FUNC_PWRST_CSWR,
+               .core_fpwrst = PWRDM_FUNC_PWRST_ON,
+               .per_min_fpwrst = PWRDM_FUNC_PWRST_CSWR,
        },
        {
-               .mpu_state = PWRDM_POWER_OFF,
-               .core_state = PWRDM_POWER_ON,
+               .mpu_fpwrst = PWRDM_FUNC_PWRST_OFF,
+               .core_fpwrst = PWRDM_FUNC_PWRST_ON,
+               .per_min_fpwrst = PWRDM_FUNC_PWRST_CSWR,
        },
        {
-               .mpu_state = PWRDM_POWER_RET,
-               .core_state = PWRDM_POWER_RET,
+               .mpu_fpwrst = PWRDM_FUNC_PWRST_CSWR,
+               .core_fpwrst = PWRDM_FUNC_PWRST_CSWR,
+               .per_min_fpwrst = PWRDM_FUNC_PWRST_OFF,
        },
        {
-               .mpu_state = PWRDM_POWER_OFF,
-               .core_state = PWRDM_POWER_RET,
+               .mpu_fpwrst = PWRDM_FUNC_PWRST_OFF,
+               .core_fpwrst = PWRDM_FUNC_PWRST_CSWR,
+               .per_min_fpwrst = PWRDM_FUNC_PWRST_OFF,
        },
        {
-               .mpu_state = PWRDM_POWER_OFF,
-               .core_state = PWRDM_POWER_OFF,
+               .mpu_fpwrst = PWRDM_FUNC_PWRST_OFF,
+               .core_fpwrst = PWRDM_FUNC_PWRST_OFF,
+               .per_min_fpwrst = PWRDM_FUNC_PWRST_OFF,
        },
 };
 
@@ -80,27 +106,23 @@ static int __omap3_enter_idle(struct cpuidle_device *dev,
                                int index)
 {
        struct omap3_idle_statedata *cx = &omap3_idle_data[index];
-       u32 mpu_state = cx->mpu_state, core_state = cx->core_state;
-
-       local_fiq_disable();
-
-       pwrdm_set_next_pwrst(mpu_pd, mpu_state);
-       pwrdm_set_next_pwrst(core_pd, core_state);
 
        if (omap_irq_pending() || need_resched())
                goto return_sleep_time;
 
        /* Deny idle for C1 */
-       if (index == 0) {
+       if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE) {
                clkdm_deny_idle(mpu_pd->pwrdm_clkdms[0]);
-               clkdm_deny_idle(core_pd->pwrdm_clkdms[0]);
+       } else {
+               pwrdm_set_next_fpwrst(mpu_pd, cx->mpu_fpwrst);
+               pwrdm_set_next_fpwrst(core_pd, cx->core_fpwrst);
        }
 
        /*
         * Call idle CPU PM enter notifier chain so that
         * VFP context is saved.
         */
-       if (mpu_state == PWRDM_POWER_OFF)
+       if (cx->mpu_fpwrst == PWRDM_FUNC_PWRST_OFF)
                cpu_pm_enter();
 
        /* Execute ARM wfi */
@@ -110,19 +132,15 @@ static int __omap3_enter_idle(struct cpuidle_device *dev,
         * Call idle CPU PM enter notifier chain to restore
         * VFP context.
         */
-       if (pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF)
+       if (cx->mpu_fpwrst == PWRDM_FUNC_PWRST_OFF &&
+           pwrdm_read_prev_fpwrst(mpu_pd) == PWRDM_FUNC_PWRST_OFF)
                cpu_pm_exit();
 
        /* Re-allow idle for C1 */
-       if (index == 0) {
+       if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE)
                clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]);
-               clkdm_allow_idle(core_pd->pwrdm_clkdms[0]);
-       }
 
 return_sleep_time:
-
-       local_fiq_enable();
-
        return index;
 }
 
@@ -159,25 +177,25 @@ static int next_valid_state(struct cpuidle_device *dev,
                            struct cpuidle_driver *drv, int index)
 {
        struct omap3_idle_statedata *cx = &omap3_idle_data[index];
-       u32 mpu_deepest_state = PWRDM_POWER_RET;
-       u32 core_deepest_state = PWRDM_POWER_RET;
+       u8 mpu_deepest_fpwrst = PWRDM_FUNC_PWRST_CSWR;
+       u8 core_deepest_fpwrst = PWRDM_FUNC_PWRST_CSWR;
        int idx;
        int next_index = 0; /* C1 is the default value */
 
        if (enable_off_mode) {
-               mpu_deepest_state = PWRDM_POWER_OFF;
+               mpu_deepest_fpwrst = PWRDM_FUNC_PWRST_OFF;
                /*
                 * Erratum i583: valable for ES rev < Es1.2 on 3630.
                 * CORE OFF mode is not supported in a stable form, restrict
                 * instead the CORE state to RET.
                 */
                if (!IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583))
-                       core_deepest_state = PWRDM_POWER_OFF;
+                       core_deepest_fpwrst = PWRDM_FUNC_PWRST_OFF;
        }
 
        /* Check if current state is valid */
-       if ((cx->mpu_state >= mpu_deepest_state) &&
-           (cx->core_state >= core_deepest_state))
+       if ((cx->mpu_fpwrst >= mpu_deepest_fpwrst) &&
+           (cx->core_fpwrst >= core_deepest_fpwrst))
                return index;
 
        /*
@@ -185,9 +203,9 @@ static int next_valid_state(struct cpuidle_device *dev,
         * Start search from the next (lower) state.
         */
        for (idx = index - 1; idx >= 0; idx--) {
-               cx =  &omap3_idle_data[idx];
-               if ((cx->mpu_state >= mpu_deepest_state) &&
-                   (cx->core_state >= core_deepest_state)) {
+               cx = &omap3_idle_data[idx];
+               if ((cx->mpu_fpwrst >= mpu_deepest_fpwrst) &&
+                   (cx->core_fpwrst >= core_deepest_fpwrst)) {
                        next_index = idx;
                        break;
                }
@@ -209,16 +227,17 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
                               struct cpuidle_driver *drv,
                               int index)
 {
-       int new_state_idx;
-       u32 core_next_state, per_next_state = 0, per_saved_state = 0;
+       int new_state_idx, ret;
+       int per_next_fpwrst, per_saved_fpwrst;
        struct omap3_idle_statedata *cx;
-       int ret;
 
        /*
         * Use only C1 if CAM is active.
         * CAM does not have wakeup capability in OMAP3.
+        * XXX This workaround belongs in the hwmod code & data
+        * as a hwmod flag
         */
-       if (pwrdm_read_pwrst(cam_pd) == PWRDM_POWER_ON)
+       if (pwrdm_read_fpwrst(cam_pd) == PWRDM_FUNC_PWRST_ON)
                new_state_idx = drv->safe_state_index;
        else
                new_state_idx = next_valid_state(dev, drv, index);
@@ -233,31 +252,20 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 
        /* Program PER state */
        cx = &omap3_idle_data[new_state_idx];
-       core_next_state = cx->core_state;
-       per_next_state = per_saved_state = pwrdm_read_next_pwrst(per_pd);
-       if (new_state_idx == 0) {
-               /* In C1 do not allow PER state lower than CORE state */
-               if (per_next_state < core_next_state)
-                       per_next_state = core_next_state;
-       } else {
-               /*
-                * Prevent PER OFF if CORE is not in RETention or OFF as this
-                * would disable PER wakeups completely.
-                */
-               if ((per_next_state == PWRDM_POWER_OFF) &&
-                   (core_next_state > PWRDM_POWER_RET))
-                       per_next_state = PWRDM_POWER_RET;
-       }
 
-       /* Are we changing PER target state? */
-       if (per_next_state != per_saved_state)
-               pwrdm_set_next_pwrst(per_pd, per_next_state);
+       per_next_fpwrst = pwrdm_read_next_fpwrst(per_pd);
+       WARN_ON(per_next_fpwrst < 0);
+       per_saved_fpwrst = per_next_fpwrst;
+       if (per_next_fpwrst < cx->per_min_fpwrst) {
+               per_next_fpwrst = cx->per_min_fpwrst;
+               WARN_ON(pwrdm_set_next_fpwrst(per_pd, per_next_fpwrst));
+       }
 
        ret = omap3_enter_idle(dev, drv, new_state_idx);
 
        /* Restore original PER state if it was modified */
-       if (per_next_state != per_saved_state)
-               pwrdm_set_next_pwrst(per_pd, per_saved_state);
+       if (per_next_fpwrst != per_saved_fpwrst)
+               WARN_ON(pwrdm_set_next_fpwrst(per_pd, per_saved_fpwrst));
 
        return ret;
 }
similarity index 55%
rename from arch/arm/mach-omap2/cpuidle44xx.c
rename to arch/arm/mach-omap2/cpuidle_omap4plus.c
index d639aef0deda3c1a8f28cb61fdebe84a5626126f..9ebeed6923c2258f0b2f62927f63d8e5a7127e55 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * OMAP4 CPU idle Routines
+ * OMAP4PLUS CPU idle Routines
  *
- * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011-2013 Texas Instruments, Inc.
  * Santosh Shilimkar <santosh.shilimkar@ti.com>
  * Rajendra Nayak <rnayak@ti.com>
  *
 #include "pm.h"
 #include "prm.h"
 #include "clockdomain.h"
+#include "soc.h"
 
 /* Machine specific information */
-struct omap4_idle_statedata {
-       u32 cpu_state;
-       u32 mpu_logic_state;
-       u32 mpu_state;
+struct idle_statedata {
+       u8 cpu_pwrst;
+       u8 mpu_pwrst;
+       u32 mpu_state_vote;
 };
 
-static struct omap4_idle_statedata omap4_idle_data[] = {
+static struct idle_statedata omap4_idle_data[] = {
        {
-               .cpu_state = PWRDM_POWER_ON,
-               .mpu_state = PWRDM_POWER_ON,
-               .mpu_logic_state = PWRDM_POWER_RET,
+               .cpu_pwrst = PWRDM_FUNC_PWRST_ON,
+               .mpu_pwrst = PWRDM_FUNC_PWRST_ON,
        },
        {
-               .cpu_state = PWRDM_POWER_OFF,
-               .mpu_state = PWRDM_POWER_RET,
-               .mpu_logic_state = PWRDM_POWER_RET,
+               .cpu_pwrst = PWRDM_FUNC_PWRST_OFF,
+               .mpu_pwrst = PWRDM_FUNC_PWRST_CSWR,
        },
        {
-               .cpu_state = PWRDM_POWER_OFF,
-               .mpu_state = PWRDM_POWER_RET,
-               .mpu_logic_state = PWRDM_POWER_OFF,
+               .cpu_pwrst = PWRDM_FUNC_PWRST_OFF,
+               .mpu_pwrst = PWRDM_FUNC_PWRST_OSWR,
+       },
+};
+
+static struct idle_statedata omap5_idle_data[] = {
+       {
+               .cpu_pwrst = PWRDM_FUNC_PWRST_ON,
+               .mpu_pwrst = PWRDM_FUNC_PWRST_ON,
+       },
+       {
+               .cpu_pwrst = PWRDM_FUNC_PWRST_CSWR,
+               .mpu_pwrst = PWRDM_FUNC_PWRST_CSWR,
+       },
+       {
+               .cpu_pwrst = PWRDM_FUNC_PWRST_OFF,
+               .mpu_pwrst = PWRDM_FUNC_PWRST_OSWR,
        },
 };
 
@@ -53,11 +66,13 @@ static struct clockdomain *cpu_clkdm[NR_CPUS];
 
 static atomic_t abort_barrier;
 static bool cpu_done[NR_CPUS];
+static struct idle_statedata *state_ptr;
+static DEFINE_RAW_SPINLOCK(mpu_lock);
 
 /* Private functions */
 
 /**
- * omap4_enter_idle_coupled_[simple/coupled] - OMAP4 cpuidle entry functions
+ * omap_enter_idle_[simple/smp/coupled] - OMAP4PLUS cpuidle entry functions
  * @dev: cpuidle device
  * @drv: cpuidle driver
  * @index: the index of state to be entered
@@ -66,26 +81,21 @@ static bool cpu_done[NR_CPUS];
  * specified low power state selected by the governor.
  * Returns the amount of time spent in the low power state.
  */
-static int omap4_enter_idle_simple(struct cpuidle_device *dev,
+static int omap_enter_idle_simple(struct cpuidle_device *dev,
                        struct cpuidle_driver *drv,
                        int index)
 {
-       local_fiq_disable();
        omap_do_wfi();
-       local_fiq_enable();
-
        return index;
 }
 
-static int omap4_enter_idle_coupled(struct cpuidle_device *dev,
+static int omap_enter_idle_coupled(struct cpuidle_device *dev,
                        struct cpuidle_driver *drv,
                        int index)
 {
-       struct omap4_idle_statedata *cx = &omap4_idle_data[index];
+       struct idle_statedata *cx = state_ptr + index;
        int cpu_id = smp_processor_id();
 
-       local_fiq_disable();
-
        /*
         * CPU0 has to wait and stay ON until CPU1 is OFF state.
         * This is necessary to honour hardware recommondation
@@ -93,7 +103,7 @@ static int omap4_enter_idle_coupled(struct cpuidle_device *dev,
         * out of coherency and in OFF mode.
         */
        if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
-               while (pwrdm_read_pwrst(cpu_pd[1]) != PWRDM_POWER_OFF) {
+               while (pwrdm_read_fpwrst(cpu_pd[1]) != PWRDM_FUNC_PWRST_OFF) {
                        cpu_relax();
 
                        /*
@@ -118,24 +128,25 @@ static int omap4_enter_idle_coupled(struct cpuidle_device *dev,
        cpu_pm_enter();
 
        if (dev->cpu == 0) {
-               pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
-               omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
+               WARN_ON(pwrdm_set_next_fpwrst(mpu_pd, cx->mpu_pwrst));
 
                /*
                 * Call idle CPU cluster PM enter notifier chain
                 * to save GIC and wakeupgen context.
                 */
-               if ((cx->mpu_state == PWRDM_POWER_RET) &&
-                       (cx->mpu_logic_state == PWRDM_POWER_OFF))
-                               cpu_cluster_pm_enter();
+               if (cx->mpu_pwrst == PWRDM_FUNC_PWRST_OSWR)
+                       cpu_cluster_pm_enter();
        }
 
-       omap4_enter_lowpower(dev->cpu, cx->cpu_state);
+       omap4_mpuss_enter_lowpower(dev->cpu, cx->cpu_pwrst);
        cpu_done[dev->cpu] = true;
 
        /* Wakeup CPU1 only if it is not offlined */
        if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
+               /* Restore MPU PD state post idle */
+               pwrdm_set_next_fpwrst(mpu_pd, PWRDM_FUNC_PWRST_ON);
                clkdm_wakeup(cpu_clkdm[1]);
+               pwrdm_set_next_fpwrst(cpu_pd[1], PWRDM_FUNC_PWRST_ON);
                clkdm_allow_idle(cpu_clkdm[1]);
        }
 
@@ -149,7 +160,7 @@ static int omap4_enter_idle_coupled(struct cpuidle_device *dev,
         * Call idle CPU cluster PM exit notifier chain
         * to restore GIC and wakeupgen context.
         */
-       if (omap4_mpuss_read_prev_context_state())
+       if (cx->mpu_pwrst == PWRDM_FUNC_PWRST_OSWR)
                cpu_cluster_pm_exit();
 
        clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
@@ -158,7 +169,36 @@ fail:
        cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
        cpu_done[dev->cpu] = false;
 
-       local_fiq_enable();
+       return index;
+}
+
+static int omap_enter_idle_smp(struct cpuidle_device *dev,
+                       struct cpuidle_driver *drv,
+                       int index)
+{
+       struct idle_statedata *cx = state_ptr + index;
+       int cpu_id = smp_processor_id();
+       unsigned long flag;
+
+       clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
+
+       raw_spin_lock_irqsave(&mpu_lock, flag);
+       cx->mpu_state_vote++;
+       if (cx->mpu_state_vote == num_online_cpus())
+               pwrdm_set_next_fpwrst(mpu_pd, cx->mpu_pwrst);
+
+       raw_spin_unlock_irqrestore(&mpu_lock, flag);
+
+       omap4_mpuss_enter_lowpower(dev->cpu, cx->cpu_pwrst);
+
+       raw_spin_lock_irqsave(&mpu_lock, flag);
+       if (cx->mpu_state_vote == num_online_cpus())
+               pwrdm_set_next_fpwrst(mpu_pd, PWRDM_FUNC_PWRST_ON);
+
+       cx->mpu_state_vote--;
+       raw_spin_unlock_irqrestore(&mpu_lock, flag);
+
+       clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
 
        return index;
 }
@@ -173,7 +213,7 @@ static void omap_setup_broadcast_timer(void *arg)
        clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
 }
 
-static DEFINE_PER_CPU(struct cpuidle_device, omap4_idle_dev);
+static DEFINE_PER_CPU(struct cpuidle_device, omap_idle_dev);
 
 static struct cpuidle_driver omap4_idle_driver = {
        .name                           = "omap4_idle",
@@ -185,7 +225,7 @@ static struct cpuidle_driver omap4_idle_driver = {
                        .exit_latency = 2 + 2,
                        .target_residency = 5,
                        .flags = CPUIDLE_FLAG_TIME_VALID,
-                       .enter = omap4_enter_idle_simple,
+                       .enter = omap_enter_idle_simple,
                        .name = "C1",
                        .desc = "MPUSS ON"
                },
@@ -194,7 +234,7 @@ static struct cpuidle_driver omap4_idle_driver = {
                        .exit_latency = 328 + 440,
                        .target_residency = 960,
                        .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
-                       .enter = omap4_enter_idle_coupled,
+                       .enter = omap_enter_idle_coupled,
                        .name = "C2",
                        .desc = "MPUSS CSWR",
                },
@@ -203,7 +243,7 @@ static struct cpuidle_driver omap4_idle_driver = {
                        .exit_latency = 460 + 518,
                        .target_residency = 1100,
                        .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
-                       .enter = omap4_enter_idle_coupled,
+                       .enter = omap_enter_idle_coupled,
                        .name = "C3",
                        .desc = "MPUSS OSWR",
                },
@@ -212,6 +252,43 @@ static struct cpuidle_driver omap4_idle_driver = {
        .safe_state_index = 0,
 };
 
+static struct cpuidle_driver omap5_idle_driver = {
+       .name                           = "omap5_idle",
+       .owner                          = THIS_MODULE,
+       .en_core_tk_irqen               = 1,
+       .states = {
+               {
+                       /* C1 - CPU0 ON + CPU1 ON + MPU ON */
+                       .exit_latency = 2 + 2,
+                       .target_residency = 5,
+                       .flags = CPUIDLE_FLAG_TIME_VALID,
+                       .enter = omap_enter_idle_simple,
+                       .name = "C1",
+                       .desc = "MPUSS ON"
+               },
+               {
+                       /* C2 - CPU0 CSWR + CPU1 CSWR + MPU CSWR */
+                       .exit_latency = 16 + 16,
+                       .target_residency = 40,
+                       .flags = CPUIDLE_FLAG_TIME_VALID,
+                       .enter = omap_enter_idle_smp,
+                       .name = "C2",
+                       .desc = "MPUSS CSWR",
+               },
+               {
+                       /* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
+                       .exit_latency = 460 + 518,
+                       .target_residency = 1100,
+                       .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
+                       .enter = omap_enter_idle_coupled,
+                       .name = "C3",
+                       .desc = "MPUSS OSWR",
+               },
+       },
+       .state_count = ARRAY_SIZE(omap5_idle_data),
+       .safe_state_index = 0,
+};
+
 /* Public functions */
 
 /**
@@ -223,8 +300,9 @@ static struct cpuidle_driver omap4_idle_driver = {
 int __init omap4_idle_init(void)
 {
        struct cpuidle_device *dev;
-       unsigned int cpu_id = 0;
+       unsigned cpu_id = 0;
 
+       /* Configure the broadcast timer on each cpu */
        mpu_pd = pwrdm_lookup("mpu_pwrdm");
        cpu_pd[0] = pwrdm_lookup("cpu0_pwrdm");
        cpu_pd[1] = pwrdm_lookup("cpu1_pwrdm");
@@ -240,12 +318,18 @@ int __init omap4_idle_init(void)
        on_each_cpu(omap_setup_broadcast_timer, NULL, 1);
 
        for_each_cpu(cpu_id, cpu_online_mask) {
-               dev = &per_cpu(omap4_idle_dev, cpu_id);
+               dev = &per_cpu(omap_idle_dev, cpu_id);
                dev->cpu = cpu_id;
 #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
                dev->coupled_cpus = *cpu_online_mask;
 #endif
-               cpuidle_register_driver(&omap4_idle_driver);
+               if (cpu_is_omap44xx()) {
+                       state_ptr = &omap4_idle_data[0];
+                       cpuidle_register_driver(&omap4_idle_driver);
+               } else if (soc_is_omap54xx()) {
+                       state_ptr = &omap5_idle_data[0];
+                       cpuidle_register_driver(&omap5_idle_driver);
+               }
 
                if (cpuidle_register_device(dev)) {
                        pr_err("%s: CPUidle register failed\n", __func__);
index 626f3ea3142f55dfd0bcd5337a9758eab5d0cf71..20ccfff7ecc5354f088c4f3492a2caa40d660b7f 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/pinctrl/machine.h>
 #include <linux/platform_data/omap4-keypad.h>
 #include <linux/platform_data/omap_ocp2scp.h>
+#include <linux/usb/omap_control_usb.h>
+#include <linux/platform_data/mailbox-omap.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/map.h>
@@ -254,6 +256,49 @@ static inline void omap_init_camera(void)
 #endif
 }
 
+#if IS_ENABLED(CONFIG_OMAP_CONTROL_USB)
+static struct omap_control_usb_platform_data omap4_control_usb_pdata = {
+       .type = 1,
+};
+
+struct resource omap4_control_usb_res[] = {
+       {
+               .name   = "control_dev_conf",
+               .start  = 0x4a002300,
+               .end    = 0x4a002303,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .name   = "otghs_control",
+               .start  = 0x4a00233c,
+               .end    = 0x4a00233f,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device omap4_control_usb = {
+       .name = "omap-control-usb",
+       .id = -1,
+       .dev = {
+               .platform_data = &omap4_control_usb_pdata,
+       },
+       .num_resources = 2,
+       .resource = omap4_control_usb_res,
+};
+
+static inline void __init omap_init_control_usb(void)
+{
+       if (!cpu_is_omap44xx())
+               return;
+
+       if (platform_device_register(&omap4_control_usb))
+               pr_err("Error registering omap_control_usb device\n");
+}
+
+#else
+static inline void omap_init_control_usb(void) { }
+#endif /* CONFIG_OMAP_CONTROL_USB */
+
 int __init omap4_keyboard_init(struct omap4_keypad_platform_data
                        *sdp4430_keypad_data, struct omap_board_data *bdata)
 {
@@ -285,25 +330,32 @@ int __init omap4_keyboard_init(struct omap4_keypad_platform_data
        return 0;
 }
 
-#if defined(CONFIG_OMAP_MBOX_FWK) || defined(CONFIG_OMAP_MBOX_FWK_MODULE)
+#if defined(CONFIG_OMAP2PLUS_MBOX) || defined(CONFIG_OMAP2PLUS_MBOX_MODULE)
 static inline void __init omap_init_mbox(void)
 {
        struct omap_hwmod *oh;
        struct platform_device *pdev;
+       struct omap_mbox_pdata *pdata;
 
        oh = omap_hwmod_lookup("mailbox");
        if (!oh) {
                pr_err("%s: unable to find hwmod\n", __func__);
                return;
        }
+       if (!oh->dev_attr) {
+               pr_err("%s: hwmod doesn't have valid attrs\n", __func__);
+               return;
+       }
 
-       pdev = omap_device_build("omap-mailbox", -1, oh, NULL, 0, NULL, 0, 0);
+       pdata = (struct omap_mbox_pdata *)oh->dev_attr;
+       pdev = omap_device_build("omap-mailbox", -1, oh, pdata, sizeof(*pdata),
+                                                               NULL, 0, 0);
        WARN(IS_ERR(pdev), "%s: could not build device, err %ld\n",
                                                __func__, PTR_ERR(pdev));
 }
 #else
 static inline void omap_init_mbox(void) { }
-#endif /* CONFIG_OMAP_MBOX_FWK */
+#endif /* CONFIG_OMAP2PLUS_MBOX */
 
 static inline void omap_init_sti(void) {}
 
@@ -323,6 +375,36 @@ static void omap_init_audio(void)
 static inline void omap_init_audio(void) {}
 #endif
 
+#if defined(CONFIG_SND_OMAP_SOC_MCASP) || \
+       defined(CONFIG_SND_OMAP_SOC_MCASP_MODULE)
+static struct omap_device_pm_latency omap_mcasp_latency[] = {
+       {
+               .deactivate_func = omap_device_idle_hwmods,
+               .activate_func = omap_device_enable_hwmods,
+               .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
+       },
+};
+
+static void omap_init_mcasp(void)
+{
+       struct omap_hwmod *oh;
+       struct platform_device *pdev;
+
+       oh = omap_hwmod_lookup("mcasp");
+       if (!oh) {
+               pr_err("could not look up mcasp hw_mod\n");
+               return;
+       }
+
+       pdev = omap_device_build("omap-mcasp", -1, oh, NULL, 0,
+                               omap_mcasp_latency,
+                               ARRAY_SIZE(omap_mcasp_latency), 0);
+       WARN(IS_ERR(pdev), "Can't build omap_device for omap-mcasp-audio.\n");
+}
+#else
+static inline void omap_init_mcasp(void) {}
+#endif
+
 #if defined(CONFIG_SND_OMAP_SOC_MCPDM) || \
                defined(CONFIG_SND_OMAP_SOC_MCPDM_MODULE)
 
@@ -365,34 +447,26 @@ static void __init omap_init_dmic(void)
 static inline void omap_init_dmic(void) {}
 #endif
 
-#if defined(CONFIG_SND_OMAP_SOC_OMAP_HDMI) || \
-               defined(CONFIG_SND_OMAP_SOC_OMAP_HDMI_MODULE)
-
-static struct platform_device omap_hdmi_audio = {
-       .name   = "omap-hdmi-audio",
-       .id     = -1,
-};
+#if defined(CONFIG_SND_OMAP_SOC_ABE) || \
+       defined(CONFIG_SND_OMAP_SOC_ABE_MODULE)
 
-static void __init omap_init_hdmi_audio(void)
+static void omap_init_aess(void)
 {
        struct omap_hwmod *oh;
        struct platform_device *pdev;
 
-       oh = omap_hwmod_lookup("dss_hdmi");
+       oh = omap_hwmod_lookup("aess");
        if (!oh) {
-               printk(KERN_ERR "Could not look up dss_hdmi hw_mod\n");
+               pr_err("Could not look up aess hw_mod\n");
                return;
        }
 
-       pdev = omap_device_build("omap-hdmi-audio-dai",
-               -1, oh, NULL, 0, NULL, 0, 0);
+       pdev = omap_device_build("aess", -1, oh, NULL, 0, NULL, 0, 0);
        WARN(IS_ERR(pdev),
-            "Can't build omap_device for omap-hdmi-audio-dai.\n");
-
-       platform_device_register(&omap_hdmi_audio);
+            "Could not build omap_device for omap-aess-audio\n");
 }
 #else
-static inline void omap_init_hdmi_audio(void) {}
+static inline void omap_init_aess(void) {}
 #endif
 
 #if defined(CONFIG_SPI_OMAP24XX) || defined(CONFIG_SPI_OMAP24XX_MODULE)
@@ -464,140 +538,31 @@ static void omap_init_rng(void)
        WARN(IS_ERR(pdev), "Can't build omap_device for omap_rng\n");
 }
 
-#if defined(CONFIG_CRYPTO_DEV_OMAP_SHAM) || defined(CONFIG_CRYPTO_DEV_OMAP_SHAM_MODULE)
-
-#ifdef CONFIG_ARCH_OMAP2
-static struct resource omap2_sham_resources[] = {
-       {
-               .start  = OMAP24XX_SEC_SHA1MD5_BASE,
-               .end    = OMAP24XX_SEC_SHA1MD5_BASE + 0x64,
-               .flags  = IORESOURCE_MEM,
-       },
-       {
-               .start  = 51 + OMAP_INTC_START,
-               .flags  = IORESOURCE_IRQ,
-       }
-};
-static int omap2_sham_resources_sz = ARRAY_SIZE(omap2_sham_resources);
-#else
-#define omap2_sham_resources           NULL
-#define omap2_sham_resources_sz                0
-#endif
-
-#ifdef CONFIG_ARCH_OMAP3
-static struct resource omap3_sham_resources[] = {
-       {
-               .start  = OMAP34XX_SEC_SHA1MD5_BASE,
-               .end    = OMAP34XX_SEC_SHA1MD5_BASE + 0x64,
-               .flags  = IORESOURCE_MEM,
-       },
-       {
-               .start  = 49 + OMAP_INTC_START,
-               .flags  = IORESOURCE_IRQ,
-       },
-       {
-               .start  = OMAP34XX_DMA_SHA1MD5_RX,
-               .flags  = IORESOURCE_DMA,
-       }
-};
-static int omap3_sham_resources_sz = ARRAY_SIZE(omap3_sham_resources);
-#else
-#define omap3_sham_resources           NULL
-#define omap3_sham_resources_sz                0
-#endif
-
-static struct platform_device sham_device = {
-       .name           = "omap-sham",
-       .id             = -1,
-};
-
-static void omap_init_sham(void)
+static void __init omap_init_sham(void)
 {
-       if (cpu_is_omap24xx()) {
-               sham_device.resource = omap2_sham_resources;
-               sham_device.num_resources = omap2_sham_resources_sz;
-       } else if (cpu_is_omap34xx()) {
-               sham_device.resource = omap3_sham_resources;
-               sham_device.num_resources = omap3_sham_resources_sz;
-       } else {
-               pr_err("%s: platform not supported\n", __func__);
-               return;
-       }
-       platform_device_register(&sham_device);
-}
-#else
-static inline void omap_init_sham(void) { }
-#endif
-
-#if defined(CONFIG_CRYPTO_DEV_OMAP_AES) || defined(CONFIG_CRYPTO_DEV_OMAP_AES_MODULE)
-
-#ifdef CONFIG_ARCH_OMAP2
-static struct resource omap2_aes_resources[] = {
-       {
-               .start  = OMAP24XX_SEC_AES_BASE,
-               .end    = OMAP24XX_SEC_AES_BASE + 0x4C,
-               .flags  = IORESOURCE_MEM,
-       },
-       {
-               .start  = OMAP24XX_DMA_AES_TX,
-               .flags  = IORESOURCE_DMA,
-       },
-       {
-               .start  = OMAP24XX_DMA_AES_RX,
-               .flags  = IORESOURCE_DMA,
-       }
-};
-static int omap2_aes_resources_sz = ARRAY_SIZE(omap2_aes_resources);
-#else
-#define omap2_aes_resources            NULL
-#define omap2_aes_resources_sz         0
-#endif
+       struct omap_hwmod *oh;
+       struct platform_device *pdev;
 
-#ifdef CONFIG_ARCH_OMAP3
-static struct resource omap3_aes_resources[] = {
-       {
-               .start  = OMAP34XX_SEC_AES_BASE,
-               .end    = OMAP34XX_SEC_AES_BASE + 0x4C,
-               .flags  = IORESOURCE_MEM,
-       },
-       {
-               .start  = OMAP34XX_DMA_AES2_TX,
-               .flags  = IORESOURCE_DMA,
-       },
-       {
-               .start  = OMAP34XX_DMA_AES2_RX,
-               .flags  = IORESOURCE_DMA,
-       }
-};
-static int omap3_aes_resources_sz = ARRAY_SIZE(omap3_aes_resources);
-#else
-#define omap3_aes_resources            NULL
-#define omap3_aes_resources_sz         0
-#endif
+       oh = omap_hwmod_lookup("sham");
+       if (!oh)
+               return;
 
-static struct platform_device aes_device = {
-       .name           = "omap-aes",
-       .id             = -1,
-};
+       pdev = omap_device_build("omap-sham", -1, oh, NULL, 0, NULL, 0, 0);
+       WARN(IS_ERR(pdev), "Can't build omap_device for omap-sham\n");
+}
 
-static void omap_init_aes(void)
+static void __init omap_init_aes(void)
 {
-       if (cpu_is_omap24xx()) {
-               aes_device.resource = omap2_aes_resources;
-               aes_device.num_resources = omap2_aes_resources_sz;
-       } else if (cpu_is_omap34xx()) {
-               aes_device.resource = omap3_aes_resources;
-               aes_device.num_resources = omap3_aes_resources_sz;
-       } else {
-               pr_err("%s: platform not supported\n", __func__);
+       struct omap_hwmod *oh;
+       struct platform_device *pdev;
+
+       oh = omap_hwmod_lookup("aes");
+       if (!oh)
                return;
-       }
-       platform_device_register(&aes_device);
-}
 
-#else
-static inline void omap_init_aes(void) { }
-#endif
+       pdev = omap_device_build("omap-aes", -1, oh, NULL, 0, NULL, 0, 0);
+       WARN(IS_ERR(pdev), "Can't build omap_device for omap-aes\n");
+}
 
 /*-------------------------------------------------------------------------*/
 
@@ -717,18 +682,19 @@ static int __init omap2_init_devices(void)
         */
        omap_init_audio();
        omap_init_camera();
-       omap_init_hdmi_audio();
        omap_init_mbox();
        /* If dtb is there, the devices will be created dynamically */
        if (!of_have_populated_dt()) {
+               omap_init_control_usb();
+               omap_init_aess();
                omap_init_dmic();
                omap_init_mcpdm();
                omap_init_mcspi();
+               omap_init_sham();
+               omap_init_aes();
        }
        omap_init_sti();
        omap_init_rng();
-       omap_init_sham();
-       omap_init_aes();
        omap_init_vout();
        omap_init_ocp2scp();
 
index cc75aaf6e7642396947a9dac47c12115edc45fae..86a0051fc2aa9a3fd6ca4f8903a387bca75e7f50 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
 
 #include <video/omapdss.h>
 #include "omap_hwmod.h"
@@ -164,6 +166,36 @@ static int omap4_dsi_mux_pads(int dsi_id, unsigned lanes)
        return 0;
 }
 
+#define CONTROL_PAD_BASE       0x4A002800
+#define CONTROL_DSIPHY         0x614
+
+static int omap5_dsi_mux_pads(int dsi_id, unsigned lanes)
+{
+       u32 enable_mask, enable_shift, reg;
+       void __iomem *ctrl_pad_base = NULL;
+
+       ctrl_pad_base = ioremap(CONTROL_PAD_BASE, SZ_4K);
+       if (!ctrl_pad_base)
+               return -ENXIO;
+
+       if (dsi_id == 0) {
+               enable_mask = OMAP4_DSI1_LANEENABLE_MASK;
+               enable_shift = OMAP4_DSI1_LANEENABLE_SHIFT;
+       } else if (dsi_id == 1) {
+               enable_mask = OMAP4_DSI2_LANEENABLE_MASK;
+               enable_shift = OMAP4_DSI2_LANEENABLE_SHIFT;
+       } else {
+               return -ENODEV;
+       }
+
+       reg = __raw_readl(ctrl_pad_base + CONTROL_DSIPHY);
+       reg &= ~enable_mask;
+       reg |= (lanes << enable_shift) & enable_mask;
+       __raw_writel(reg, ctrl_pad_base + CONTROL_DSIPHY);
+
+       return 0;
+}
+
 int __init omap_hdmi_init(enum omap_hdmi_flags flags)
 {
        if (cpu_is_omap44xx()) {
@@ -178,6 +210,8 @@ static int omap_dsi_enable_pads(int dsi_id, unsigned lane_mask)
 {
        if (cpu_is_omap44xx())
                return omap4_dsi_mux_pads(dsi_id, lane_mask);
+       else if (soc_is_omap54xx())
+               return omap5_dsi_mux_pads(dsi_id, lane_mask);
 
        return 0;
 }
@@ -186,6 +220,8 @@ static void omap_dsi_disable_pads(int dsi_id, unsigned lane_mask)
 {
        if (cpu_is_omap44xx())
                omap4_dsi_mux_pads(dsi_id, 0);
+       else if (soc_is_omap54xx())
+               omap5_dsi_mux_pads(dsi_id, 0);
 }
 
 static int omap_dss_set_min_bus_tput(struct device *dev, unsigned long tput)
@@ -416,6 +452,37 @@ int __init omap_display_init(struct omap_dss_board_info *board_data)
                }
        }
 
+       /* Create devices for HDMI audio drivers */
+       for (i = 0; i < board_data->num_devices; i++) {
+               struct platform_device *au_pdev;
+               struct omap_dss_device *dssdev = board_data->devices[i];
+               bool card_created = false;
+
+               if (dssdev->type != OMAP_DISPLAY_TYPE_HDMI)
+                       continue;
+
+               /* We need only one device for the audio card */
+               if (card_created == false) {
+                       au_pdev = create_simple_dss_pdev("omap-hdmi-audio-card",
+                                                        -1, NULL, 0, dss_pdev);
+                       if (IS_ERR(au_pdev)) {
+                               pr_err("Could not build platform_device for omap-hdmi-audio-card\n");
+                               return PTR_ERR(au_pdev);
+                       }
+                       card_created = true;
+               }
+
+               /* One device for each HDMI connector in the board */
+               au_pdev = create_simple_dss_pdev("hdmi-audio-codec",
+                                                 dssdev->dev.id,
+                                                 NULL, 0, dss_pdev);
+               if (IS_ERR(au_pdev)) {
+                       pr_err("Could not build platform_device for hdmi-audio-codec\n");
+                       return PTR_ERR(au_pdev);
+               }
+
+       }
+
        return 0;
 }
 
@@ -564,3 +631,82 @@ int omap_dss_reset(struct omap_hwmod *oh)
 
        return r;
 }
+
+static int __init hdmi_init_of(void)
+{
+       if (cpu_is_omap44xx()) {
+               enum omap_hdmi_flags flags;
+
+               /*
+                * OMAP4460SDP/Blaze and OMAP4430 ES2.3 SDP/Blaze boards and
+                * later have external pull up on the HDMI I2C lines.
+                */
+               /* FIXME: Ideally we should know from the DT whether if there is a
+                * resistor. This could work with PandaES, as it is the only Panda
+                * with 4460. For SDP, however, there are no dedicated DT files for
+                * each processor board to know whether the pull-up resistor is
+                * present.
+                */
+               if (cpu_is_omap446x() || omap_rev() > OMAP4430_REV_ES2_2)
+                       flags = OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP;
+               else
+                       flags = 0;
+
+               omap4_hdmi_mux_pads(flags);
+       }
+
+       return 0;
+}
+
+int __init omapdss_init_of(void)
+{
+       int r;
+       struct platform_device *pdev;
+       struct device_node *node;
+       enum omapdss_version ver;
+
+       static struct omap_dss_board_info board_data = {
+               .dsi_enable_pads = omap_dsi_enable_pads,
+               .dsi_disable_pads = omap_dsi_disable_pads,
+               .get_context_loss_count = omap_pm_get_dev_context_loss_count,
+               .set_min_bus_tput = omap_dss_set_min_bus_tput,
+       };
+
+       ver = omap_display_get_version();
+
+       if (ver == OMAPDSS_VER_UNKNOWN) {
+               pr_err("DSS not supported on this SoC\n");
+               return -ENODEV;
+       }
+
+       board_data.version = ver;
+
+       /* find the main dss node  */
+       node = of_find_node_by_name(NULL, "dss");
+       if (!node)
+               return 0;
+
+       pdev = of_find_device_by_node(node);
+       if (!pdev) {
+               pr_err("Cannot find dss platform device\n");
+               return -EINVAL;
+       }
+
+       r = of_platform_populate(node, NULL, NULL, &pdev->dev);
+       if (r) {
+               pr_err("Failed to populate dss devices\n");
+               return -EINVAL;
+       }
+
+       omap_display_device.dev.platform_data = &board_data;
+
+       r = platform_device_register(&omap_display_device);
+       if (r < 0) {
+               pr_err("Unable to register omapdss device\n");
+               return r;
+       }
+
+       hdmi_init_of();
+
+       return 0;
+}
index 0a02aab5df677db9bc5577093f5f6091d4c86ff8..7f4ca7d0c4eddddbc776cb18003e60620f1cf482 100644 (file)
@@ -310,7 +310,8 @@ static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel)
         * Set jitter correction. No jitter correction for OMAP4 and 3630
         * since freqsel field is no longer present
         */
-       if (!soc_is_am33xx() && !cpu_is_omap44xx() && !cpu_is_omap3630()) {
+       if (!soc_is_am33xx() && !cpu_is_omap44xx() && !cpu_is_omap3630()
+            && !soc_is_omap54xx()) {
                v = __raw_readl(dd->control_reg);
                v &= ~dd->freqsel_mask;
                v |= freqsel << __ffs(dd->freqsel_mask);
@@ -480,20 +481,22 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
        if (!dd)
                return -EINVAL;
 
-       __clk_prepare(dd->clk_bypass);
-       clk_enable(dd->clk_bypass);
-       __clk_prepare(dd->clk_ref);
-       clk_enable(dd->clk_ref);
-
        if (__clk_get_rate(dd->clk_bypass) == rate &&
            (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
                pr_debug("%s: %s: set rate: entering bypass.\n",
                         __func__, __clk_get_name(hw->clk));
 
+               __clk_prepare(dd->clk_bypass);
+               clk_enable(dd->clk_bypass);
                ret = _omap3_noncore_dpll_bypass(clk);
                if (!ret)
                        new_parent = dd->clk_bypass;
+               clk_disable(dd->clk_bypass);
+               __clk_unprepare(dd->clk_bypass);
        } else {
+               __clk_prepare(dd->clk_ref);
+               clk_enable(dd->clk_ref);
+
                if (dd->last_rounded_rate != rate)
                        rate = __clk_round_rate(hw->clk, rate);
 
@@ -501,7 +504,8 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
                        return -EINVAL;
 
                /* No freqsel on OMAP4 and OMAP3630 */
-               if (!cpu_is_omap44xx() && !cpu_is_omap3630()) {
+               if (!cpu_is_omap44xx() && !cpu_is_omap3630()
+                   && !soc_is_omap54xx()) {
                        freqsel = _omap3_dpll_compute_freqsel(clk,
                                                dd->last_rounded_n);
                        WARN_ON(!freqsel);
@@ -513,6 +517,8 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
                ret = omap3_noncore_dpll_program(clk, freqsel);
                if (!ret)
                        new_parent = dd->clk_ref;
+               clk_disable(dd->clk_ref);
+               __clk_unprepare(dd->clk_ref);
        }
        /*
        * FIXME - this is all wrong.  common code handles reparenting and
@@ -524,11 +530,6 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
        if (!ret)
                __clk_reparent(hw->clk, new_parent);
 
-       clk_disable(dd->clk_ref);
-       __clk_unprepare(dd->clk_ref);
-       clk_disable(dd->clk_bypass);
-       __clk_unprepare(dd->clk_bypass);
-
        return 0;
 }
 
index 4be5cfc81ab8cd8c0ab1b6333b13c33fe6d0d733..18a208c5834c208cdb42c8d3c0bab8d779716a29 100644 (file)
@@ -70,6 +70,9 @@ static struct omap_dss_device  omap4_panda_hdmi_device = {
        .type = OMAP_DISPLAY_TYPE_HDMI,
        .channel = OMAP_DSS_CHANNEL_DIGIT,
        .data = &omap4_panda_hdmi_data,
+       .dev = {
+               .id = -1,
+       },
 };
 
 static struct omap_dss_device *omap4_panda_dss_devices[] = {
@@ -171,6 +174,9 @@ static struct omap_dss_device sdp4430_hdmi_device = {
        .type = OMAP_DISPLAY_TYPE_HDMI,
        .channel = OMAP_DSS_CHANNEL_DIGIT,
        .data = &sdp4430_hdmi_data,
+       .dev = {
+               .id = -1,
+       },
 };
 
 static struct picodlp_panel_data sdp4430_picodlp_pdata = {
index 45cc7ed4dd5875e4a9ceb290ca14a85b931b33db..7773df752929b81b702c31f2b4ddc082ee1d9c28 100644 (file)
@@ -71,6 +71,32 @@ out:
 }
 EXPORT_SYMBOL(omap_type);
 
+u8 omap_get_sysboot_value(void)
+{
+       u32 val = 0;
+       u8 mask = OMAP2_SYSBOOT_5_MASK | OMAP2_SYSBOOT_4_MASK |
+               OMAP2_SYSBOOT_3_MASK | OMAP2_SYSBOOT_2_MASK |
+               OMAP2_SYSBOOT_1_MASK | OMAP2_SYSBOOT_0_MASK;
+
+       if (cpu_is_omap24xx()) {
+               val = omap_ctrl_readl(OMAP24XX_CONTROL_STATUS);
+       } else if (soc_is_am33xx()) {
+               val = omap_ctrl_readl(AM33XX_CONTROL_STATUS);
+       } else if (cpu_is_omap34xx()) {
+               val = omap_ctrl_readl(OMAP343X_CONTROL_STATUS);
+       } else if (cpu_is_omap44xx()) {
+               val = omap_ctrl_readl(OMAP4_CTRL_MODULE_CORE_STATUS);
+               /* OMAP4 has more bits! */
+               mask |= OMAP2_SYSBOOT_6_MASK | OMAP2_SYSBOOT_7_MASK;
+       } else if (soc_is_omap54xx()) {
+               val = omap_ctrl_readl(OMAP5XXX_CONTROL_STATUS);
+       } else {
+               pr_err("Cannot detect omap type!\n");
+       }
+
+       return val & mask;
+}
+EXPORT_SYMBOL(omap_get_sysboot_value);
 
 /*----------------------------------------------------------------------------*/
 
@@ -519,22 +545,35 @@ void __init omap5xxx_check_revision(void)
        case 0xb942:
                switch (rev) {
                case 0:
-               default:
                        omap_revision = OMAP5430_REV_ES1_0;
+                       /* Fix ES2.0 wrongly efused samples ID detection */
+                       if (read_cpuid_id() == 0x412FC0F2)
+                               omap_revision = OMAP5430_REV_ES2_0;
+                       break;
+               case 1:
+                       omap_revision = OMAP5430_REV_ES2_0;
+                       break;
+               default:
+                       omap_revision = OMAP5430_REV_ES2_0;
                }
                break;
 
        case 0xb998:
                switch (rev) {
                case 0:
-               default:
                        omap_revision = OMAP5432_REV_ES1_0;
+                       break;
+               case 1:
+                       omap_revision = OMAP5432_REV_ES2_0;
+                       break;
+               default:
+                       omap_revision = OMAP5432_REV_ES2_0;
                }
                break;
 
        default:
                /* Unknown default to latest silicon rev as default*/
-               omap_revision = OMAP5430_REV_ES1_0;
+               omap_revision = OMAP5430_REV_ES2_0;
        }
 
        pr_info("OMAP%04x ES%d.0\n",
index 2c3fdd65387b56c542d6d97cbc093fcf69a51b9a..9ac384c319c6e4b0fc6663cf87be088bfacbc069 100644 (file)
@@ -38,6 +38,7 @@
 #include "clock2xxx.h"
 #include "clock3xxx.h"
 #include "clock44xx.h"
+#include "clock54xx.h"
 #include "omap-pm.h"
 #include "sdrc.h"
 #include "control.h"
@@ -235,9 +236,9 @@ static struct map_desc omap44xx_io_desc[] __initdata = {
        },
 #ifdef CONFIG_OMAP4_ERRATA_I688
        {
-               .virtual        = OMAP4_SRAM_VA,
-               .pfn            = __phys_to_pfn(OMAP4_SRAM_PA),
-               .length         = PAGE_SIZE,
+               .virtual        = OMAP4_ERRATA_I688_SRAM_VA,
+               .pfn            = __phys_to_pfn(OMAP4_ERRATA_I688_SRAM_PA),
+               .length         = OMAP4_ERRATA_I688_SIZE,
                .type           = MT_MEMORY_SO,
        },
 #endif
@@ -271,6 +272,14 @@ static struct map_desc omap54xx_io_desc[] __initdata = {
                .length         = L4_PER_54XX_SIZE,
                .type           = MT_DEVICE,
        },
+#ifdef CONFIG_OMAP4_ERRATA_I688
+       {
+               .virtual        = OMAP5_ERRATA_I688_SRAM_VA,
+               .pfn            = __phys_to_pfn(OMAP5_ERRATA_I688_SRAM_PA),
+               .length         = OMAP4_ERRATA_I688_SIZE,
+               .type           = MT_MEMORY_SO,
+       },
+#endif
 };
 #endif
 
@@ -323,6 +332,7 @@ void __init omap4_map_io(void)
 void __init omap5_map_io(void)
 {
        iotable_init(omap54xx_io_desc, ARRAY_SIZE(omap54xx_io_desc));
+       omap_barriers_init();
 }
 #endif
 /*
@@ -570,6 +580,13 @@ void __init am33xx_init_early(void)
        omap_hwmod_init_postsetup();
        am33xx_clk_init();
 }
+
+void __init am33xx_init_late(void)
+{
+       omap_mux_late_init();
+       omap2_common_pm_late_init();
+       am33xx_pm_init();
+}
 #endif
 
 #ifdef CONFIG_ARCH_OMAP4
@@ -618,7 +635,23 @@ void __init omap5_init_early(void)
        omap2_set_globals_prcm_mpu(OMAP2_L4_IO_ADDRESS(OMAP54XX_PRCM_MPU_BASE));
        omap_prm_base_init();
        omap_cm_base_init();
+       omap44xx_prm_init();
        omap5xxx_check_revision();
+       omap54xx_voltagedomains_init();
+       omap54xx_powerdomains_init();
+       omap54xx_clockdomains_init();
+       omap54xx_hwmod_init();
+       omap_hwmod_init_postsetup();
+       omap5xxx_clk_init();
+
+}
+
+void __init omap5_init_late(void)
+{
+       omap_mux_late_init();
+       omap2_common_pm_late_init();
+       omap4_pm_init();
+       omap2_clk_enable_autoidle_all();
 }
 #endif
 
index 3926f370448f91825fd07b4cdcaa0af09db74b2b..072f13e52027e393bd16068681c3db714f101ce9 100644 (file)
 #define OMAP3_IRQ_BASE         OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE)
 #define INTCPS_SIR_IRQ_OFFSET  0x0040  /* omap2/3 active interrupt offset */
 #define ACTIVEIRQ_MASK         0x7f    /* omap2/3 active interrupt bits */
-#define INTCPS_NR_MIR_REGS     3
-#define INTCPS_NR_IRQS         96
+/*
+ * Max from AM33XX device
+ */
+#define INTCPS_MAX_NR_REGS_REQ 4
+#define INTCPS_MAX_NR_IRQS     128
+
 
 /*
  * OMAP2 has a number of different interrupt controllers, each interrupt
 static struct omap_irq_bank {
        void __iomem *base_reg;
        unsigned int nr_irqs;
+       unsigned int nr_regs_req;
 } __attribute__ ((aligned(4))) irq_banks[] = {
        {
                /* MPU INTC */
                .nr_irqs        = 96,
+               .nr_regs_req    = 3,
        },
 };
 
@@ -75,8 +81,8 @@ struct omap3_intc_regs {
        u32 protection;
        u32 idle;
        u32 threshold;
-       u32 ilr[INTCPS_NR_IRQS];
-       u32 mir[INTCPS_NR_MIR_REGS];
+       u32 ilr[INTCPS_MAX_NR_IRQS];
+       u32 mir[INTCPS_MAX_NR_REGS_REQ];
 };
 
 /* INTC bank register get/set */
@@ -183,6 +189,7 @@ static void __init omap_init_irq(u32 base, int nr_irqs,
                struct omap_irq_bank *bank = irq_banks + i;
 
                bank->nr_irqs = nr_irqs;
+               bank->nr_regs_req = 0;
 
                /* Static mapping, never released */
                bank->base_reg = ioremap(base, SZ_4K);
@@ -193,9 +200,10 @@ static void __init omap_init_irq(u32 base, int nr_irqs,
 
                omap_irq_bank_init_one(bank);
 
-               for (j = 0; j < bank->nr_irqs; j += 32)
+               for (j = 0; j < bank->nr_irqs; j += 32) {
                        omap_alloc_gc(bank->base_reg + j, j + irq_base, 32);
-
+                       bank->nr_regs_req++;
+               }
                nr_of_irqs += bank->nr_irqs;
                nr_banks++;
        }
@@ -219,25 +227,19 @@ void __init ti81xx_init_irq(void)
        omap_init_irq(OMAP34XX_IC_BASE, 128, NULL);
 }
 
-static inline void omap_intc_handle_irq(void __iomem *base_addr, struct pt_regs *regs)
+static inline void omap_intc_handle_irq(void __iomem *base_addr,
+               unsigned int no_regs_req, struct pt_regs *regs)
 {
-       u32 irqnr;
+       u32 irqnr = 0;
 
        do {
-               irqnr = readl_relaxed(base_addr + 0x98);
-               if (irqnr)
-                       goto out;
-
-               irqnr = readl_relaxed(base_addr + 0xb8);
-               if (irqnr)
-                       goto out;
+               int i = 0;
 
-               irqnr = readl_relaxed(base_addr + 0xd8);
-#ifdef CONFIG_SOC_TI81XX
-               if (irqnr)
-                       goto out;
-               irqnr = readl_relaxed(base_addr + 0xf8);
-#endif
+               for (i = 0; i < no_regs_req; i++) {
+                       irqnr = readl_relaxed(base_addr + 0x98 + (0x20 * i));
+                       if (irqnr)
+                               goto out;
+               }
 
 out:
                if (!irqnr)
@@ -256,7 +258,7 @@ out:
 asmlinkage void __exception_irq_entry omap2_intc_handle_irq(struct pt_regs *regs)
 {
        void __iomem *base_addr = OMAP2_IRQ_BASE;
-       omap_intc_handle_irq(base_addr, regs);
+       omap_intc_handle_irq(base_addr, irq_banks[0].nr_regs_req, regs);
 }
 
 int __init intc_of_init(struct device_node *node,
@@ -307,10 +309,10 @@ void omap_intc_save_context(void)
                        intc_bank_read_reg(bank, INTC_IDLE);
                intc_context[ind].threshold =
                        intc_bank_read_reg(bank, INTC_THRESHOLD);
-               for (i = 0; i < INTCPS_NR_IRQS; i++)
+               for (i = 0; i < bank->nr_irqs; i++)
                        intc_context[ind].ilr[i] =
                                intc_bank_read_reg(bank, (0x100 + 0x4*i));
-               for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
+               for (i = 0; i < bank->nr_regs_req; i++)
                        intc_context[ind].mir[i] =
                                intc_bank_read_reg(&irq_banks[0], INTC_MIR0 +
                                (0x20 * i));
@@ -333,10 +335,10 @@ void omap_intc_restore_context(void)
                                        bank, INTC_IDLE);
                intc_bank_write_reg(intc_context[ind].threshold,
                                        bank, INTC_THRESHOLD);
-               for (i = 0; i < INTCPS_NR_IRQS; i++)
+               for (i = 0; i < bank->nr_irqs; i++)
                        intc_bank_write_reg(intc_context[ind].ilr[i],
                                bank, (0x100 + 0x4*i));
-               for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
+               for (i = 0; i < bank->nr_regs_req; i++)
                        intc_bank_write_reg(intc_context[ind].mir[i],
                                 &irq_banks[0], INTC_MIR0 + (0x20 * i));
        }
@@ -367,6 +369,6 @@ void omap3_intc_resume_idle(void)
 asmlinkage void __exception_irq_entry omap3_intc_handle_irq(struct pt_regs *regs)
 {
        void __iomem *base_addr = OMAP3_IRQ_BASE;
-       omap_intc_handle_irq(base_addr, regs);
+       omap_intc_handle_irq(base_addr, irq_banks[0].nr_regs_req, regs);
 }
 #endif /* CONFIG_ARCH_OMAP3 */
index 6a217c98db5484a2560d08ff343219557ee75fb9..26fd8ceb6a36845675f55ad4df365e1e155b5a8e 100644 (file)
@@ -122,10 +122,8 @@ static int __init _omap_mux_init_gpio(struct omap_mux_partition *partition,
                }
        }
 
-       if (found == 0) {
-               pr_err("%s: Could not set gpio%i\n", __func__, gpio);
+       if (found == 0)
                return -ENODEV;
-       }
 
        if (found > 1) {
                pr_info("%s: Multiple gpio paths (%d) for gpio%i\n", __func__,
@@ -154,6 +152,8 @@ int __init omap_mux_init_gpio(int gpio, int val)
                        return ret;
        }
 
+       pr_err("%s: Could not set gpio%i\n", __func__, gpio);
+
        return -ENODEV;
 }
 
@@ -211,8 +211,6 @@ static int __init _omap_mux_get_by_name(struct omap_mux_partition *partition,
                return -EINVAL;
        }
 
-       pr_err("%s: Could not find signal %s\n", __func__, muxname);
-
        return -ENODEV;
 }
 
@@ -234,6 +232,8 @@ int __init omap_mux_get_by_name(const char *muxname,
                return mux_mode;
        }
 
+       pr_err("%s: Could not find signal %s\n", __func__, muxname);
+
        return -ENODEV;
 }
 
index e712d1725a8bc80d5c0074cce5500c2683bc1109..539082a5a991fe017a19a6948211ddf5449f16bf 100644 (file)
 #include <linux/smp.h>
 #include <linux/io.h>
 
-#include <asm/cacheflush.h>
 #include "omap-wakeupgen.h"
-
 #include "common.h"
-
 #include "powerdomain.h"
 
 /*
@@ -35,9 +32,6 @@ void __ref omap4_cpu_die(unsigned int cpu)
        unsigned int boot_cpu = 0;
        void __iomem *base = omap_get_wakeupgen_base();
 
-       flush_cache_all();
-       dsb();
-
        /*
         * we're ready for shutdown now, so do it
         */
@@ -53,7 +47,7 @@ void __ref omap4_cpu_die(unsigned int cpu)
                /*
                 * Enter into low power state
                 */
-               omap4_hotplug_cpu(cpu, PWRDM_POWER_OFF);
+               omap4_mpuss_hotplug_cpu(cpu, PWRDM_FUNC_PWRST_OFF);
 
                if (omap_secure_apis_support())
                        boot_cpu = omap_read_auxcoreboot0();
index aac46bfdbeb2cb4b45bd44ab0b77b46e9d7b41f3..eb1b6e00050b90f48a6d4fb7390acc9183bc80f6 100644 (file)
@@ -56,6 +56,7 @@
 #include "omap4-sar-layout.h"
 #include "pm.h"
 #include "prcm_mpu44xx.h"
+#include "prcm_mpu54xx.h"
 #include "prminst44xx.h"
 #include "prcm44xx.h"
 #include "prm44xx.h"
@@ -71,74 +72,84 @@ struct omap4_cpu_pm_info {
        void (*secondary_startup)(void);
 };
 
+struct cpu_pm_ops {
+       int (*finish_suspend)(unsigned long cpu_state);
+       void (*resume)(void);
+       void (*scu_prepare)(unsigned int cpu_id, u8 cpu_state);
+       void (*hotplug_restart)(void);
+};
+
+extern int omap4_finish_suspend(unsigned long cpu_state);
+extern void omap4_cpu_resume(void);
+extern int omap5_finish_suspend(unsigned long cpu_state);
+extern void omap5_cpu_resume(void);
+
 static DEFINE_PER_CPU(struct omap4_cpu_pm_info, omap4_pm_info);
 static struct powerdomain *mpuss_pd;
 static void __iomem *sar_base;
+static u32 cpu_context_offset, cpu_cswr_supported;
 
-/*
- * Program the wakeup routine address for the CPU0 and CPU1
- * used for OFF or DORMANT wakeup.
- */
-static inline void set_cpu_wakeup_addr(unsigned int cpu_id, u32 addr)
+static int default_finish_suspend(unsigned long cpu_state)
 {
-       struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
-
-       __raw_writel(addr, pm_info->wkup_sar_addr);
+       omap_do_wfi();
+       return 0;
 }
 
-/*
- * Set the CPUx powerdomain's previous power state
- */
-static inline void set_cpu_next_pwrst(unsigned int cpu_id,
-                               unsigned int power_state)
-{
-       struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
-
-       pwrdm_set_next_pwrst(pm_info->pwrdm, power_state);
-}
+static void dummy_cpu_resume(void)
+{}
 
-/*
- * Read CPU's previous power state
- */
-static inline unsigned int read_cpu_prev_pwrst(unsigned int cpu_id)
-{
-       struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
+void dummy_scu_prepare(unsigned int cpu_id, u8 cpu_state)
+{}
 
-       return pwrdm_read_prev_pwrst(pm_info->pwrdm);
-}
+struct cpu_pm_ops omap_pm_ops = {
+       .finish_suspend         = default_finish_suspend,
+       .resume                 = dummy_cpu_resume,
+       .scu_prepare            = dummy_scu_prepare,
+       .hotplug_restart        = dummy_cpu_resume,
+};
 
 /*
- * Clear the CPUx powerdomain's previous power state
+ * Program the wakeup routine address for the CPU0 and CPU1
+ * used for OFF or DORMANT wakeup.
  */
-static inline void clear_cpu_prev_pwrst(unsigned int cpu_id)
+static inline void set_cpu_wakeup_addr(unsigned int cpu_id, u32 addr)
 {
        struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
 
-       pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
+       /*
+        * XXX should not be writing directly into another IP block's
+        * address space!
+        */
+       __raw_writel(addr, pm_info->wkup_sar_addr);
 }
 
 /*
  * Store the SCU power status value to scratchpad memory
  */
-static void scu_pwrst_prepare(unsigned int cpu_id, unsigned int cpu_state)
+static void scu_pwrst_prepare(unsigned int cpu_id, u8 fpwrst)
 {
        struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
        u32 scu_pwr_st;
 
-       switch (cpu_state) {
-       case PWRDM_POWER_RET:
+       switch (fpwrst) {
+       case PWRDM_FUNC_PWRST_CSWR:
+       case PWRDM_FUNC_PWRST_OSWR: /* XXX is this accurate? */
                scu_pwr_st = SCU_PM_DORMANT;
                break;
-       case PWRDM_POWER_OFF:
+       case PWRDM_FUNC_PWRST_OFF:
                scu_pwr_st = SCU_PM_POWEROFF;
                break;
-       case PWRDM_POWER_ON:
-       case PWRDM_POWER_INACTIVE:
+       case PWRDM_FUNC_PWRST_ON:
+       case PWRDM_FUNC_PWRST_INACTIVE:
        default:
                scu_pwr_st = SCU_PM_NORMAL;
                break;
        }
 
+       /*
+        * XXX should not be writing directly into another IP block's
+        * address space!
+        */
        __raw_writel(scu_pwr_st, pm_info->scu_sar_addr);
 }
 
@@ -159,31 +170,17 @@ static inline void cpu_clear_prev_logic_pwrst(unsigned int cpu_id)
 
        if (cpu_id) {
                reg = omap4_prcm_mpu_read_inst_reg(OMAP4430_PRCM_MPU_CPU1_INST,
-                                       OMAP4_RM_CPU1_CPU1_CONTEXT_OFFSET);
+                                       cpu_context_offset);
                omap4_prcm_mpu_write_inst_reg(reg, OMAP4430_PRCM_MPU_CPU1_INST,
-                                       OMAP4_RM_CPU1_CPU1_CONTEXT_OFFSET);
+                                       cpu_context_offset);
        } else {
                reg = omap4_prcm_mpu_read_inst_reg(OMAP4430_PRCM_MPU_CPU0_INST,
-                                       OMAP4_RM_CPU0_CPU0_CONTEXT_OFFSET);
+                                       cpu_context_offset);
                omap4_prcm_mpu_write_inst_reg(reg, OMAP4430_PRCM_MPU_CPU0_INST,
-                                       OMAP4_RM_CPU0_CPU0_CONTEXT_OFFSET);
+                                       cpu_context_offset);
        }
 }
 
-/**
- * omap4_mpuss_read_prev_context_state:
- * Function returns the MPUSS previous context state
- */
-u32 omap4_mpuss_read_prev_context_state(void)
-{
-       u32 reg;
-
-       reg = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
-               OMAP4430_PRM_MPU_INST, OMAP4_RM_MPU_MPU_CONTEXT_OFFSET);
-       reg &= OMAP4430_LOSTCONTEXT_DFF_MASK;
-       return reg;
-}
-
 /*
  * Store the CPU cluster state for L2X0 low power operations.
  */
@@ -191,6 +188,10 @@ static void l2x0_pwrst_prepare(unsigned int cpu_id, unsigned int save_state)
 {
        struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
 
+       /*
+        * XXX should not be writing directly into another IP block's
+        * address space!
+        */
        __raw_writel(save_state, pm_info->l2x0_sar_addr);
 }
 
@@ -203,11 +204,12 @@ static void save_l2x0_context(void)
 {
        u32 val;
        void __iomem *l2x0_base = omap4_get_l2cache_base();
-
-       val = __raw_readl(l2x0_base + L2X0_AUX_CTRL);
-       __raw_writel(val, sar_base + L2X0_AUXCTRL_OFFSET);
-       val = __raw_readl(l2x0_base + L2X0_PREFETCH_CTRL);
-       __raw_writel(val, sar_base + L2X0_PREFETCH_CTRL_OFFSET);
+       if (l2x0_base) {
+               val = __raw_readl(l2x0_base + L2X0_AUX_CTRL);
+               __raw_writel(val, sar_base + L2X0_AUXCTRL_OFFSET);
+               val = __raw_readl(l2x0_base + L2X0_PREFETCH_CTRL);
+               __raw_writel(val, sar_base + L2X0_PREFETCH_CTRL_OFFSET);
+       }
 }
 #else
 static void save_l2x0_context(void)
@@ -215,11 +217,11 @@ static void save_l2x0_context(void)
 #endif
 
 /**
- * omap4_enter_lowpower: OMAP4 MPUSS Low Power Entry Function
+ * omap4_mpuss_enter_lowpower: OMAP4 MPUSS Low Power Entry Function
  * The purpose of this function is to manage low power programming
  * of OMAP4 MPUSS subsystem
  * @cpu : CPU ID
- * @power_state: Low power state.
+ * @fpwrst: functional powerstate for the MPUSS to enter
  *
  * MPUSS states for the context save:
  * save_state =
@@ -228,23 +230,29 @@ static void save_l2x0_context(void)
  *     2 - CPUx L1 and logic lost + GIC lost: MPUSS OSWR
  *     3 - CPUx L1 and logic lost + GIC + L2 lost: DEVICE OFF
  */
-int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
+int omap4_mpuss_enter_lowpower(unsigned int cpu, u8 fpwrst)
 {
+       struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu);
        unsigned int save_state = 0;
        unsigned int wakeup_cpu;
 
        if (omap_rev() == OMAP4430_REV_ES1_0)
                return -ENXIO;
 
-       switch (power_state) {
-       case PWRDM_POWER_ON:
-       case PWRDM_POWER_INACTIVE:
+       switch (fpwrst) {
+       case PWRDM_FUNC_PWRST_ON:
+       case PWRDM_FUNC_PWRST_INACTIVE:
                save_state = 0;
                break;
-       case PWRDM_POWER_OFF:
+       case PWRDM_FUNC_PWRST_OFF:
                save_state = 1;
                break;
-       case PWRDM_POWER_RET:
+       case PWRDM_FUNC_PWRST_CSWR:
+       case PWRDM_FUNC_PWRST_OSWR:
+               if (cpu_cswr_supported) {
+                       save_state = 0;
+                       break;
+               }
        default:
                /*
                 * CPUx CSWR is invalid hardware state. Also CPUx OSWR
@@ -260,23 +268,25 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
 
        /*
         * Check MPUSS next state and save interrupt controller if needed.
-        * In MPUSS OSWR or device OFF, interrupt controller  contest is lost.
+        * In MPUSS OSWR or device OFF, interrupt controller context is lost.
         */
        mpuss_clear_prev_logic_pwrst();
-       if ((pwrdm_read_next_pwrst(mpuss_pd) == PWRDM_POWER_RET) &&
-               (pwrdm_read_logic_retst(mpuss_pd) == PWRDM_POWER_OFF))
+       if (pwrdm_read_next_fpwrst(mpuss_pd) == PWRDM_FUNC_PWRST_OSWR)
                save_state = 2;
 
        cpu_clear_prev_logic_pwrst(cpu);
-       set_cpu_next_pwrst(cpu, power_state);
-       set_cpu_wakeup_addr(cpu, virt_to_phys(omap4_cpu_resume));
-       scu_pwrst_prepare(cpu, power_state);
+       WARN_ON(pwrdm_set_next_fpwrst(pm_info->pwrdm, fpwrst));
+       set_cpu_wakeup_addr(cpu, virt_to_phys(omap_pm_ops.resume));
+       omap_pm_ops.scu_prepare(cpu, fpwrst);
        l2x0_pwrst_prepare(cpu, save_state);
 
        /*
         * Call low level function  with targeted low power state.
         */
-       cpu_suspend(save_state, omap4_finish_suspend);
+       if (save_state)
+               cpu_suspend(save_state, omap_pm_ops.finish_suspend);
+       else
+               omap_pm_ops.finish_suspend(save_state);
 
        /*
         * Restore the CPUx power state to ON otherwise CPUx
@@ -286,52 +296,68 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
         * domain transition
         */
        wakeup_cpu = smp_processor_id();
-       set_cpu_next_pwrst(wakeup_cpu, PWRDM_POWER_ON);
 
        pwrdm_post_transition(NULL);
 
+       WARN_ON(pwrdm_set_next_fpwrst(pm_info->pwrdm, PWRDM_FUNC_PWRST_ON));
+
        return 0;
 }
 
 /**
  * omap4_hotplug_cpu: OMAP4 CPU hotplug entry
  * @cpu : CPU ID
- * @power_state: CPU low power state.
+ * @fpwrst: functional power state to program the CPU powerdomain to enter
  */
-int __cpuinit omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state)
+int __cpuinit omap4_mpuss_hotplug_cpu(unsigned int cpu, u8 fpwrst)
 {
-       unsigned int cpu_state = 0;
        struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu);
+       unsigned int cpu_state = 0;
 
        if (omap_rev() == OMAP4430_REV_ES1_0)
                return -ENXIO;
 
-       if (power_state == PWRDM_POWER_OFF)
+       if (fpwrst == PWRDM_FUNC_PWRST_OFF || fpwrst == PWRDM_FUNC_PWRST_OSWR)
                cpu_state = 1;
 
-       clear_cpu_prev_pwrst(cpu);
-       set_cpu_next_pwrst(cpu, power_state);
-       set_cpu_wakeup_addr(cpu, virt_to_phys(pm_info->secondary_startup));
-       scu_pwrst_prepare(cpu, power_state);
+       pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
+       WARN_ON(pwrdm_set_next_fpwrst(pm_info->pwrdm, fpwrst));
+       set_cpu_wakeup_addr(cpu, virt_to_phys(omap_pm_ops.hotplug_restart));
+       omap_pm_ops.scu_prepare(cpu, fpwrst);
 
        /*
         * CPU never retuns back if targeted power state is OFF mode.
         * CPU ONLINE follows normal CPU ONLINE ptah via
         * omap_secondary_startup().
         */
-       omap4_finish_suspend(cpu_state);
+       omap_pm_ops.finish_suspend(cpu_state);
 
-       set_cpu_next_pwrst(cpu, PWRDM_POWER_ON);
+       WARN_ON(pwrdm_set_next_fpwrst(pm_info->pwrdm, PWRDM_FUNC_PWRST_ON));
        return 0;
 }
 
 
+/*
+ * Enable Mercury Fast HG retention mode by default.
+ */
+static void enable_mercury_retention_mode(void)
+{
+       u32 reg;
+
+       reg = omap4_prcm_mpu_read_inst_reg(OMAP54XX_PRCM_MPU_DEVICE_INST,
+                       OMAP54XX_PRCM_MPU_PRM_PSCON_COUNT_OFFSET);
+       reg |= BIT(24) | BIT(25);
+       omap4_prcm_mpu_write_inst_reg(reg, OMAP54XX_PRCM_MPU_DEVICE_INST,
+                       OMAP54XX_PRCM_MPU_PRM_PSCON_COUNT_OFFSET);
+}
+
 /*
  * Initialise OMAP4 MPUSS
  */
 int __init omap4_mpuss_init(void)
 {
        struct omap4_cpu_pm_info *pm_info;
+       u32 cpu_wakeup_addr = 0;
 
        if (omap_rev() == OMAP4430_REV_ES1_0) {
                WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
@@ -341,9 +367,13 @@ int __init omap4_mpuss_init(void)
        sar_base = omap4_get_sar_ram_base();
 
        /* Initilaise per CPU PM information */
+       if (cpu_is_omap44xx())
+               cpu_wakeup_addr = CPU0_WAKEUP_NS_PA_ADDR_OFFSET;
+       else if (soc_is_omap54xx())
+               cpu_wakeup_addr = OMAP5_CPU0_WAKEUP_NS_PA_ADDR_OFFSET;
        pm_info = &per_cpu(omap4_pm_info, 0x0);
        pm_info->scu_sar_addr = sar_base + SCU_OFFSET0;
-       pm_info->wkup_sar_addr = sar_base + CPU0_WAKEUP_NS_PA_ADDR_OFFSET;
+       pm_info->wkup_sar_addr = sar_base + cpu_wakeup_addr;
        pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET0;
        pm_info->pwrdm = pwrdm_lookup("cpu0_pwrdm");
        if (!pm_info->pwrdm) {
@@ -356,16 +386,16 @@ int __init omap4_mpuss_init(void)
        cpu_clear_prev_logic_pwrst(0);
 
        /* Initialise CPU0 power domain state to ON */
-       pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
+       WARN_ON(pwrdm_set_next_fpwrst(pm_info->pwrdm, PWRDM_FUNC_PWRST_ON));
 
+       if (cpu_is_omap44xx())
+               cpu_wakeup_addr = CPU1_WAKEUP_NS_PA_ADDR_OFFSET;
+       else if (soc_is_omap54xx())
+               cpu_wakeup_addr = OMAP5_CPU1_WAKEUP_NS_PA_ADDR_OFFSET;
        pm_info = &per_cpu(omap4_pm_info, 0x1);
        pm_info->scu_sar_addr = sar_base + SCU_OFFSET1;
-       pm_info->wkup_sar_addr = sar_base + CPU1_WAKEUP_NS_PA_ADDR_OFFSET;
+       pm_info->wkup_sar_addr = sar_base + cpu_wakeup_addr;
        pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET1;
-       if (cpu_is_omap446x())
-               pm_info->secondary_startup = omap_secondary_startup_4460;
-       else
-               pm_info->secondary_startup = omap_secondary_startup;
 
        pm_info->pwrdm = pwrdm_lookup("cpu1_pwrdm");
        if (!pm_info->pwrdm) {
@@ -378,7 +408,7 @@ int __init omap4_mpuss_init(void)
        cpu_clear_prev_logic_pwrst(1);
 
        /* Initialise CPU1 power domain state to ON */
-       pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
+       WARN_ON(pwrdm_set_next_fpwrst(pm_info->pwrdm, PWRDM_FUNC_PWRST_ON));
 
        mpuss_pd = pwrdm_lookup("mpu_pwrdm");
        if (!mpuss_pd) {
@@ -396,6 +426,24 @@ int __init omap4_mpuss_init(void)
 
        save_l2x0_context();
 
+       if (cpu_is_omap44xx()) {
+               omap_pm_ops.finish_suspend = omap4_finish_suspend;
+               omap_pm_ops.hotplug_restart = omap_secondary_startup;
+               omap_pm_ops.resume = omap4_cpu_resume;
+               omap_pm_ops.scu_prepare = scu_pwrst_prepare;
+               cpu_context_offset = OMAP4_RM_CPU0_CPU0_CONTEXT_OFFSET;
+       } else if (soc_is_omap54xx()) {
+               omap_pm_ops.finish_suspend = omap5_finish_suspend;
+               omap_pm_ops.hotplug_restart = omap5_secondary_startup;
+               omap_pm_ops.resume = omap5_cpu_resume;
+               cpu_context_offset = OMAP54XX_RM_CPU0_CPU0_CONTEXT_OFFSET;
+               enable_mercury_retention_mode();
+               cpu_cswr_supported = 1;
+       }
+
+       if (cpu_is_omap446x())
+               omap_pm_ops.hotplug_restart = omap_secondary_startup_4460;
+
        return 0;
 }
 
index 6a3be2bebddb638ec5b34c0241ee780e1f9e3918..b1bc738ef33ac7cb44c909a664fd957f99bc2cc2 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/cpufreq.h>
 #include <linux/device.h>
 #include <linux/platform_device.h>
+#include <linux/export.h>
 
 #include "omap_device.h"
 #include "omap-pm.h"
@@ -351,6 +352,7 @@ int omap_pm_get_dev_context_loss_count(struct device *dev)
 }
 
 #endif
+EXPORT_SYMBOL(omap_pm_get_dev_context_loss_count);
 
 /* Should be called before clk framework init */
 int __init omap_pm_if_early_init(void)
index 0e729170c46b81f2ee7a263c797853abc7d01ab3..a171a5a6fe8ec4f55041a4d5094231b0538285c2 100644 (file)
 #define OMAP4_HAL_SAVEHW_INDEX         0x1b
 #define OMAP4_HAL_SAVEALL_INDEX                0x1c
 #define OMAP4_HAL_SAVEGIC_INDEX                0x1d
+#define OMAP5_HAL_SAVESECURERAM_INDEX  0x1c
+#define OMAP5_HAL_SAVEHW_INDEX         0x1d
+#define OMAP5_HAL_SAVEALL_INDEX                0x1e
+#define OMAP5_HAL_SAVEGIC_INDEX                0x1f
 
 /* Secure Monitor mode APIs */
 #define OMAP4_MON_SCU_PWR_INDEX                0x108
 #define OMAP4_MON_L2X0_CTRL_INDEX      0x102
 #define OMAP4_MON_L2X0_AUXCTRL_INDEX   0x109
 #define OMAP4_MON_L2X0_PREFETCH_INDEX  0x113
+#define OMAP5_MON_CACHES_CLEAN_INDEX   0x103
+#define OMAP5_MON_AUX_CTRL_INDEX       0x107
+#define OMAP5_MON_L2AUX_CTRL_INDEX     0x104
+
+#define OMAP5_MON_AMBA_IF_INDEX                0x108
 
 /* Secure PPA(Primary Protected Application) APIs */
 #define OMAP4_PPA_L2_POR_INDEX         0x23
index cd42d921940dcdb1dd91de67bb56b0350e2b442a..ef0bcce60580979459a444b6d2faca0684852a0f 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/smp.h>
 #include <linux/io.h>
 
-#include <asm/cacheflush.h>
 #include <asm/hardware/gic.h>
 #include <asm/smp_scu.h>
 
@@ -84,6 +83,7 @@ static int __cpuinit omap4_boot_secondary(unsigned int cpu, struct task_struct *
 {
        static struct clockdomain *cpu1_clkdm;
        static bool booted;
+       static struct powerdomain *cpu1_pwrdm;
        void __iomem *base = omap_get_wakeupgen_base();
 
        /*
@@ -103,11 +103,10 @@ static int __cpuinit omap4_boot_secondary(unsigned int cpu, struct task_struct *
        else
                __raw_writel(0x20, base + OMAP_AUX_CORE_BOOT_0);
 
-       flush_cache_all();
-       smp_wmb();
-
-       if (!cpu1_clkdm)
+       if (!cpu1_clkdm && !cpu1_pwrdm) {
                cpu1_clkdm = clkdm_lookup("mpu1_clkdm");
+               cpu1_pwrdm = pwrdm_lookup("cpu1_pwrdm");
+       }
 
        /*
         * The SGI(Software Generated Interrupts) are not wakeup capable
@@ -120,7 +119,7 @@ static int __cpuinit omap4_boot_secondary(unsigned int cpu, struct task_struct *
         * Section :
         *      4.3.4.2 Power States of CPU0 and CPU1
         */
-       if (booted) {
+       if (booted && cpu1_pwrdm && cpu1_clkdm) {
                /*
                 * GIC distributor control register has changed between
                 * CortexA9 r1pX and r2pX. The Control Register secure
@@ -141,7 +140,12 @@ static int __cpuinit omap4_boot_secondary(unsigned int cpu, struct task_struct *
                        gic_dist_disable();
                }
 
+               /*
+                * Ensure that CPU power state is set to ON to avoid CPU
+                * powerdomain transition on wfi
+                */
                clkdm_wakeup(cpu1_clkdm);
+               pwrdm_set_next_fpwrst(cpu1_pwrdm, PWRDM_FUNC_PWRST_ON);
                clkdm_allow_idle(cpu1_clkdm);
 
                if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD)) {
@@ -168,38 +172,6 @@ static int __cpuinit omap4_boot_secondary(unsigned int cpu, struct task_struct *
        return 0;
 }
 
-static void __init wakeup_secondary(void)
-{
-       void *startup_addr = omap_secondary_startup;
-       void __iomem *base = omap_get_wakeupgen_base();
-
-       if (cpu_is_omap446x()) {
-               startup_addr = omap_secondary_startup_4460;
-               pm44xx_errata |= PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD;
-       }
-
-       /*
-        * Write the address of secondary startup routine into the
-        * AuxCoreBoot1 where ROM code will jump and start executing
-        * on secondary core once out of WFE
-        * A barrier is added to ensure that write buffer is drained
-        */
-       if (omap_secure_apis_support())
-               omap_auxcoreboot_addr(virt_to_phys(startup_addr));
-       else
-               __raw_writel(virt_to_phys(omap5_secondary_startup),
-                                               base + OMAP_AUX_CORE_BOOT_1);
-
-       smp_wmb();
-
-       /*
-        * Send a 'sev' to wake the secondary core from WFE.
-        * Drain the outstanding writes to memory
-        */
-       dsb_sev();
-       mb();
-}
-
 /*
  * Initialise the CPU possible map early - this describes the CPUs
  * which may be present or become present in the system.
@@ -237,6 +209,8 @@ static void __init omap4_smp_init_cpus(void)
 
 static void __init omap4_smp_prepare_cpus(unsigned int max_cpus)
 {
+       void *startup_addr = omap_secondary_startup;
+       void __iomem *base = omap_get_wakeupgen_base();
 
        /*
         * Initialise the SCU and wake up the secondary core using
@@ -244,7 +218,24 @@ static void __init omap4_smp_prepare_cpus(unsigned int max_cpus)
         */
        if (scu_base)
                scu_enable(scu_base);
-       wakeup_secondary();
+
+       if (cpu_is_omap446x()) {
+               startup_addr = omap_secondary_startup_4460;
+               pm44xx_errata |= PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD;
+       }
+
+       /*
+        * Write the address of secondary startup routine into the
+        * AuxCoreBoot1 where ROM code will jump and start executing
+        * on secondary core once out of WFE
+        * A barrier is added to ensure that write buffer is drained
+        */
+       if (omap_secure_apis_support())
+               omap_auxcoreboot_addr(virt_to_phys(startup_addr));
+       else
+               __raw_writel(virt_to_phys(omap5_secondary_startup),
+                                               base + OMAP_AUX_CORE_BOOT_1);
+
 }
 
 struct smp_operations omap4_smp_ops __initdata = {
index 5d3b4f4f81aed8ad94ddd158ca9682907faf342b..f57b0b8ccdd01ea91112187f3a0689fc08e5bb71 100644 (file)
@@ -50,7 +50,7 @@ static DEFINE_SPINLOCK(wakeupgen_lock);
 static unsigned int irq_target_cpu[MAX_IRQS];
 static unsigned int irq_banks = MAX_NR_REG_BANKS;
 static unsigned int max_irqs = MAX_IRQS;
-static unsigned int omap_secure_apis;
+static unsigned int omap_secure_apis, secure_api_index;
 
 /*
  * Static helper functions.
@@ -315,7 +315,7 @@ static void irq_sar_clear(void)
 static void irq_save_secure_context(void)
 {
        u32 ret;
-       ret = omap_secure_dispatcher(OMAP4_HAL_SAVEGIC_INDEX,
+       ret = omap_secure_dispatcher(secure_api_index,
                                FLAG_START_CRITICAL,
                                0, 0, 0, 0, 0);
        if (ret != API_HAL_RET_VALUE_OK)
@@ -377,9 +377,7 @@ static struct notifier_block irq_notifier_block = {
 
 static void __init irq_pm_init(void)
 {
-       /* FIXME: Remove this when MPU OSWR support is added */
-       if (!soc_is_omap54xx())
-               cpu_pm_register_notifier(&irq_notifier_block);
+       cpu_pm_register_notifier(&irq_notifier_block);
 }
 #else
 static void __init irq_pm_init(void)
@@ -403,6 +401,7 @@ int __init omap_wakeupgen_init(void)
 {
        int i;
        unsigned int boot_cpu = smp_processor_id();
+       u32 val;
 
        /* Not supported on OMAP4 ES1.0 silicon */
        if (omap_rev() == OMAP4430_REV_ES1_0) {
@@ -419,6 +418,9 @@ int __init omap_wakeupgen_init(void)
                irq_banks = OMAP4_NR_BANKS;
                max_irqs = OMAP4_NR_IRQS;
                omap_secure_apis = 1;
+               secure_api_index = OMAP4_HAL_SAVEGIC_INDEX;
+       } else if (soc_is_omap54xx()) {
+               secure_api_index = OMAP5_HAL_SAVEGIC_INDEX;
        }
 
        /* Clear all IRQ bitmasks at wakeupGen level */
@@ -444,6 +446,19 @@ int __init omap_wakeupgen_init(void)
        for (i = 0; i < max_irqs; i++)
                irq_target_cpu[i] = boot_cpu;
 
+       /*
+        * Enables OMAP5 ES2 PM Mode using ES2_PM_MODE in AMBA_IF_MODE
+        * 0x0: ES1 behavior, CPU cores would enter and exit OFF mode together.
+        * 0x1: ES2 behavior, CPU cores are allowed to enter/exit OFF mode
+        * independently.
+        * This needs to be set one time thanks to always ON domain.
+        */
+       if (soc_is_omap54xx()) {
+               val = __raw_readl(wakeupgen_base + OMAP_AMBA_IF_MODE);
+               val |= BIT(5);
+               omap_smc1(OMAP5_MON_AMBA_IF_INDEX, val);
+       }
+
        irq_hotplug_init();
        irq_pm_init();
 
index b0fd16f5c3912a8ed9addba17f58d73a52b338bb..b3c8eccfae790de050f853d7528662a3a5c33aa2 100644 (file)
@@ -27,6 +27,7 @@
 #define OMAP_WKG_ENB_E_1                       0x420
 #define OMAP_AUX_CORE_BOOT_0                   0x800
 #define OMAP_AUX_CORE_BOOT_1                   0x804
+#define OMAP_AMBA_IF_MODE                      0x80c
 #define OMAP_PTMSYNCREQ_MASK                   0xc00
 #define OMAP_PTMSYNCREQ_EN                     0xc04
 #define OMAP_TIMESTAMPCYCLELO                  0xc08
index 6897ae21bb82e29e88a5bb8c5b25c959398fcbd4..4638c10c6fd7dbe7b5051acf77e3e31588ba5ef8 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/export.h>
+#include <linux/of_address.h>
+#include <linux/interrupt.h>
 
 #include <asm/hardware/gic.h>
 #include <asm/hardware/cache-l2x0.h>
@@ -38,6 +40,7 @@
 #include "omap4-sar-layout.h"
 #include "omap-secure.h"
 #include "sram.h"
+#include "omap44xx.h"
 
 #ifdef CONFIG_CACHE_L2X0
 static void __iomem *l2cache_base;
@@ -48,6 +51,7 @@ static void __iomem *gic_dist_base_addr;
 static void __iomem *twd_base;
 
 #define IRQ_LOCALTIMER         29
+#define OMAP54XX_IRQ_AXI (3 + OMAP44XX_IRQ_GIC_START)
 
 #ifdef CONFIG_OMAP4_ERRATA_I688
 /* Used to implement memory barrier on DRAM path */
@@ -88,7 +92,10 @@ void __init omap_barriers_init(void)
        dram_io_desc[0].type = MT_MEMORY_SO;
        iotable_init(dram_io_desc, ARRAY_SIZE(dram_io_desc));
        dram_sync = (void __iomem *) dram_io_desc[0].virtual;
-       sram_sync = (void __iomem *) OMAP4_SRAM_VA;
+       if (cpu_is_omap44xx())
+               sram_sync = (void __iomem *)OMAP4_ERRATA_I688_SRAM_VA;
+       else
+               sram_sync = (void __iomem *)OMAP5_ERRATA_I688_SRAM_VA;
 
        pr_info("OMAP4: Map 0x%08llx to 0x%08lx for dram barrier\n",
                (long long) paddr, dram_io_desc[0].virtual);
@@ -239,15 +246,21 @@ void __iomem *omap4_get_sar_ram_base(void)
  */
 static int __init omap4_sar_ram_init(void)
 {
+       unsigned long sar_base;
+
        /*
         * To avoid code running on other OMAPs in
         * multi-omap builds
         */
-       if (!cpu_is_omap44xx())
+       if (cpu_is_omap44xx())
+               sar_base = OMAP44XX_SAR_RAM_BASE;
+       else if (soc_is_omap54xx())
+               sar_base = OMAP54XX_SAR_RAM_BASE;
+       else
                return -ENOMEM;
 
        /* Static mapping, never released */
-       sar_ram_base = ioremap(OMAP44XX_SAR_RAM_BASE, SZ_16K);
+       sar_ram_base = ioremap(sar_base, SZ_16K);
        if (WARN_ON(!sar_ram_base))
                return -ENOMEM;
 
@@ -261,8 +274,28 @@ static struct of_device_id irq_match[] __initdata = {
        { }
 };
 
+static struct of_device_id twd_match[] __initdata = {
+       { .compatible = "arm,cortex-a9-twd-timer", },
+       { }
+};
+
 void __init omap_gic_of_init(void)
 {
+       struct device_node *np;
+
+       /* Extract GIC distributor and TWD bases for OMAP4460 ROM Errata WA */
+       if (!cpu_is_omap446x())
+               goto skip_errata_init;
+
+       np = of_find_matching_node(NULL, irq_match);
+       gic_dist_base_addr = of_iomap(np, 0);
+       WARN_ON(!gic_dist_base_addr);
+
+       np = of_find_matching_node(NULL, twd_match);
+       twd_base = of_iomap(np, 0);
+       WARN_ON(!twd_base);
+
+skip_errata_init:
        omap_wakeupgen_init();
        of_irq_init(irq_match);
 }
@@ -338,3 +371,40 @@ void omap44xx_restart(char mode, const char *cmd)
        while (1);
 }
 
+
+static irqreturn_t axi_error_handler(int irq, void *unused)
+{
+       unsigned int err;
+
+       /* read L2ECTLR to decode the AXI error */
+       asm volatile("mrc p15, 1, %0, c9, c0, 3" : "=r" (err));
+
+       if (err == AXI_ERROR) {
+               WARN(true, "\n AXI error due to L2 ECC/GIC and " \
+                               "Local pheripheral illegal writes");
+       } else if (err == AXI_L2_ERROR) {
+               WARN(true, "\n AXI error due to L2 ECC/GIC illegal write");
+       } else {
+               WARN(true, "\n AXI error with Local pheripheral");
+       }
+
+       /* Clear the error */
+       asm volatile("mcr p15, 1, %0, c9, c0, 3" : : "r" (0x0));
+
+       return IRQ_HANDLED;
+}
+
+static int __init omap5_axi_err_init(void)
+{
+       int ret;
+
+       if (!soc_is_omap54xx())
+               return 0;
+
+       ret = request_threaded_irq(OMAP54XX_IRQ_AXI,
+                       axi_error_handler, (irq_handler_t) 0,
+                       IRQF_DISABLED, "axi-err-irq", 0);
+
+       return ret;
+}
+postcore_initcall(omap5_axi_err_init);
index e170fe803b046b2e0ff624e1b502960e902ac2a7..5b2966a0f73308d2e552d7f768f45a107b05a7be 100644 (file)
 #define SAR_BANK4_OFFSET               0x3000
 
 /* Scratch pad memory offsets from SAR_BANK1 */
-#define SCU_OFFSET0                            0xd00
-#define SCU_OFFSET1                            0xd04
-#define OMAP_TYPE_OFFSET                       0xd10
-#define L2X0_SAVE_OFFSET0                      0xd14
-#define L2X0_SAVE_OFFSET1                      0xd18
-#define L2X0_AUXCTRL_OFFSET                    0xd1c
-#define L2X0_PREFETCH_CTRL_OFFSET              0xd20
+#define SCU_OFFSET0                            0xfe4
+#define SCU_OFFSET1                            0xfe8
+#define OMAP_TYPE_OFFSET                       0xfec
+#define L2X0_SAVE_OFFSET0                      0xff0
+#define L2X0_SAVE_OFFSET1                      0xff4
+#define L2X0_AUXCTRL_OFFSET                    0xff8
+#define L2X0_PREFETCH_CTRL_OFFSET              0xffc
 
 /* CPUx Wakeup Non-Secure Physical Address offsets in SAR_BANK3 */
 #define CPU0_WAKEUP_NS_PA_ADDR_OFFSET          0xa04
 #define CPU1_WAKEUP_NS_PA_ADDR_OFFSET          0xa08
+#define OMAP5_CPU0_WAKEUP_NS_PA_ADDR_OFFSET    0xe00
+#define OMAP5_CPU1_WAKEUP_NS_PA_ADDR_OFFSET    0xe04
 
 #define SAR_BACKUP_STATUS_OFFSET               (SAR_BANK3_OFFSET + 0x500)
 #define SAR_SECURE_RAM_SIZE_OFFSET             (SAR_BANK3_OFFSET + 0x504)
 #define SAR_BACKUP_STATUS_WAKEUPGEN            0x10
 
 /* WakeUpGen save restore offset from OMAP54XX_SAR_RAM_BASE */
-#define OMAP5_WAKEUPGENENB_OFFSET_CPU0         (SAR_BANK3_OFFSET + 0x8d4)
-#define OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU0  (SAR_BANK3_OFFSET + 0x8e8)
-#define OMAP5_WAKEUPGENENB_OFFSET_CPU1         (SAR_BANK3_OFFSET + 0x8fc)
-#define OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU1  (SAR_BANK3_OFFSET + 0x910)
-#define OMAP5_AUXCOREBOOT0_OFFSET              (SAR_BANK3_OFFSET + 0x924)
-#define OMAP5_AUXCOREBOOT1_OFFSET              (SAR_BANK3_OFFSET + 0x928)
-#define OMAP5_AMBA_IF_MODE_OFFSET              (SAR_BANK3_OFFSET + 0x92c)
+#define OMAP5_WAKEUPGENENB_OFFSET_CPU0         (SAR_BANK3_OFFSET + 0x9dc)
+#define OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU0  (SAR_BANK3_OFFSET + 0x9f0)
+#define OMAP5_WAKEUPGENENB_OFFSET_CPU1         (SAR_BANK3_OFFSET + 0xa04)
+#define OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU1  (SAR_BANK3_OFFSET + 0xa18)
+#define OMAP5_AUXCOREBOOT0_OFFSET              (SAR_BANK3_OFFSET + 0xa2c)
+#define OMAP5_AUXCOREBOOT1_OFFSET              (SAR_BANK3_OFFSET + 0x930)
+#define OMAP5_AMBA_IF_MODE_OFFSET              (SAR_BANK3_OFFSET + 0xa34)
 #define OMAP5_SAR_BACKUP_STATUS_OFFSET         (SAR_BANK3_OFFSET + 0x800)
 
 #endif
index a2582bb3cab3d4b7dc5293e61bf757e0dba3a2b0..a086ba15868b2c4a32ea24de917e5cbd6d734675 100644 (file)
@@ -28,5 +28,6 @@
 #define OMAP54XX_PRCM_MPU_BASE         0x48243000
 #define OMAP54XX_SCM_BASE              0x4a002000
 #define OMAP54XX_CTRL_BASE             0x4a002800
+#define OMAP54XX_SAR_RAM_BASE          0x4ae26000
 
 #endif /* __ASM_SOC_OMAP555554XX_H */
index 4653efb87a2721ea20a9fe06a30a6c204d6d2282..687d89ee76b15f5edb7681cb5c1cdfd91e317d57 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/slab.h>
 #include <linux/bootmem.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
 
 #include "clock.h"
 #include "omap_hwmod.h"
@@ -588,9 +591,7 @@ static void _set_idle_ioring_wakeup(struct omap_hwmod *oh, bool set_wake)
 static int _enable_wakeup(struct omap_hwmod *oh, u32 *v)
 {
        if (!oh->class->sysc ||
-           !((oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) ||
-             (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) ||
-             (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP)))
+           !(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP))
                return -EINVAL;
 
        if (!oh->class->sysc->sysc_fields) {
@@ -601,15 +602,8 @@ static int _enable_wakeup(struct omap_hwmod *oh, u32 *v)
        if (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP)
                *v |= 0x1 << oh->class->sysc->sysc_fields->enwkup_shift;
 
-       if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP)
-               _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART_WKUP, v);
-       if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP)
-               _set_master_standbymode(oh, HWMOD_IDLEMODE_SMART_WKUP, v);
-
        /* XXX test pwrdm_get_wken for this hwmod's subsystem */
 
-       oh->_int_flags |= _HWMOD_WAKEUP_ENABLED;
-
        return 0;
 }
 
@@ -623,9 +617,7 @@ static int _enable_wakeup(struct omap_hwmod *oh, u32 *v)
 static int _disable_wakeup(struct omap_hwmod *oh, u32 *v)
 {
        if (!oh->class->sysc ||
-           !((oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) ||
-             (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) ||
-             (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP)))
+           !(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP))
                return -EINVAL;
 
        if (!oh->class->sysc->sysc_fields) {
@@ -636,15 +628,8 @@ static int _disable_wakeup(struct omap_hwmod *oh, u32 *v)
        if (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP)
                *v &= ~(0x1 << oh->class->sysc->sysc_fields->enwkup_shift);
 
-       if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP)
-               _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART, v);
-       if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP)
-               _set_master_standbymode(oh, HWMOD_IDLEMODE_SMART, v);
-
        /* XXX test pwrdm_get_wken for this hwmod's subsystem */
 
-       oh->_int_flags &= ~_HWMOD_WAKEUP_ENABLED;
-
        return 0;
 }
 
@@ -1355,13 +1340,25 @@ static void _enable_sysc(struct omap_hwmod *oh)
 
        clkdm = _get_clkdm(oh);
        if (sf & SYSC_HAS_SIDLEMODE) {
+               if (oh->flags & HWMOD_SWSUP_SIDLE ||
+                               oh->flags & HWMOD_SWSUP_SIDLE_ACT) {
+                       idlemode = HWMOD_IDLEMODE_NO;
+               } else {
+                       if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP)
+                               idlemode = HWMOD_IDLEMODE_SMART_WKUP;
+                       else
+                               idlemode = HWMOD_IDLEMODE_SMART;
+               }
+
+               /*
+                * This is special handling for some IPs like
+                * 32k sync timer. Force them to idle!
+                */
                clkdm_act = (clkdm && clkdm->flags & CLKDM_ACTIVE_WITH_MPU);
                if (clkdm_act && !(oh->class->sysc->idlemodes &
                                   (SIDLE_SMART | SIDLE_SMART_WKUP)))
                        idlemode = HWMOD_IDLEMODE_FORCE;
-               else
-                       idlemode = (oh->flags & HWMOD_SWSUP_SIDLE) ?
-                               HWMOD_IDLEMODE_NO : HWMOD_IDLEMODE_SMART;
+
                _set_slave_idlemode(oh, idlemode, &v);
        }
 
@@ -1369,8 +1366,6 @@ static void _enable_sysc(struct omap_hwmod *oh)
                if (oh->flags & HWMOD_SWSUP_MSTANDBY) {
                        idlemode = HWMOD_IDLEMODE_NO;
                } else {
-                       if (sf & SYSC_HAS_ENAWAKEUP)
-                               _enable_wakeup(oh, &v);
                        if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP)
                                idlemode = HWMOD_IDLEMODE_SMART_WKUP;
                        else
@@ -1388,9 +1383,7 @@ static void _enable_sysc(struct omap_hwmod *oh)
            (sf & SYSC_HAS_CLOCKACTIVITY))
                _set_clockactivity(oh, oh->class->sysc->clockact, &v);
 
-       /* If slave is in SMARTIDLE, also enable wakeup */
-       if ((sf & SYSC_HAS_SIDLEMODE) && !(oh->flags & HWMOD_SWSUP_SIDLE))
-               _enable_wakeup(oh, &v);
+       _enable_wakeup(oh, &v);
 
        _write_sysconfig(v, oh);
 
@@ -1427,13 +1420,14 @@ static void _idle_sysc(struct omap_hwmod *oh)
        sf = oh->class->sysc->sysc_flags;
 
        if (sf & SYSC_HAS_SIDLEMODE) {
-               /* XXX What about HWMOD_IDLEMODE_SMART_WKUP? */
-               if (oh->flags & HWMOD_SWSUP_SIDLE ||
-                   !(oh->class->sysc->idlemodes &
-                     (SIDLE_SMART | SIDLE_SMART_WKUP)))
+               if (oh->flags & HWMOD_SWSUP_SIDLE) {
                        idlemode = HWMOD_IDLEMODE_FORCE;
-               else
-                       idlemode = HWMOD_IDLEMODE_SMART;
+               } else {
+                       if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP)
+                               idlemode = HWMOD_IDLEMODE_SMART_WKUP;
+                       else
+                               idlemode = HWMOD_IDLEMODE_SMART;
+               }
                _set_slave_idlemode(oh, idlemode, &v);
        }
 
@@ -1441,8 +1435,6 @@ static void _idle_sysc(struct omap_hwmod *oh)
                if (oh->flags & HWMOD_SWSUP_MSTANDBY) {
                        idlemode = HWMOD_IDLEMODE_FORCE;
                } else {
-                       if (sf & SYSC_HAS_ENAWAKEUP)
-                               _enable_wakeup(oh, &v);
                        if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP)
                                idlemode = HWMOD_IDLEMODE_SMART_WKUP;
                        else
@@ -1451,9 +1443,7 @@ static void _idle_sysc(struct omap_hwmod *oh)
                _set_master_standbymode(oh, idlemode, &v);
        }
 
-       /* If slave is in SMARTIDLE, also enable wakeup */
-       if ((sf & SYSC_HAS_SIDLEMODE) && !(oh->flags & HWMOD_SWSUP_SIDLE))
-               _enable_wakeup(oh, &v);
+       _enable_wakeup(oh, &v);
 
        _write_sysconfig(v, oh);
 }
@@ -2052,6 +2042,23 @@ static int _omap4_get_context_lost(struct omap_hwmod *oh)
        return oh->prcm.omap4.context_lost_counter;
 }
 
+/**
+ * _enable_preprogram - Pre-program an IP block during the _enable() process
+ * @oh: struct omap_hwmod *
+ *
+ * Some IP blocks (such as AESS) require some additional programming
+ * after enable before they can enter idle.  If a function pointer to
+ * do so is present in the hwmod data, then call it and pass along the
+ * return value; otherwise, return 0.
+ */
+static int __init _enable_preprogram(struct omap_hwmod *oh)
+{
+       if (!oh->class->enable_preprogram)
+               return 0;
+
+       return oh->class->enable_preprogram(oh);
+}
+
 /**
  * _enable - enable an omap_hwmod
  * @oh: struct omap_hwmod *
@@ -2156,6 +2163,7 @@ static int _enable(struct omap_hwmod *oh)
                                _update_sysc_cache(oh);
                        _enable_sysc(oh);
                }
+               r = _enable_preprogram(oh);
        } else {
                if (soc_ops.disable_module)
                        soc_ops.disable_module(oh);
@@ -2180,6 +2188,7 @@ static int _enable(struct omap_hwmod *oh)
  */
 static int _idle(struct omap_hwmod *oh)
 {
+       int r, hwsup = 0;
        pr_debug("omap_hwmod: %s: idling\n", oh->name);
 
        if (oh->_state != _HWMOD_STATE_ENABLED) {
@@ -2194,6 +2203,23 @@ static int _idle(struct omap_hwmod *oh)
        if (oh->class->sysc)
                _idle_sysc(oh);
        _del_initiator_dep(oh, mpu_oh);
+       if (oh->clkdm && oh->clkdm->flags &&
+           (oh->clkdm->flags & CLKDM_CAN_ENABLE_AUTO) &&
+           (oh->prcm.omap4.modulemode == MODULEMODE_HWCTRL)) {
+               /*
+                * For modules with modulemode configured as "auto" and
+                * if this clockdomain supports HW_AUTO mode
+                * the clockdomain must be in SW_WKUP before disabling
+                * a module completely.
+                */
+               hwsup = clkdm_in_hwsup(oh->clkdm);
+               r = clkdm_hwmod_enable(oh->clkdm, oh);
+               if (r) {
+                       WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
+                            oh->name, oh->clkdm->name, r);
+                       return r;
+               }
+       }
 
        if (soc_ops.disable_module)
                soc_ops.disable_module(oh);
@@ -2205,8 +2231,11 @@ static int _idle(struct omap_hwmod *oh)
         * transition to complete properly.
         */
        _disable_clocks(oh);
-       if (oh->clkdm)
+       if (oh->clkdm) {
+               if (hwsup)
+                       clkdm_allow_idle(oh->clkdm);
                clkdm_hwmod_disable(oh->clkdm, oh);
+       }
 
        /* Mux pins for device idle if populated */
        if (oh->mux && oh->mux->pads_dynamic) {
@@ -2219,42 +2248,6 @@ static int _idle(struct omap_hwmod *oh)
        return 0;
 }
 
-/**
- * omap_hwmod_set_ocp_autoidle - set the hwmod's OCP autoidle bit
- * @oh: struct omap_hwmod *
- * @autoidle: desired AUTOIDLE bitfield value (0 or 1)
- *
- * Sets the IP block's OCP autoidle bit in hardware, and updates our
- * local copy. Intended to be used by drivers that require
- * direct manipulation of the AUTOIDLE bits.
- * Returns -EINVAL if @oh is null or is not in the ENABLED state, or passes
- * along the return value from _set_module_autoidle().
- *
- * Any users of this function should be scrutinized carefully.
- */
-int omap_hwmod_set_ocp_autoidle(struct omap_hwmod *oh, u8 autoidle)
-{
-       u32 v;
-       int retval = 0;
-       unsigned long flags;
-
-       if (!oh || oh->_state != _HWMOD_STATE_ENABLED)
-               return -EINVAL;
-
-       spin_lock_irqsave(&oh->_lock, flags);
-
-       v = oh->_sysc_cache;
-
-       retval = _set_module_autoidle(oh, autoidle, &v);
-
-       if (!retval)
-               _write_sysconfig(v, oh);
-
-       spin_unlock_irqrestore(&oh->_lock, flags);
-
-       return retval;
-}
-
 /**
  * _shutdown - shutdown an omap_hwmod
  * @oh: struct omap_hwmod *
@@ -2266,7 +2259,7 @@ int omap_hwmod_set_ocp_autoidle(struct omap_hwmod *oh, u8 autoidle)
  */
 static int _shutdown(struct omap_hwmod *oh)
 {
-       int ret, i;
+       int ret, i, hwsup = 0;
        u8 prev_state;
 
        if (oh->_state != _HWMOD_STATE_IDLE &&
@@ -2302,12 +2295,32 @@ static int _shutdown(struct omap_hwmod *oh)
        /* clocks and deps are already disabled in idle */
        if (oh->_state == _HWMOD_STATE_ENABLED) {
                _del_initiator_dep(oh, mpu_oh);
+               if (oh->clkdm && oh->clkdm->flags &&
+                   (oh->clkdm->flags & CLKDM_CAN_ENABLE_AUTO) &&
+                   (oh->prcm.omap4.modulemode == MODULEMODE_HWCTRL)) {
+                       /*
+                        * For modules with modulemode configured as "auto" and
+                        * if this clockdomain supports HW_AUTO mode
+                        * the clockdomain must be in SW_WKUP before disabling
+                        * a module completely.
+                        */
+                       hwsup = clkdm_in_hwsup(oh->clkdm);
+                       ret = clkdm_hwmod_enable(oh->clkdm, oh);
+                       if (ret) {
+                               WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
+                                    oh->name, oh->clkdm->name, ret);
+                               return ret;
+                       }
+               }
                /* XXX what about the other system initiators here? dma, dsp */
                if (soc_ops.disable_module)
                        soc_ops.disable_module(oh);
                _disable_clocks(oh);
-               if (oh->clkdm)
+               if (oh->clkdm) {
+                       if (hwsup)
+                               clkdm_allow_idle(oh->clkdm);
                        clkdm_hwmod_disable(oh->clkdm, oh);
+               }
        }
        /* XXX Should this code also force-disable the optional clocks? */
 
@@ -2323,6 +2336,65 @@ static int _shutdown(struct omap_hwmod *oh)
        return 0;
 }
 
+#if defined(CONFIG_DEBUG_FS)
+
+static struct dentry *omap_hwmod_dbg_dir;
+
+/* internal hwmod states */
+static const char *hwmod_states[_HWMOD_STATE_COUNT] = {
+       [_HWMOD_STATE_UNKNOWN]          = "unknown",
+       [_HWMOD_STATE_REGISTERED]       = "registered",
+       [_HWMOD_STATE_CLKS_INITED]      = "clks_inited",
+       [_HWMOD_STATE_INITIALIZED]      = "initialized",
+       [_HWMOD_STATE_ENABLED]          = "enabled",
+       [_HWMOD_STATE_IDLE]             = "idle",
+       [_HWMOD_STATE_DISABLED]         = "disabled",
+};
+
+const char *_state_str(u8 state)
+{
+       if (state >= _HWMOD_STATE_COUNT)
+               return "invalid_state";
+       return hwmod_states[state];
+}
+
+static int omap_hwmod_dbg_show(struct seq_file *s, void *unused)
+{
+       struct omap_hwmod *oh;
+
+       list_for_each_entry(oh, &omap_hwmod_list, node) {
+               seq_printf(s, "name: %16s, state %d/%s\n", oh->name,
+                       oh->_state, _state_str(oh->_state));
+       }
+
+       return 0;
+}
+
+static int omap_hwmod_dbg_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, omap_hwmod_dbg_show, inode->i_private);
+}
+
+static const struct file_operations omap_hwmod_dbg_fops = {
+       .open           = omap_hwmod_dbg_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void __init omap_hwmod_dbg_init(void)
+{
+       omap_hwmod_dbg_dir = debugfs_create_dir("omap_hwmod", NULL);
+       if (!omap_hwmod_dbg_dir)
+               return;
+
+       (void)debugfs_create_file("state", S_IRUGO, omap_hwmod_dbg_dir,
+                                       NULL, &omap_hwmod_dbg_fops);
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
+
 /**
  * _init_mpu_rt_base - populate the virtual address for a hwmod
  * @oh: struct omap_hwmod * to locate the virtual address
@@ -2522,6 +2594,12 @@ static void __init _setup_postsetup(struct omap_hwmod *oh)
                postsetup_state = _HWMOD_STATE_ENABLED;
        }
 
+       /*
+        * Process HWMOD_NO_ACCESS: module should be disabled and not accessible
+        */
+       if (oh->flags & HWMOD_ACCESS_DISABLED)
+               postsetup_state = _HWMOD_STATE_DISABLED;
+
        if (postsetup_state == _HWMOD_STATE_IDLE)
                _idle(oh);
        else if (postsetup_state == _HWMOD_STATE_DISABLED)
@@ -3041,11 +3119,8 @@ static int _am33xx_assert_hardreset(struct omap_hwmod *oh,
 static int _am33xx_deassert_hardreset(struct omap_hwmod *oh,
                                     struct omap_hwmod_rst_info *ohri)
 {
-       if (ohri->st_shift)
-               pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n",
-                      oh->name, ohri->name);
-
        return am33xx_prm_deassert_hardreset(ohri->rst_shift,
+                               ohri->st_shift,
                                oh->clkdm->pwrdm.ptr->prcm_offs,
                                oh->prcm.omap4.rstctrl_offs,
                                oh->prcm.omap4.rstst_offs);
@@ -3116,38 +3191,6 @@ error:
        return ret;
 }
 
-/**
- * omap_hwmod_set_slave_idlemode - set the hwmod's OCP slave idlemode
- * @oh: struct omap_hwmod *
- * @idlemode: SIDLEMODE field bits (shifted to bit 0)
- *
- * Sets the IP block's OCP slave idlemode in hardware, and updates our
- * local copy.  Intended to be used by drivers that have some erratum
- * that requires direct manipulation of the SIDLEMODE bits.  Returns
- * -EINVAL if @oh is null, or passes along the return value from
- * _set_slave_idlemode().
- *
- * XXX Does this function have any current users?  If not, we should
- * remove it; it is better to let the rest of the hwmod code handle this.
- * Any users of this function should be scrutinized carefully.
- */
-int omap_hwmod_set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode)
-{
-       u32 v;
-       int retval = 0;
-
-       if (!oh)
-               return -EINVAL;
-
-       v = oh->_sysc_cache;
-
-       retval = _set_slave_idlemode(oh, idlemode, &v);
-       if (!retval)
-               _write_sysconfig(v, oh);
-
-       return retval;
-}
-
 /**
  * omap_hwmod_lookup - look up a registered omap_hwmod by name
  * @name: name of the omap_hwmod to look up
@@ -3163,6 +3206,11 @@ struct omap_hwmod *omap_hwmod_lookup(const char *name)
                return NULL;
 
        oh = _lookup(name);
+       /* check access flag */
+       if (oh && oh->flags & HWMOD_ACCESS_DISABLED) {
+               pr_warning("omap_hwmod: %s: access denied\n", oh->name);
+               oh = NULL;
+       }
 
        return oh;
 }
@@ -3301,6 +3349,10 @@ static int __init omap_hwmod_setup_all(void)
        omap_hwmod_for_each(_init, NULL);
        omap_hwmod_for_each(_setup, NULL);
 
+#ifdef CONFIG_DEBUG_FS
+       omap_hwmod_dbg_init();
+#endif
+
        return 0;
 }
 core_initcall(omap_hwmod_setup_all);
@@ -3455,6 +3507,40 @@ int omap_hwmod_reset(struct omap_hwmod *oh)
        return r;
 }
 
+
+/**
+ * omap_hwmod_register_flags - set/clear hwmod flags
+ * @ohs: pointer to an array of omap_hwmods to register new flags
+ * @set_flags: flags which have to be set
+ * @clear_flags: flags which have to be cleared
+ *
+ * Intended to be called early in boot before the clock framework is
+ * initialized.  If @ohs is not null, will set/clear flags.
+ * Allowed to be called only in REGISTERED state.
+ * Returns 0.
+ */
+int __init omap_hwmod_register_flags(struct omap_hwmod **ohs,
+                                       u32 set_flags, u32 clear_flags)
+{
+       int i = 0;
+
+       if (!ohs)
+               return 0;
+
+       /* scan through list of modules */
+       do {
+               /* allow to be called only in registered state */
+               if (ohs[i]->_state != _HWMOD_STATE_REGISTERED) {
+                       i++;
+                       continue;
+               }
+
+               ohs[i]->flags = (ohs[i]->flags & ~clear_flags) | set_flags;
+       } while (ohs[++i]);
+
+       return 0;
+}
+
 /*
  * IP block data retrieval functions
  */
@@ -3669,7 +3755,9 @@ struct powerdomain *omap_hwmod_get_pwrdm(struct omap_hwmod *oh)
        if (oh->clkdm)
                return oh->clkdm->pwrdm.ptr;
 
-       if (oh->_clk) {
+       if (oh->clkdm) {
+               return oh->clkdm->pwrdm.ptr;
+       } else if (oh->_clk) {
                c = oh->_clk;
        } else {
                oi = _find_mpu_rt_port(oh);
@@ -3770,17 +3858,8 @@ int omap_hwmod_del_initiator_dep(struct omap_hwmod *oh,
 int omap_hwmod_enable_wakeup(struct omap_hwmod *oh)
 {
        unsigned long flags;
-       u32 v;
 
        spin_lock_irqsave(&oh->_lock, flags);
-
-       if (oh->class->sysc &&
-           (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP)) {
-               v = oh->_sysc_cache;
-               _enable_wakeup(oh, &v);
-               _write_sysconfig(v, oh);
-       }
-
        _set_idle_ioring_wakeup(oh, true);
        spin_unlock_irqrestore(&oh->_lock, flags);
 
index 3ae852a522f95fd0a8ad5dc53a6dbcadb5a4faa7..82ec62daa8b6e4323d759d4c4e2af2df66e7f4be 100644 (file)
@@ -451,6 +451,11 @@ struct omap_hwmod_omap4_prcm {
  *     enabled.  This prevents the hwmod code from being able to
  *     enable and reset the IP block early.  XXX Eventually it should
  *     be possible to query the clock framework for this information.
+ * HWMOD_SWSUP_SIDLE_ACT: omap_hwmod code should manually bring the module out of
+ *     idle, but rely on smart-idle to the put it back in idle, so the wakeups
+ *     are still functional (Only known case for now is UART)
+ * HWMOD_ACCESS_DISABLED: this module cannot be accessed and must remain
+ *     disabled.
  */
 #define HWMOD_SWSUP_SIDLE                      (1 << 0)
 #define HWMOD_SWSUP_MSTANDBY                   (1 << 1)
@@ -462,21 +467,21 @@ struct omap_hwmod_omap4_prcm {
 #define HWMOD_CONTROL_OPT_CLKS_IN_RESET                (1 << 7)
 #define HWMOD_16BIT_REG                                (1 << 8)
 #define HWMOD_EXT_OPT_MAIN_CLK                 (1 << 9)
+#define HWMOD_SWSUP_SIDLE_ACT                  (1 << 10)
+#define HWMOD_ACCESS_DISABLED                  (1 << 11)
 
 /*
  * omap_hwmod._int_flags definitions
  * These are for internal use only and are managed by the omap_hwmod code.
  *
  * _HWMOD_NO_MPU_PORT: no path exists for the MPU to write to this module
- * _HWMOD_WAKEUP_ENABLED: set when the omap_hwmod code has enabled ENAWAKEUP
  * _HWMOD_SYSCONFIG_LOADED: set when the OCP_SYSCONFIG value has been cached
  * _HWMOD_SKIP_ENABLE: set if hwmod enabled during init (HWMOD_INIT_NO_IDLE) -
  *     causes the first call to _enable() to only update the pinmux
  */
 #define _HWMOD_NO_MPU_PORT                     (1 << 0)
-#define _HWMOD_WAKEUP_ENABLED                  (1 << 1)
-#define _HWMOD_SYSCONFIG_LOADED                        (1 << 2)
-#define _HWMOD_SKIP_ENABLE                     (1 << 3)
+#define _HWMOD_SYSCONFIG_LOADED                        (1 << 1)
+#define _HWMOD_SKIP_ENABLE                     (1 << 2)
 
 /*
  * omap_hwmod._state definitions
@@ -493,6 +498,8 @@ struct omap_hwmod_omap4_prcm {
 #define _HWMOD_STATE_ENABLED                   4
 #define _HWMOD_STATE_IDLE                      5
 #define _HWMOD_STATE_DISABLED                  6
+/* count of possible states */
+#define _HWMOD_STATE_COUNT                     (_HWMOD_STATE_DISABLED + 1)
 
 /**
  * struct omap_hwmod_class - the type of an IP block
@@ -501,6 +508,7 @@ struct omap_hwmod_omap4_prcm {
  * @rev: revision of the IP class
  * @pre_shutdown: ptr to fn to be executed immediately prior to device shutdown
  * @reset: ptr to fn to be executed in place of the standard hwmod reset fn
+ * @enable_preprogram:  ptr to fn to be executed during device enable
  *
  * Represent the class of a OMAP hardware "modules" (e.g. timer,
  * smartreflex, gpio, uart...)
@@ -524,6 +532,7 @@ struct omap_hwmod_class {
        u32                                     rev;
        int                                     (*pre_shutdown)(struct omap_hwmod *oh);
        int                                     (*reset)(struct omap_hwmod *oh);
+       int                                     (*enable_preprogram)(struct omap_hwmod *oh);
 };
 
 /**
@@ -610,6 +619,8 @@ struct omap_hwmod {
        u8                              _postsetup_state;
 };
 
+int omap_hwmod_register_flags(struct omap_hwmod **ohs,
+                             u32 set_flags, u32 clear_flags);
 struct omap_hwmod *omap_hwmod_lookup(const char *name);
 int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh, void *data),
                        void *data);
@@ -627,9 +638,6 @@ int omap_hwmod_read_hardreset(struct omap_hwmod *oh, const char *name);
 int omap_hwmod_enable_clocks(struct omap_hwmod *oh);
 int omap_hwmod_disable_clocks(struct omap_hwmod *oh);
 
-int omap_hwmod_set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode);
-int omap_hwmod_set_ocp_autoidle(struct omap_hwmod *oh, u8 autoidle);
-
 int omap_hwmod_reset(struct omap_hwmod *oh);
 void omap_hwmod_ocp_barrier(struct omap_hwmod *oh);
 
@@ -670,6 +678,12 @@ extern void __init omap_hwmod_init(void);
 
 const char *omap_hwmod_get_main_clk(struct omap_hwmod *oh);
 
+/*
+ *
+ */
+
+extern int omap_hwmod_aess_preprogram(struct omap_hwmod *oh);
+
 /*
  * Chip variant-specific hwmod init routines - XXX should be converted
  * to use initcalls once the initial boot ordering is straightened out
@@ -678,6 +692,7 @@ extern int omap2420_hwmod_init(void);
 extern int omap2430_hwmod_init(void);
 extern int omap3xxx_hwmod_init(void);
 extern int omap44xx_hwmod_init(void);
+extern int omap54xx_hwmod_init(void);
 extern int am33xx_hwmod_init(void);
 
 extern int __init omap_hwmod_register_links(struct omap_hwmod_ocp_if **ois);
index b5efe58c0be09cb4953dfcb70e0e93f21524a721..c674059e7b6c3ee5931de5e02f8e70c9629ac488 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/i2c-omap.h>
 #include <linux/platform_data/spi-omap2-mcspi.h>
 #include <linux/omap-dma.h>
+#include <linux/platform_data/mailbox-omap.h>
 #include <plat/dmtimer.h>
 
 #include "omap_hwmod.h"
@@ -161,6 +162,16 @@ static struct omap_hwmod omap2420_dma_system_hwmod = {
 };
 
 /* mailbox */
+static struct omap_mbox_dev_info omap2420_mailbox_info[] = {
+       { .name = "dsp", .tx_id = 0, .rx_id = 1, .irq_id = 0, .usr_id = 0 },
+       { .name = "iva", .tx_id = 2, .rx_id = 3, .irq_id = 1, .usr_id = 3 },
+};
+
+static struct omap_mbox_pdata omap2420_mailbox_attrs = {
+       .info_cnt       = ARRAY_SIZE(omap2420_mailbox_info),
+       .info           = omap2420_mailbox_info,
+};
+
 static struct omap_hwmod_irq_info omap2420_mailbox_irqs[] = {
        { .name = "dsp", .irq = 26 + OMAP_INTC_START, },
        { .name = "iva", .irq = 34 + OMAP_INTC_START, },
@@ -181,6 +192,7 @@ static struct omap_hwmod omap2420_mailbox_hwmod = {
                        .idlest_idle_bit = OMAP24XX_ST_MAILBOXES_SHIFT,
                },
        },
+       .dev_attr       = &omap2420_mailbox_attrs,
 };
 
 /*
@@ -605,6 +617,8 @@ static struct omap_hwmod_ocp_if *omap2420_hwmod_ocp_ifs[] __initdata = {
        &omap2420_l4_core__mcbsp2,
        &omap2420_l4_core__msdi1,
        &omap2xxx_l4_core__rng,
+       &omap2xxx_l4_core__sham,
+       &omap2xxx_l4_core__aes,
        &omap2420_l4_core__hdq1w,
        &omap2420_l4_wkup__counter_32k,
        &omap2420_l3__gpmc,
index d2d3840557c392b2033e3b3cbf65d1eb985e9833..df2f8742fe41dc8f5c0611867aff140343287167 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/platform_data/asoc-ti-mcbsp.h>
 #include <linux/platform_data/spi-omap2-mcspi.h>
 #include <linux/omap-dma.h>
+#include <linux/platform_data/mailbox-omap.h>
 #include <plat/dmtimer.h>
 
 #include "omap_hwmod.h"
@@ -170,6 +171,15 @@ static struct omap_hwmod omap2430_dma_system_hwmod = {
 };
 
 /* mailbox */
+static struct omap_mbox_dev_info omap2430_mailbox_info[] = {
+       { .name = "dsp", .tx_id = 0, .rx_id = 1 },
+};
+
+static struct omap_mbox_pdata omap2430_mailbox_attrs = {
+       .info_cnt       = ARRAY_SIZE(omap2430_mailbox_info),
+       .info           = omap2430_mailbox_info,
+};
+
 static struct omap_hwmod_irq_info omap2430_mailbox_irqs[] = {
        { .irq = 26 + OMAP_INTC_START, },
        { .irq = -1 },
@@ -189,6 +199,7 @@ static struct omap_hwmod omap2430_mailbox_hwmod = {
                        .idlest_idle_bit = OMAP24XX_ST_MAILBOXES_SHIFT,
                },
        },
+       .dev_attr       = &omap2430_mailbox_attrs,
 };
 
 /* mcspi3 */
@@ -963,6 +974,8 @@ static struct omap_hwmod_ocp_if *omap2430_hwmod_ocp_ifs[] __initdata = {
        &omap2430_l4_core__mcbsp5,
        &omap2430_l4_core__hdq1w,
        &omap2xxx_l4_core__rng,
+       &omap2xxx_l4_core__sham,
+       &omap2xxx_l4_core__aes,
        &omap2430_l4_wkup__counter_32k,
        &omap2430_l3__gpmc,
        NULL,
index 47901a5e76de517b9f86d5f4bc83b0614857e5e1..8d4d53d51526eefdac0923586cceedc024b22ab3 100644 (file)
@@ -138,6 +138,24 @@ static struct omap_hwmod_addr_space omap2_rng_addr_space[] = {
        { }
 };
 
+struct omap_hwmod_addr_space omap2xxx_sham_addrs[] = {
+       {
+               .pa_start       = 0x480a4000,
+               .pa_end         = 0x480a4000 + 0x64 - 1,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+struct omap_hwmod_addr_space omap2xxx_aes_addrs[] = {
+       {
+               .pa_start       = 0x480a6000,
+               .pa_end         = 0x480a6000 + 0x50 - 1,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
 /*
  * Common interconnect data
  */
@@ -389,3 +407,21 @@ struct omap_hwmod_ocp_if omap2xxx_l4_core__rng = {
        .addr           = omap2_rng_addr_space,
        .user           = OCP_USER_MPU | OCP_USER_SDMA,
 };
+
+/* l4 core -> sham interface */
+struct omap_hwmod_ocp_if omap2xxx_l4_core__sham = {
+       .master         = &omap2xxx_l4_core_hwmod,
+       .slave          = &omap2xxx_sham_hwmod,
+       .clk            = "sha_ick",
+       .addr           = omap2xxx_sham_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l4 core -> aes interface */
+struct omap_hwmod_ocp_if omap2xxx_l4_core__aes = {
+       .master         = &omap2xxx_l4_core_hwmod,
+       .slave          = &omap2xxx_aes_hwmod,
+       .clk            = "aes_ick",
+       .addr           = omap2xxx_aes_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
index e596117004d48c8faccd1c6c3b792b0b623d7b89..f9e18a553940546c03730cf78633c2db383d909d 100644 (file)
@@ -512,6 +512,7 @@ struct omap_hwmod omap2xxx_uart1_hwmod = {
        .mpu_irqs       = omap2_uart1_mpu_irqs,
        .sdma_reqs      = omap2_uart1_sdma_reqs,
        .main_clk       = "uart1_fck",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .prcm           = {
                .omap2 = {
                        .module_offs = CORE_MOD,
@@ -531,6 +532,7 @@ struct omap_hwmod omap2xxx_uart2_hwmod = {
        .mpu_irqs       = omap2_uart2_mpu_irqs,
        .sdma_reqs      = omap2_uart2_sdma_reqs,
        .main_clk       = "uart2_fck",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .prcm           = {
                .omap2 = {
                        .module_offs = CORE_MOD,
@@ -550,6 +552,7 @@ struct omap_hwmod omap2xxx_uart3_hwmod = {
        .mpu_irqs       = omap2_uart3_mpu_irqs,
        .sdma_reqs      = omap2_uart3_sdma_reqs,
        .main_clk       = "uart3_fck",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .prcm           = {
                .omap2 = {
                        .module_offs = CORE_MOD,
@@ -864,3 +867,84 @@ struct omap_hwmod omap2xxx_rng_hwmod = {
        .flags          = HWMOD_INIT_NO_RESET,
        .class          = &omap2_rng_hwmod_class,
 };
+
+/* SHAM */
+
+static struct omap_hwmod_class_sysconfig omap2_sham_sysc = {
+       .rev_offs       = 0x5c,
+       .sysc_offs      = 0x60,
+       .syss_offs      = 0x64,
+       .sysc_flags     = (SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE |
+                          SYSS_HAS_RESET_STATUS),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap2xxx_sham_class = {
+       .name   = "sham",
+       .sysc   = &omap2_sham_sysc,
+};
+
+struct omap_hwmod_irq_info omap2_sham_mpu_irqs[] = {
+       { .irq = 51 + OMAP_INTC_START, },
+       { .irq = -1 }
+};
+
+struct omap_hwmod_dma_info omap2_sham_sdma_chs[] = {
+       { .name = "rx", .dma_req = 13 },
+       { .dma_req = -1 }
+};
+
+struct omap_hwmod omap2xxx_sham_hwmod = {
+       .name           = "sham",
+       .mpu_irqs       = omap2_sham_mpu_irqs,
+       .sdma_reqs      = omap2_sham_sdma_chs,
+       .main_clk       = "l4_ck",
+       .prcm           = {
+               .omap2 = {
+                       .module_offs = CORE_MOD,
+                       .prcm_reg_id = 4,
+                       .module_bit = OMAP24XX_EN_SHA_SHIFT,
+                       .idlest_reg_id = 4,
+                       .idlest_idle_bit = OMAP24XX_ST_SHA_SHIFT,
+               },
+       },
+       .class          = &omap2xxx_sham_class,
+};
+
+/* AES */
+
+static struct omap_hwmod_class_sysconfig omap2_aes_sysc = {
+       .rev_offs       = 0x44,
+       .sysc_offs      = 0x48,
+       .syss_offs      = 0x4c,
+       .sysc_flags     = (SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE |
+                          SYSS_HAS_RESET_STATUS),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap2xxx_aes_class = {
+       .name   = "aes",
+       .sysc   = &omap2_aes_sysc,
+};
+
+struct omap_hwmod_dma_info omap2_aes_sdma_chs[] = {
+       { .name = "tx", .dma_req = 9 },
+       { .name = "rx", .dma_req = 10 },
+       { .dma_req = -1 }
+};
+
+struct omap_hwmod omap2xxx_aes_hwmod = {
+       .name           = "aes",
+       .sdma_reqs      = omap2_aes_sdma_chs,
+       .main_clk       = "l4_ck",
+       .prcm           = {
+               .omap2 = {
+                       .module_offs = CORE_MOD,
+                       .prcm_reg_id = 4,
+                       .module_bit = OMAP24XX_EN_AES_SHIFT,
+                       .idlest_reg_id = 4,
+                       .idlest_idle_bit = OMAP24XX_ST_AES_SHIFT,
+               },
+       },
+       .class          = &omap2xxx_aes_class,
+};
index 646c14d9fdb9dfbe656437b45c6757c8db4d1b15..f4783c0844529e672a7672b0e56874ecd481636e 100644 (file)
@@ -19,6 +19,7 @@
 #include "omap_hwmod.h"
 #include <linux/platform_data/gpio-omap.h>
 #include <linux/platform_data/spi-omap2-mcspi.h>
+#include <linux/platform_data/mailbox-omap.h>
 
 #include "omap_hwmod_common_data.h"
 
@@ -28,6 +29,7 @@
 #include "prm-regbits-33xx.h"
 #include "i2c.h"
 #include "mmc.h"
+#include "wd_timer.h"
 
 /*
  * IP blocks
@@ -262,13 +264,15 @@ static struct omap_hwmod am33xx_wkup_m3_hwmod = {
        .name           = "wkup_m3",
        .class          = &am33xx_wkup_m3_hwmod_class,
        .clkdm_name     = "l4_wkup_aon_clkdm",
-       .flags          = HWMOD_INIT_NO_RESET,  /* Keep hardreset asserted */
+       /* Keep hardreset asserted */
+       .flags          = HWMOD_INIT_NO_RESET | HWMOD_NO_IDLEST,
        .mpu_irqs       = am33xx_wkup_m3_irqs,
        .main_clk       = "dpll_core_m4_div2_ck",
        .prcm           = {
                .omap4  = {
                        .clkctrl_offs   = AM33XX_CM_WKUP_WKUP_M3_CLKCTRL_OFFSET,
                        .rstctrl_offs   = AM33XX_RM_WKUP_RSTCTRL_OFFSET,
+                       .rstst_offs     = AM33XX_RM_WKUP_RSTST_OFFSET,
                        .modulemode     = MODULEMODE_SWCTRL,
                },
        },
@@ -414,10 +418,7 @@ static struct omap_hwmod am33xx_adc_tsc_hwmod = {
  *    - cEFUSE (doesn't fall under any ocp_if)
  *    - clkdiv32k
  *    - debugss
- *    - ocmc ram
  *    - ocp watch point
- *    - aes0
- *    - sha0
  */
 #if 0
 /*
@@ -481,6 +482,24 @@ static struct omap_hwmod am33xx_debugss_hwmod = {
        },
 };
 
+/* ocpwp */
+static struct omap_hwmod_class am33xx_ocpwp_hwmod_class = {
+       .name           = "ocpwp",
+};
+
+static struct omap_hwmod am33xx_ocpwp_hwmod = {
+       .name           = "ocpwp",
+       .class          = &am33xx_ocpwp_hwmod_class,
+       .clkdm_name     = "l4ls_clkdm",
+       .main_clk       = "l4ls_gclk",
+       .prcm           = {
+               .omap4  = {
+                       .clkctrl_offs   = AM33XX_CM_PER_OCPWP_CLKCTRL_OFFSET,
+                       .modulemode     = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
 /* ocmcram */
 static struct omap_hwmod_class am33xx_ocmcram_hwmod_class = {
        .name = "ocmcram",
@@ -517,25 +536,41 @@ static struct omap_hwmod am33xx_ocpwp_hwmod = {
                },
        },
 };
+#endif
 
 /*
- * 'aes' class
+ * 'aes0' class
  */
-static struct omap_hwmod_class am33xx_aes_hwmod_class = {
-       .name           = "aes",
+static struct omap_hwmod_class_sysconfig am33xx_aes0_sysc = {
+       .rev_offs       = 0x80,
+       .sysc_offs      = 0x84,
+       .syss_offs      = 0x88,
+       .sysc_flags     = SYSS_HAS_RESET_STATUS,
+};
+
+static struct omap_hwmod_class am33xx_aes0_hwmod_class = {
+       .name           = "aes0",
+       .sysc           = &am33xx_aes0_sysc,
 };
 
 static struct omap_hwmod_irq_info am33xx_aes0_irqs[] = {
-       { .irq = 102 + OMAP_INTC_START, },
+       { .irq = 103 + OMAP_INTC_START, },
        { .irq = -1 },
 };
 
+struct omap_hwmod_dma_info am33xx_aes0_edma_reqs[] = {
+       { .name = "tx", .dma_req = 6, },
+       { .name = "rx", .dma_req = 5, },
+       { .dma_req = -1 }
+};
+
 static struct omap_hwmod am33xx_aes0_hwmod = {
-       .name           = "aes0",
-       .class          = &am33xx_aes_hwmod_class,
+       .name           = "aes",
+       .class          = &am33xx_aes0_hwmod_class,
        .clkdm_name     = "l3_clkdm",
        .mpu_irqs       = am33xx_aes0_irqs,
-       .main_clk       = "l3_gclk",
+       .sdma_reqs      = am33xx_aes0_edma_reqs,
+       .main_clk       = "aes0_fck",
        .prcm           = {
                .omap4  = {
                        .clkctrl_offs   = AM33XX_CM_PER_AES0_CLKCTRL_OFFSET,
@@ -544,21 +579,35 @@ static struct omap_hwmod am33xx_aes0_hwmod = {
        },
 };
 
-/* sha0 */
+/* sha0 HIB2 (the 'P' (public) device) */
+static struct omap_hwmod_class_sysconfig am33xx_sha0_sysc = {
+       .rev_offs       = 0x100,
+       .sysc_offs      = 0x110,
+       .syss_offs      = 0x114,
+       .sysc_flags     = SYSS_HAS_RESET_STATUS,
+};
+
 static struct omap_hwmod_class am33xx_sha0_hwmod_class = {
        .name           = "sha0",
+       .sysc           = &am33xx_sha0_sysc,
 };
 
 static struct omap_hwmod_irq_info am33xx_sha0_irqs[] = {
-       { .irq = 108 + OMAP_INTC_START, },
+       { .irq = 109 + OMAP_INTC_START, },
        { .irq = -1 },
 };
 
+struct omap_hwmod_dma_info am33xx_sha0_edma_reqs[] = {
+       { .name = "rx", .dma_req = 36, },
+       { .dma_req = -1 }
+};
+
 static struct omap_hwmod am33xx_sha0_hwmod = {
-       .name           = "sha0",
+       .name           = "sham",
        .class          = &am33xx_sha0_hwmod_class,
        .clkdm_name     = "l3_clkdm",
        .mpu_irqs       = am33xx_sha0_irqs,
+       .sdma_reqs      = am33xx_sha0_edma_reqs,
        .main_clk       = "l3_gclk",
        .prcm           = {
                .omap4  = {
@@ -568,8 +617,6 @@ static struct omap_hwmod am33xx_sha0_hwmod = {
        },
 };
 
-#endif
-
 /* 'smartreflex' class */
 static struct omap_hwmod_class am33xx_smartreflex_hwmod_class = {
        .name           = "smartreflex",
@@ -783,9 +830,7 @@ static struct omap_hwmod am33xx_elm_hwmod = {
        },
 };
 
-/*
- * 'epwmss' class: ecap0,1,2,  ehrpwm0,1,2
- */
+/* pwmss  */
 static struct omap_hwmod_class_sysconfig am33xx_epwmss_sysc = {
        .rev_offs       = 0x0,
        .sysc_offs      = 0x4,
@@ -801,18 +846,23 @@ static struct omap_hwmod_class am33xx_epwmss_hwmod_class = {
        .sysc           = &am33xx_epwmss_sysc,
 };
 
-/* ehrpwm0 */
-static struct omap_hwmod_irq_info am33xx_ehrpwm0_irqs[] = {
-       { .name = "int", .irq = 86 + OMAP_INTC_START, },
-       { .name = "tzint", .irq = 58 + OMAP_INTC_START, },
-       { .irq = -1 },
+static struct omap_hwmod_class am33xx_ecap_hwmod_class = {
+       .name           = "ecap",
 };
 
-static struct omap_hwmod am33xx_ehrpwm0_hwmod = {
-       .name           = "ehrpwm0",
+static struct omap_hwmod_class am33xx_eqep_hwmod_class = {
+       .name           = "eqep",
+};
+
+static struct omap_hwmod_class am33xx_ehrpwm_hwmod_class = {
+       .name           = "ehrpwm",
+};
+
+/* epwmss0 */
+static struct omap_hwmod am33xx_epwmss0_hwmod = {
+       .name           = "epwmss0",
        .class          = &am33xx_epwmss_hwmod_class,
        .clkdm_name     = "l4ls_clkdm",
-       .mpu_irqs       = am33xx_ehrpwm0_irqs,
        .main_clk       = "l4ls_gclk",
        .prcm           = {
                .omap4  = {
@@ -822,63 +872,58 @@ static struct omap_hwmod am33xx_ehrpwm0_hwmod = {
        },
 };
 
-/* ehrpwm1 */
-static struct omap_hwmod_irq_info am33xx_ehrpwm1_irqs[] = {
-       { .name = "int", .irq = 87 + OMAP_INTC_START, },
-       { .name = "tzint", .irq = 59 + OMAP_INTC_START, },
+/* ecap0 */
+static struct omap_hwmod_irq_info am33xx_ecap0_irqs[] = {
+       { .irq = 31 + OMAP_INTC_START, },
        { .irq = -1 },
 };
 
-static struct omap_hwmod am33xx_ehrpwm1_hwmod = {
-       .name           = "ehrpwm1",
-       .class          = &am33xx_epwmss_hwmod_class,
+static struct omap_hwmod am33xx_ecap0_hwmod = {
+       .name           = "ecap0",
+       .class          = &am33xx_ecap_hwmod_class,
        .clkdm_name     = "l4ls_clkdm",
-       .mpu_irqs       = am33xx_ehrpwm1_irqs,
+       .mpu_irqs       = am33xx_ecap0_irqs,
        .main_clk       = "l4ls_gclk",
-       .prcm           = {
-               .omap4  = {
-                       .clkctrl_offs   = AM33XX_CM_PER_EPWMSS1_CLKCTRL_OFFSET,
-                       .modulemode     = MODULEMODE_SWCTRL,
-               },
-       },
 };
 
-/* ehrpwm2 */
-static struct omap_hwmod_irq_info am33xx_ehrpwm2_irqs[] = {
-       { .name = "int", .irq = 39 + OMAP_INTC_START, },
-       { .name = "tzint", .irq = 60 + OMAP_INTC_START, },
+/* eqep0 */
+static struct omap_hwmod_irq_info am33xx_eqep0_irqs[] = {
+       { .irq = 79 + OMAP_INTC_START, },
        { .irq = -1 },
 };
 
-static struct omap_hwmod am33xx_ehrpwm2_hwmod = {
-       .name           = "ehrpwm2",
-       .class          = &am33xx_epwmss_hwmod_class,
+static struct omap_hwmod am33xx_eqep0_hwmod = {
+       .name           = "eqep0",
+       .class          = &am33xx_eqep_hwmod_class,
        .clkdm_name     = "l4ls_clkdm",
-       .mpu_irqs       = am33xx_ehrpwm2_irqs,
+       .mpu_irqs       = am33xx_eqep0_irqs,
        .main_clk       = "l4ls_gclk",
-       .prcm           = {
-               .omap4  = {
-                       .clkctrl_offs   = AM33XX_CM_PER_EPWMSS2_CLKCTRL_OFFSET,
-                       .modulemode     = MODULEMODE_SWCTRL,
-               },
-       },
 };
 
-/* ecap0 */
-static struct omap_hwmod_irq_info am33xx_ecap0_irqs[] = {
-       { .irq = 31 + OMAP_INTC_START, },
+/* ehrpwm0 */
+static struct omap_hwmod_irq_info am33xx_ehrpwm0_irqs[] = {
+       { .name = "int", .irq = 86 + OMAP_INTC_START, },
+       { .name = "tzint", .irq = 58 + OMAP_INTC_START, },
        { .irq = -1 },
 };
 
-static struct omap_hwmod am33xx_ecap0_hwmod = {
-       .name           = "ecap0",
+static struct omap_hwmod am33xx_ehrpwm0_hwmod = {
+       .name           = "ehrpwm0",
+       .class          = &am33xx_ehrpwm_hwmod_class,
+       .clkdm_name     = "l4ls_clkdm",
+       .mpu_irqs       = am33xx_ehrpwm0_irqs,
+       .main_clk       = "l4ls_gclk",
+};
+
+/* epwmss1 */
+static struct omap_hwmod am33xx_epwmss1_hwmod = {
+       .name           = "epwmss1",
        .class          = &am33xx_epwmss_hwmod_class,
        .clkdm_name     = "l4ls_clkdm",
-       .mpu_irqs       = am33xx_ecap0_irqs,
        .main_clk       = "l4ls_gclk",
        .prcm           = {
                .omap4  = {
-                       .clkctrl_offs   = AM33XX_CM_PER_EPWMSS0_CLKCTRL_OFFSET,
+                       .clkctrl_offs   = AM33XX_CM_PER_EPWMSS1_CLKCTRL_OFFSET,
                        .modulemode     = MODULEMODE_SWCTRL,
                },
        },
@@ -892,13 +937,50 @@ static struct omap_hwmod_irq_info am33xx_ecap1_irqs[] = {
 
 static struct omap_hwmod am33xx_ecap1_hwmod = {
        .name           = "ecap1",
-       .class          = &am33xx_epwmss_hwmod_class,
+       .class          = &am33xx_ecap_hwmod_class,
        .clkdm_name     = "l4ls_clkdm",
        .mpu_irqs       = am33xx_ecap1_irqs,
        .main_clk       = "l4ls_gclk",
+};
+
+/* eqep1 */
+static struct omap_hwmod_irq_info am33xx_eqep1_irqs[] = {
+       { .irq = 88 + OMAP_INTC_START, },
+       { .irq = -1 },
+};
+
+static struct omap_hwmod am33xx_eqep1_hwmod = {
+       .name           = "eqep1",
+       .class          = &am33xx_eqep_hwmod_class,
+       .clkdm_name     = "l4ls_clkdm",
+       .mpu_irqs       = am33xx_eqep1_irqs,
+       .main_clk       = "l4ls_gclk",
+};
+
+/* ehrpwm1 */
+static struct omap_hwmod_irq_info am33xx_ehrpwm1_irqs[] = {
+       { .name = "int", .irq = 87 + OMAP_INTC_START, },
+       { .name = "tzint", .irq = 59 + OMAP_INTC_START, },
+       { .irq = -1 },
+};
+
+static struct omap_hwmod am33xx_ehrpwm1_hwmod = {
+       .name           = "ehrpwm1",
+       .class          = &am33xx_ehrpwm_hwmod_class,
+       .clkdm_name     = "l4ls_clkdm",
+       .mpu_irqs       = am33xx_ehrpwm1_irqs,
+       .main_clk       = "l4ls_gclk",
+};
+
+/* epwmss2 */
+static struct omap_hwmod am33xx_epwmss2_hwmod = {
+       .name           = "epwmss2",
+       .class          = &am33xx_epwmss_hwmod_class,
+       .clkdm_name     = "l4ls_clkdm",
+       .main_clk       = "l4ls_gclk",
        .prcm           = {
                .omap4  = {
-                       .clkctrl_offs   = AM33XX_CM_PER_EPWMSS1_CLKCTRL_OFFSET,
+                       .clkctrl_offs   = AM33XX_CM_PER_EPWMSS2_CLKCTRL_OFFSET,
                        .modulemode     = MODULEMODE_SWCTRL,
                },
        },
@@ -912,16 +994,39 @@ static struct omap_hwmod_irq_info am33xx_ecap2_irqs[] = {
 
 static struct omap_hwmod am33xx_ecap2_hwmod = {
        .name           = "ecap2",
+       .class          = &am33xx_ecap_hwmod_class,
+       .clkdm_name     = "l4ls_clkdm",
        .mpu_irqs       = am33xx_ecap2_irqs,
-       .class          = &am33xx_epwmss_hwmod_class,
+       .main_clk       = "l4ls_gclk",
+};
+
+/* eqep2 */
+static struct omap_hwmod_irq_info am33xx_eqep2_irqs[] = {
+       { .irq = 89 + OMAP_INTC_START, },
+       { .irq = -1 },
+};
+
+static struct omap_hwmod am33xx_eqep2_hwmod = {
+       .name           = "eqep2",
+       .class          = &am33xx_eqep_hwmod_class,
        .clkdm_name     = "l4ls_clkdm",
+       .mpu_irqs       = am33xx_eqep2_irqs,
+       .main_clk       = "l4ls_gclk",
+};
+
+/* ehrpwm2 */
+static struct omap_hwmod_irq_info am33xx_ehrpwm2_irqs[] = {
+       { .name = "int", .irq = 39 + OMAP_INTC_START, },
+       { .name = "tzint", .irq = 60 + OMAP_INTC_START, },
+       { .irq = -1 },
+};
+
+static struct omap_hwmod am33xx_ehrpwm2_hwmod = {
+       .name           = "ehrpwm2",
+       .class          = &am33xx_ehrpwm_hwmod_class,
+       .clkdm_name     = "l4ls_clkdm",
+       .mpu_irqs       = am33xx_ehrpwm2_irqs,
        .main_clk       = "l4ls_gclk",
-       .prcm           = {
-               .omap4  = {
-                       .clkctrl_offs   = AM33XX_CM_PER_EPWMSS2_CLKCTRL_OFFSET,
-                       .modulemode     = MODULEMODE_SWCTRL,
-               },
-       },
 };
 
 /*
@@ -1262,6 +1367,16 @@ static struct omap_hwmod_class am33xx_mailbox_hwmod_class = {
        .sysc   = &am33xx_mailbox_sysc,
 };
 
+static struct omap_mbox_dev_info am33xx_mailbox_info[] = {
+       { .name = "wkup_m3", .tx_id = 0, .usr_id = 3 },
+};
+
+static struct omap_mbox_pdata am33xx_mailbox_attrs = {
+       .intr_type      = MBOX_INTR_CFG_TYPE2,
+       .info_cnt       = ARRAY_SIZE(am33xx_mailbox_info),
+       .info           = am33xx_mailbox_info,
+};
+
 static struct omap_hwmod_irq_info am33xx_mailbox_irqs[] = {
        { .irq = 77 + OMAP_INTC_START, },
        { .irq = -1 },
@@ -1279,6 +1394,7 @@ static struct omap_hwmod am33xx_mailbox_hwmod = {
                        .modulemode     = MODULEMODE_SWCTRL,
                },
        },
+       .dev_attr       = &am33xx_mailbox_attrs,
 };
 
 /*
@@ -1824,6 +1940,7 @@ static struct omap_hwmod am33xx_tptc0_hwmod = {
        .class          = &am33xx_tptc_hwmod_class,
        .clkdm_name     = "l3_clkdm",
        .mpu_irqs       = am33xx_tptc0_irqs,
+       .flags          = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY,
        .main_clk       = "l3_gclk",
        .prcm           = {
                .omap4  = {
@@ -1908,6 +2025,7 @@ static struct omap_hwmod am33xx_uart1_hwmod = {
        .name           = "uart1",
        .class          = &uart_class,
        .clkdm_name     = "l4_wkup_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .mpu_irqs       = am33xx_uart1_irqs,
        .sdma_reqs      = uart1_edma_reqs,
        .main_clk       = "dpll_per_m2_div4_wkupdm_ck",
@@ -1928,6 +2046,7 @@ static struct omap_hwmod am33xx_uart2_hwmod = {
        .name           = "uart2",
        .class          = &uart_class,
        .clkdm_name     = "l4ls_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .mpu_irqs       = am33xx_uart2_irqs,
        .sdma_reqs      = uart1_edma_reqs,
        .main_clk       = "dpll_per_m2_div4_ck",
@@ -1955,6 +2074,7 @@ static struct omap_hwmod am33xx_uart3_hwmod = {
        .name           = "uart3",
        .class          = &uart_class,
        .clkdm_name     = "l4ls_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .mpu_irqs       = am33xx_uart3_irqs,
        .sdma_reqs      = uart3_edma_reqs,
        .main_clk       = "dpll_per_m2_div4_ck",
@@ -1975,6 +2095,7 @@ static struct omap_hwmod am33xx_uart4_hwmod = {
        .name           = "uart4",
        .class          = &uart_class,
        .clkdm_name     = "l4ls_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .mpu_irqs       = am33xx_uart4_irqs,
        .sdma_reqs      = uart1_edma_reqs,
        .main_clk       = "dpll_per_m2_div4_ck",
@@ -1995,6 +2116,7 @@ static struct omap_hwmod am33xx_uart5_hwmod = {
        .name           = "uart5",
        .class          = &uart_class,
        .clkdm_name     = "l4ls_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .mpu_irqs       = am33xx_uart5_irqs,
        .sdma_reqs      = uart1_edma_reqs,
        .main_clk       = "dpll_per_m2_div4_ck",
@@ -2015,6 +2137,7 @@ static struct omap_hwmod am33xx_uart6_hwmod = {
        .name           = "uart6",
        .class          = &uart_class,
        .clkdm_name     = "l4ls_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .mpu_irqs       = am33xx_uart6_irqs,
        .sdma_reqs      = uart1_edma_reqs,
        .main_clk       = "dpll_per_m2_div4_ck",
@@ -2027,8 +2150,21 @@ static struct omap_hwmod am33xx_uart6_hwmod = {
 };
 
 /* 'wd_timer' class */
+static struct omap_hwmod_class_sysconfig wdt_sysc = {
+       .rev_offs       = 0x0,
+       .sysc_offs      = 0x10,
+       .syss_offs      = 0x14,
+       .sysc_flags     = (SYSC_HAS_EMUFREE | SYSC_HAS_SIDLEMODE |
+                       SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                       SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
 static struct omap_hwmod_class am33xx_wd_timer_hwmod_class = {
        .name           = "wd_timer",
+       .sysc           = &wdt_sysc,
+       .pre_shutdown   = &omap2_wd_timer_disable,
 };
 
 /*
@@ -2039,6 +2175,7 @@ static struct omap_hwmod am33xx_wd_timer1_hwmod = {
        .name           = "wd_timer2",
        .class          = &am33xx_wd_timer_hwmod_class,
        .clkdm_name     = "l4_wkup_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE,
        .main_clk       = "wdt1_fck",
        .prcm           = {
                .omap4  = {
@@ -2496,7 +2633,6 @@ static struct omap_hwmod_addr_space am33xx_cpgmac0_addr_space[] = {
        {
                .pa_start       = 0x4a100000,
                .pa_end         = 0x4a100000 + SZ_2K - 1,
-               .flags          = ADDR_TYPE_RT,
        },
        /* cpsw wr */
        {
@@ -2547,162 +2683,202 @@ static struct omap_hwmod_ocp_if am33xx_l4_ls__elm = {
        .user           = OCP_USER_MPU,
 };
 
-/*
- * Splitting the resources to handle access of PWMSS config space
- * and module specific part independently
- */
-static struct omap_hwmod_addr_space am33xx_ehrpwm0_addr_space[] = {
+static struct omap_hwmod_addr_space am33xx_epwmss0_addr_space[] = {
        {
                .pa_start       = 0x48300000,
                .pa_end         = 0x48300000 + SZ_16 - 1,
                .flags          = ADDR_TYPE_RT
        },
-       {
-               .pa_start       = 0x48300200,
-               .pa_end         = 0x48300200 + SZ_256 - 1,
-               .flags          = ADDR_TYPE_RT
-       },
        { }
 };
 
-static struct omap_hwmod_ocp_if am33xx_l4_ls__ehrpwm0 = {
+static struct omap_hwmod_ocp_if am33xx_l4_ls__epwmss0 = {
        .master         = &am33xx_l4_ls_hwmod,
-       .slave          = &am33xx_ehrpwm0_hwmod,
+       .slave          = &am33xx_epwmss0_hwmod,
        .clk            = "l4ls_gclk",
-       .addr           = am33xx_ehrpwm0_addr_space,
+       .addr           = am33xx_epwmss0_addr_space,
        .user           = OCP_USER_MPU,
 };
 
-/*
- * Splitting the resources to handle access of PWMSS config space
- * and module specific part independently
- */
-static struct omap_hwmod_addr_space am33xx_ehrpwm1_addr_space[] = {
-       {
-               .pa_start       = 0x48302000,
-               .pa_end         = 0x48302000 + SZ_16 - 1,
-               .flags          = ADDR_TYPE_RT
-       },
+static struct omap_hwmod_addr_space am33xx_ecap0_addr_space[] = {
        {
-               .pa_start       = 0x48302200,
-               .pa_end         = 0x48302200 + SZ_256 - 1,
-               .flags          = ADDR_TYPE_RT
+               .pa_start       = 0x48300100,
+               .pa_end         = 0x48300100 + SZ_128 - 1,
        },
        { }
 };
 
-static struct omap_hwmod_ocp_if am33xx_l4_ls__ehrpwm1 = {
-       .master         = &am33xx_l4_ls_hwmod,
-       .slave          = &am33xx_ehrpwm1_hwmod,
+static struct omap_hwmod_ocp_if am33xx_epwmss0__ecap0 = {
+       .master         = &am33xx_epwmss0_hwmod,
+       .slave          = &am33xx_ecap0_hwmod,
        .clk            = "l4ls_gclk",
-       .addr           = am33xx_ehrpwm1_addr_space,
+       .addr           = am33xx_ecap0_addr_space,
        .user           = OCP_USER_MPU,
 };
 
-/*
- * Splitting the resources to handle access of PWMSS config space
- * and module specific part independently
- */
-static struct omap_hwmod_addr_space am33xx_ehrpwm2_addr_space[] = {
-       {
-               .pa_start       = 0x48304000,
-               .pa_end         = 0x48304000 + SZ_16 - 1,
-               .flags          = ADDR_TYPE_RT
-       },
+static struct omap_hwmod_addr_space am33xx_eqep0_addr_space[] = {
        {
-               .pa_start       = 0x48304200,
-               .pa_end         = 0x48304200 + SZ_256 - 1,
-               .flags          = ADDR_TYPE_RT
+               .pa_start       = 0x48300180,
+               .pa_end         = 0x48300180 + SZ_128 - 1,
        },
        { }
 };
 
-static struct omap_hwmod_ocp_if am33xx_l4_ls__ehrpwm2 = {
-       .master         = &am33xx_l4_ls_hwmod,
-       .slave          = &am33xx_ehrpwm2_hwmod,
+static struct omap_hwmod_ocp_if am33xx_epwmss0__eqep0 = {
+       .master         = &am33xx_epwmss0_hwmod,
+       .slave          = &am33xx_eqep0_hwmod,
        .clk            = "l4ls_gclk",
-       .addr           = am33xx_ehrpwm2_addr_space,
+       .addr           = am33xx_eqep0_addr_space,
        .user           = OCP_USER_MPU,
 };
 
-/*
- * Splitting the resources to handle access of PWMSS config space
- * and module specific part independently
- */
-static struct omap_hwmod_addr_space am33xx_ecap0_addr_space[] = {
-       {
-               .pa_start       = 0x48300000,
-               .pa_end         = 0x48300000 + SZ_16 - 1,
-               .flags          = ADDR_TYPE_RT
-       },
+static struct omap_hwmod_addr_space am33xx_ehrpwm0_addr_space[] = {
        {
-               .pa_start       = 0x48300100,
-               .pa_end         = 0x48300100 + SZ_256 - 1,
-               .flags          = ADDR_TYPE_RT
+               .pa_start       = 0x48300200,
+               .pa_end         = 0x48300200 + SZ_128 - 1,
        },
        { }
 };
 
-static struct omap_hwmod_ocp_if am33xx_l4_ls__ecap0 = {
-       .master         = &am33xx_l4_ls_hwmod,
-       .slave          = &am33xx_ecap0_hwmod,
+static struct omap_hwmod_ocp_if am33xx_epwmss0__ehrpwm0 = {
+       .master         = &am33xx_epwmss0_hwmod,
+       .slave          = &am33xx_ehrpwm0_hwmod,
        .clk            = "l4ls_gclk",
-       .addr           = am33xx_ecap0_addr_space,
+       .addr           = am33xx_ehrpwm0_addr_space,
        .user           = OCP_USER_MPU,
 };
 
-/*
- * Splitting the resources to handle access of PWMSS config space
- * and module specific part independently
- */
-static struct omap_hwmod_addr_space am33xx_ecap1_addr_space[] = {
+
+static struct omap_hwmod_addr_space am33xx_epwmss1_addr_space[] = {
        {
                .pa_start       = 0x48302000,
                .pa_end         = 0x48302000 + SZ_16 - 1,
                .flags          = ADDR_TYPE_RT
        },
+       { }
+};
+
+static struct omap_hwmod_ocp_if am33xx_l4_ls__epwmss1 = {
+       .master         = &am33xx_l4_ls_hwmod,
+       .slave          = &am33xx_epwmss1_hwmod,
+       .clk            = "l4ls_gclk",
+       .addr           = am33xx_epwmss1_addr_space,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space am33xx_ecap1_addr_space[] = {
        {
                .pa_start       = 0x48302100,
-               .pa_end         = 0x48302100 + SZ_256 - 1,
-               .flags          = ADDR_TYPE_RT
+               .pa_end         = 0x48302100 + SZ_128 - 1,
        },
        { }
 };
 
-static struct omap_hwmod_ocp_if am33xx_l4_ls__ecap1 = {
-       .master         = &am33xx_l4_ls_hwmod,
+static struct omap_hwmod_ocp_if am33xx_epwmss1__ecap1 = {
+       .master         = &am33xx_epwmss1_hwmod,
        .slave          = &am33xx_ecap1_hwmod,
        .clk            = "l4ls_gclk",
        .addr           = am33xx_ecap1_addr_space,
        .user           = OCP_USER_MPU,
 };
 
-/*
- * Splitting the resources to handle access of PWMSS config space
- * and module specific part independently
- */
-static struct omap_hwmod_addr_space am33xx_ecap2_addr_space[] = {
+static struct omap_hwmod_addr_space am33xx_eqep1_addr_space[] = {
+       {
+               .pa_start       = 0x48302180,
+               .pa_end         = 0x48302180 + SZ_128 - 1,
+       },
+       { }
+};
+
+static struct omap_hwmod_ocp_if am33xx_epwmss1__eqep1 = {
+       .master         = &am33xx_epwmss1_hwmod,
+       .slave          = &am33xx_eqep1_hwmod,
+       .clk            = "l4ls_gclk",
+       .addr           = am33xx_eqep1_addr_space,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space am33xx_ehrpwm1_addr_space[] = {
+       {
+               .pa_start       = 0x48302200,
+               .pa_end         = 0x48302200 + SZ_128 - 1,
+       },
+       { }
+};
+
+static struct omap_hwmod_ocp_if am33xx_epwmss1__ehrpwm1 = {
+       .master         = &am33xx_epwmss1_hwmod,
+       .slave          = &am33xx_ehrpwm1_hwmod,
+       .clk            = "l4ls_gclk",
+       .addr           = am33xx_ehrpwm1_addr_space,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space am33xx_epwmss2_addr_space[] = {
        {
                .pa_start       = 0x48304000,
                .pa_end         = 0x48304000 + SZ_16 - 1,
                .flags          = ADDR_TYPE_RT
        },
+       { }
+};
+
+static struct omap_hwmod_ocp_if am33xx_l4_ls__epwmss2 = {
+       .master         = &am33xx_l4_ls_hwmod,
+       .slave          = &am33xx_epwmss2_hwmod,
+       .clk            = "l4ls_gclk",
+       .addr           = am33xx_epwmss2_addr_space,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space am33xx_ecap2_addr_space[] = {
        {
                .pa_start       = 0x48304100,
-               .pa_end         = 0x48304100 + SZ_256 - 1,
-               .flags          = ADDR_TYPE_RT
+               .pa_end         = 0x48304100 + SZ_128 - 1,
        },
        { }
 };
 
-static struct omap_hwmod_ocp_if am33xx_l4_ls__ecap2 = {
-       .master         = &am33xx_l4_ls_hwmod,
+static struct omap_hwmod_ocp_if am33xx_epwmss2__ecap2 = {
+       .master         = &am33xx_epwmss2_hwmod,
        .slave          = &am33xx_ecap2_hwmod,
        .clk            = "l4ls_gclk",
        .addr           = am33xx_ecap2_addr_space,
        .user           = OCP_USER_MPU,
 };
 
+static struct omap_hwmod_addr_space am33xx_eqep2_addr_space[] = {
+       {
+               .pa_start       = 0x48304180,
+               .pa_end         = 0x48304180 + SZ_128 - 1,
+       },
+       { }
+};
+
+static struct omap_hwmod_ocp_if am33xx_epwmss2__eqep2 = {
+       .master         = &am33xx_epwmss2_hwmod,
+       .slave          = &am33xx_eqep2_hwmod,
+       .clk            = "l4ls_gclk",
+       .addr           = am33xx_eqep2_addr_space,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space am33xx_ehrpwm2_addr_space[] = {
+       {
+               .pa_start       = 0x48304200,
+               .pa_end         = 0x48304200 + SZ_128 - 1,
+       },
+       { }
+};
+
+static struct omap_hwmod_ocp_if am33xx_epwmss2__ehrpwm2 = {
+       .master         = &am33xx_epwmss2_hwmod,
+       .slave          = &am33xx_ehrpwm2_hwmod,
+       .clk            = "l4ls_gclk",
+       .addr           = am33xx_ehrpwm2_addr_space,
+       .user           = OCP_USER_MPU,
+};
+
 /* l3s cfg -> gpmc */
 static struct omap_hwmod_addr_space am33xx_gpmc_addr_space[] = {
        {
@@ -3328,6 +3504,42 @@ static struct omap_hwmod_ocp_if am33xx_l3_s__usbss = {
        .flags          = OCPIF_SWSUP_IDLE,
 };
 
+/* l3 main -> sha0 HIB2 */
+static struct omap_hwmod_addr_space am33xx_sha0_addrs[] = {
+       {
+               .pa_start       = 0x53100000,
+               .pa_end         = 0x53100000 + SZ_512 - 1,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+static struct omap_hwmod_ocp_if am33xx_l3_main__sha0 = {
+       .master         = &am33xx_l3_main_hwmod,
+       .slave          = &am33xx_sha0_hwmod,
+       .clk            = "sha0_fck",
+       .addr           = am33xx_sha0_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l3 main -> AES0 HIB2 */
+static struct omap_hwmod_addr_space am33xx_aes0_addrs[] = {
+       {
+               .pa_start       = 0x53500000,
+               .pa_end         = 0x53500000 + SZ_1M - 1,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+static struct omap_hwmod_ocp_if am33xx_l3_main__aes0 = {
+       .master         = &am33xx_l3_main_hwmod,
+       .slave          = &am33xx_aes0_hwmod,
+       .clk            = "aes0_fck",
+       .addr           = am33xx_aes0_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
 static struct omap_hwmod_ocp_if *am33xx_hwmod_ocp_ifs[] __initdata = {
        &am33xx_l4_fw__emif_fw,
        &am33xx_l3_main__emif,
@@ -3385,12 +3597,18 @@ static struct omap_hwmod_ocp_if *am33xx_hwmod_ocp_ifs[] __initdata = {
        &am33xx_l4_ls__uart6,
        &am33xx_l4_ls__spinlock,
        &am33xx_l4_ls__elm,
-       &am33xx_l4_ls__ehrpwm0,
-       &am33xx_l4_ls__ehrpwm1,
-       &am33xx_l4_ls__ehrpwm2,
-       &am33xx_l4_ls__ecap0,
-       &am33xx_l4_ls__ecap1,
-       &am33xx_l4_ls__ecap2,
+       &am33xx_l4_ls__epwmss0,
+       &am33xx_epwmss0__ecap0,
+       &am33xx_epwmss0__eqep0,
+       &am33xx_epwmss0__ehrpwm0,
+       &am33xx_l4_ls__epwmss1,
+       &am33xx_epwmss1__ecap1,
+       &am33xx_epwmss1__eqep1,
+       &am33xx_epwmss1__ehrpwm1,
+       &am33xx_l4_ls__epwmss2,
+       &am33xx_epwmss2__ecap2,
+       &am33xx_epwmss2__eqep2,
+       &am33xx_epwmss2__ehrpwm2,
        &am33xx_l3_s__gpmc,
        &am33xx_l3_main__lcdc,
        &am33xx_l4_ls__mcspi0,
@@ -3401,6 +3619,8 @@ static struct omap_hwmod_ocp_if *am33xx_hwmod_ocp_ifs[] __initdata = {
        &am33xx_l3_s__usbss,
        &am33xx_l4_hs__cpgmac0,
        &am33xx_cpgmac0__mdio,
+       &am33xx_l3_main__sha0,
+       &am33xx_l3_main__aes0,
        NULL,
 };
 
index 8bb2628df34ed26522b30560f5eafc2c6a8c459c..3536289272f3a04c4c9ecf201f027f4693b803b3 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/platform_data/asoc-ti-mcbsp.h>
 #include <linux/platform_data/spi-omap2-mcspi.h>
 #include <linux/platform_data/iommu-omap.h>
+#include <linux/platform_data/mailbox-omap.h>
 #include <plat/dmtimer.h>
 
 #include "am35xx.h"
@@ -490,6 +491,7 @@ static struct omap_hwmod omap3xxx_uart1_hwmod = {
        .mpu_irqs       = omap2_uart1_mpu_irqs,
        .sdma_reqs      = omap2_uart1_sdma_reqs,
        .main_clk       = "uart1_fck",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .prcm           = {
                .omap2 = {
                        .module_offs = CORE_MOD,
@@ -508,6 +510,7 @@ static struct omap_hwmod omap3xxx_uart2_hwmod = {
        .mpu_irqs       = omap2_uart2_mpu_irqs,
        .sdma_reqs      = omap2_uart2_sdma_reqs,
        .main_clk       = "uart2_fck",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .prcm           = {
                .omap2 = {
                        .module_offs = CORE_MOD,
@@ -526,6 +529,7 @@ static struct omap_hwmod omap3xxx_uart3_hwmod = {
        .mpu_irqs       = omap2_uart3_mpu_irqs,
        .sdma_reqs      = omap2_uart3_sdma_reqs,
        .main_clk       = "uart3_fck",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .prcm           = {
                .omap2 = {
                        .module_offs = OMAP3430_PER_MOD,
@@ -555,6 +559,7 @@ static struct omap_hwmod omap36xx_uart4_hwmod = {
        .mpu_irqs       = uart4_mpu_irqs,
        .sdma_reqs      = uart4_sdma_reqs,
        .main_clk       = "uart4_fck",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .prcm           = {
                .omap2 = {
                        .module_offs = OMAP3430_PER_MOD,
@@ -1501,6 +1506,15 @@ static struct omap_hwmod_class omap3xxx_mailbox_hwmod_class = {
        .sysc = &omap3xxx_mailbox_sysc,
 };
 
+static struct omap_mbox_dev_info omap3xxx_mailbox_info[] = {
+       { .name = "dsp", .tx_id = 0, .rx_id = 1 },
+};
+
+static struct omap_mbox_pdata omap3xxx_mailbox_attrs = {
+       .info_cnt       = ARRAY_SIZE(omap3xxx_mailbox_info),
+       .info           = omap3xxx_mailbox_info,
+};
+
 static struct omap_hwmod_irq_info omap3xxx_mailbox_irqs[] = {
        { .irq = 26 + OMAP_INTC_START, },
        { .irq = -1 },
@@ -1520,6 +1534,7 @@ static struct omap_hwmod omap3xxx_mailbox_hwmod = {
                        .idlest_idle_bit = OMAP3430_ST_MAILBOXES_SHIFT,
                },
        },
+       .dev_attr       = &omap3xxx_mailbox_attrs,
 };
 
 /*
@@ -3540,6 +3555,132 @@ static struct omap_hwmod_ocp_if omap3xxx_l3_main__gpmc = {
        .user           = OCP_USER_MPU | OCP_USER_SDMA,
 };
 
+/* l4_core -> SHAM2 (SHA1/MD5) (similar to omap24xx) */
+static struct omap_hwmod_sysc_fields omap3_sham_sysc_fields = {
+       .sidle_shift    = 4,
+       .srst_shift     = 1,
+       .autoidle_shift = 0,
+};
+
+static struct omap_hwmod_class_sysconfig omap3_sham_sysc = {
+       .rev_offs       = 0x5c,
+       .sysc_offs      = 0x60,
+       .syss_offs      = 0x64,
+       .sysc_flags     = (SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET |
+                          SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS),
+       .sysc_fields    = &omap3_sham_sysc_fields,
+};
+
+static struct omap_hwmod_class omap3xxx_sham_class = {
+       .name   = "sham",
+       .sysc   = &omap3_sham_sysc,
+};
+
+struct omap_hwmod_irq_info omap3_sham_mpu_irqs[] = {
+       { .irq = 49 + OMAP_INTC_START, },
+       { .irq = -1 }
+};
+
+struct omap_hwmod_dma_info omap3_sham_sdma_reqs[] = {
+       { .name = "rx", .dma_req = OMAP34XX_DMA_SHA1MD5_RX, },
+       { .dma_req = -1 }
+};
+
+struct omap_hwmod omap3xxx_sham_hwmod = {
+       .name           = "sham",
+       .mpu_irqs       = omap3_sham_mpu_irqs,
+       .sdma_reqs      = omap3_sham_sdma_reqs,
+       .main_clk       = "sha12_ick",
+       .prcm           = {
+               .omap2 = {
+                       .module_offs = CORE_MOD,
+                       .prcm_reg_id = 1,
+                       .module_bit = OMAP3430_EN_SHA12_SHIFT,
+                       .idlest_reg_id = 1,
+                       .idlest_idle_bit = OMAP3430_ST_SHA12_SHIFT,
+               },
+       },
+       .class          = &omap3xxx_sham_class,
+};
+
+static struct omap_hwmod_addr_space omap3xxx_sham_addrs[] = {
+       {
+               .pa_start       = 0x480c3000,
+               .pa_end         = 0x480c3000 + 0x64 - 1,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+static struct omap_hwmod_ocp_if omap3xxx_l4_core__sham = {
+       .master         = &omap3xxx_l4_core_hwmod,
+       .slave          = &omap3xxx_sham_hwmod,
+       .clk            = "sha12_ick",
+       .addr           = omap3xxx_sham_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l4_core -> AES */
+static struct omap_hwmod_sysc_fields omap3xxx_aes_sysc_fields = {
+       .sidle_shift    = 6,
+       .srst_shift     = 1,
+       .autoidle_shift = 0,
+};
+
+static struct omap_hwmod_class_sysconfig omap3_aes_sysc = {
+       .rev_offs       = 0x44,
+       .sysc_offs      = 0x48,
+       .syss_offs      = 0x4c,
+       .sysc_flags     = (SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET |
+                          SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap3xxx_aes_sysc_fields,
+};
+
+static struct omap_hwmod_class omap3xxx_aes_class = {
+       .name   = "aes",
+       .sysc   = &omap3_aes_sysc,
+};
+
+struct omap_hwmod_dma_info omap3_aes_sdma_reqs[] = {
+       { .name = "tx", .dma_req = OMAP34XX_DMA_AES2_TX, },
+       { .name = "rx", .dma_req = OMAP34XX_DMA_AES2_RX, },
+       { .dma_req = -1 }
+};
+
+struct omap_hwmod omap3xxx_aes_hwmod = {
+       .name           = "aes",
+       .sdma_reqs      = omap3_aes_sdma_reqs,
+       .main_clk       = "aes2_ick",
+       .prcm           = {
+               .omap2 = {
+                       .module_offs = CORE_MOD,
+                       .prcm_reg_id = 1,
+                       .module_bit = OMAP3430_EN_AES2_SHIFT,
+                       .idlest_reg_id = 1,
+                       .idlest_idle_bit = OMAP3430_ST_AES2_SHIFT,
+               },
+       },
+       .class          = &omap3xxx_aes_class,
+};
+
+static struct omap_hwmod_addr_space omap3xxx_aes_addrs[] = {
+       {
+               .pa_start       = 0x480c5000,
+               .pa_end         = 0x480c5000 + 0x50 - 1,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+static struct omap_hwmod_ocp_if omap3xxx_l4_core__aes = {
+       .master         = &omap3xxx_l4_core_hwmod,
+       .slave          = &omap3xxx_aes_hwmod,
+       .clk            = "aes2_ick",
+       .addr           = omap3xxx_aes_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
 static struct omap_hwmod_ocp_if *omap3xxx_hwmod_ocp_ifs[] __initdata = {
        &omap3xxx_l3_main__l4_core,
        &omap3xxx_l3_main__l4_per,
@@ -3593,6 +3734,8 @@ static struct omap_hwmod_ocp_if *omap3xxx_hwmod_ocp_ifs[] __initdata = {
 /* GP-only hwmod links */
 static struct omap_hwmod_ocp_if *omap3xxx_gp_hwmod_ocp_ifs[] __initdata = {
        &omap3xxx_l4_sec__timer12,
+       &omap3xxx_l4_core__sham,
+       &omap3xxx_l4_core__aes,
        NULL
 };
 
index 793f54ac7d14b7cae5117404a564d527414d3e50..3df2453db0200a739bf81edfafa88c7faab91723 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/platform_data/spi-omap2-mcspi.h>
 #include <linux/platform_data/asoc-ti-mcbsp.h>
 #include <linux/platform_data/iommu-omap.h>
+#include <linux/platform_data/mailbox-omap.h>
 #include <plat/dmtimer.h>
 
 #include "omap_hwmod.h"
@@ -322,6 +323,7 @@ static struct omap_hwmod_class_sysconfig omap44xx_aess_sysc = {
 static struct omap_hwmod_class omap44xx_aess_hwmod_class = {
        .name   = "aess",
        .sysc   = &omap44xx_aess_sysc,
+       .enable_preprogram = omap_hwmod_aess_preprogram,
 };
 
 /* aess */
@@ -1860,6 +1862,17 @@ static struct omap_hwmod_class omap44xx_mailbox_hwmod_class = {
 };
 
 /* mailbox */
+static struct omap_mbox_dev_info omap44xx_mailbox_info[] = {
+       { .name = "mbox-ipu", .tx_id = 0, .rx_id = 1 },
+       { .name = "mbox-dsp", .tx_id = 3, .rx_id = 2 },
+};
+
+static struct omap_mbox_pdata omap44xx_mailbox_attrs = {
+       .intr_type      = MBOX_INTR_CFG_TYPE2,
+       .info_cnt       = ARRAY_SIZE(omap44xx_mailbox_info),
+       .info           = omap44xx_mailbox_info,
+};
+
 static struct omap_hwmod_irq_info omap44xx_mailbox_irqs[] = {
        { .irq = 26 + OMAP44XX_IRQ_GIC_START },
        { .irq = -1 }
@@ -1876,6 +1889,7 @@ static struct omap_hwmod omap44xx_mailbox_hwmod = {
                        .context_offs = OMAP4_RM_L4CFG_MAILBOX_CONTEXT_OFFSET,
                },
        },
+       .dev_attr       = &omap44xx_mailbox_attrs,
 };
 
 /*
@@ -2702,13 +2716,6 @@ static struct resource omap44xx_usb_phy_and_pll_addrs[] = {
                .end            = 0x4a0ae000,
                .flags          = IORESOURCE_MEM,
        },
-       {
-               /* XXX: Remove this once control module driver is in place */
-               .name           = "ctrl_dev",
-               .start          = 0x4a002300,
-               .end            = 0x4a002303,
-               .flags          = IORESOURCE_MEM,
-       },
        { }
 };
 
@@ -3431,6 +3438,7 @@ static struct omap_hwmod omap44xx_uart1_hwmod = {
        .name           = "uart1",
        .class          = &omap44xx_uart_hwmod_class,
        .clkdm_name     = "l4_per_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .mpu_irqs       = omap44xx_uart1_irqs,
        .sdma_reqs      = omap44xx_uart1_sdma_reqs,
        .main_clk       = "uart1_fck",
@@ -3459,6 +3467,7 @@ static struct omap_hwmod omap44xx_uart2_hwmod = {
        .name           = "uart2",
        .class          = &omap44xx_uart_hwmod_class,
        .clkdm_name     = "l4_per_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .mpu_irqs       = omap44xx_uart2_irqs,
        .sdma_reqs      = omap44xx_uart2_sdma_reqs,
        .main_clk       = "uart2_fck",
@@ -3487,7 +3496,8 @@ static struct omap_hwmod omap44xx_uart3_hwmod = {
        .name           = "uart3",
        .class          = &omap44xx_uart_hwmod_class,
        .clkdm_name     = "l4_per_clkdm",
-       .flags          = HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET,
+       .flags          = HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET |
+                               HWMOD_SWSUP_SIDLE_ACT,
        .mpu_irqs       = omap44xx_uart3_irqs,
        .sdma_reqs      = omap44xx_uart3_sdma_reqs,
        .main_clk       = "uart3_fck",
@@ -3516,6 +3526,7 @@ static struct omap_hwmod omap44xx_uart4_hwmod = {
        .name           = "uart4",
        .class          = &omap44xx_uart_hwmod_class,
        .clkdm_name     = "l4_per_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
        .mpu_irqs       = omap44xx_uart4_irqs,
        .sdma_reqs      = omap44xx_uart4_sdma_reqs,
        .main_clk       = "uart4_fck",
@@ -4249,6 +4260,27 @@ static struct omap_hwmod_ocp_if omap44xx_l4_cfg__ocp_wp_noc = {
 
 static struct omap_hwmod_addr_space omap44xx_aess_addrs[] = {
        {
+               .name           = "dmem",
+               .pa_start       = 0x40180000,
+               .pa_end         = 0x4018ffff
+       },
+       {
+               .name           = "cmem",
+               .pa_start       = 0x401a0000,
+               .pa_end         = 0x401a1fff
+       },
+       {
+               .name           = "smem",
+               .pa_start       = 0x401c0000,
+               .pa_end         = 0x401c5fff
+       },
+       {
+               .name           = "pmem",
+               .pa_start       = 0x401e0000,
+               .pa_end         = 0x401e1fff
+       },
+       {
+               .name           = "mpu",
                .pa_start       = 0x401f1000,
                .pa_end         = 0x401f13ff,
                .flags          = ADDR_TYPE_RT
@@ -4267,6 +4299,27 @@ static struct omap_hwmod_ocp_if __maybe_unused omap44xx_l4_abe__aess = {
 
 static struct omap_hwmod_addr_space omap44xx_aess_dma_addrs[] = {
        {
+               .name           = "dmem_dma",
+               .pa_start       = 0x49080000,
+               .pa_end         = 0x4908ffff
+       },
+       {
+               .name           = "cmem_dma",
+               .pa_start       = 0x490a0000,
+               .pa_end         = 0x490a1fff
+       },
+       {
+               .name           = "smem_dma",
+               .pa_start       = 0x490c0000,
+               .pa_end         = 0x490c5fff
+       },
+       {
+               .name           = "pmem_dma",
+               .pa_start       = 0x490e0000,
+               .pa_end         = 0x490e1fff
+       },
+       {
+               .name           = "dma",
                .pa_start       = 0x490f1000,
                .pa_end         = 0x490f13ff,
                .flags          = ADDR_TYPE_RT
@@ -4625,10 +4678,26 @@ static struct omap_hwmod_ocp_if omap44xx_l4_per__dss_dsi2 = {
 
 static struct omap_hwmod_addr_space omap44xx_dss_hdmi_dma_addrs[] = {
        {
+               .name           = "hdmi_wp",
                .pa_start       = 0x58006000,
-               .pa_end         = 0x58006fff,
+               .pa_end         = 0x580060ff,
                .flags          = ADDR_TYPE_RT
        },
+       {
+               .name           = "pllctrl",
+               .pa_start       = 0x58006200,
+               .pa_end         = 0x5800623f,
+       },
+       {
+               .name           = "hdmitxphy",
+               .pa_start       = 0x58006300,
+               .pa_end         = 0x5800633f,
+       },
+       {
+               .name           = "hdmi_core",
+               .pa_start       = 0x58006400,
+               .pa_end         = 0x58006dff,
+       },
        { }
 };
 
@@ -6156,12 +6225,6 @@ static struct omap_hwmod_addr_space omap44xx_usb_otg_hs_addrs[] = {
                .pa_end         = 0x4a0ab7ff,
                .flags          = ADDR_TYPE_RT
        },
-       {
-               /* XXX: Remove this once control module driver is in place */
-               .pa_start       = 0x4a00233c,
-               .pa_end         = 0x4a00233f,
-               .flags          = ADDR_TYPE_RT
-       },
        { }
 };
 
@@ -6282,7 +6345,7 @@ static struct omap_hwmod_ocp_if *omap44xx_hwmod_ocp_ifs[] __initdata = {
        &omap44xx_l3_main_1__l3_main_3,
        &omap44xx_l3_main_2__l3_main_3,
        &omap44xx_l4_cfg__l3_main_3,
-       /* &omap44xx_aess__l4_abe, */
+       &omap44xx_aess__l4_abe,
        &omap44xx_dsp__l4_abe,
        &omap44xx_l3_main_1__l4_abe,
        &omap44xx_mpu__l4_abe,
@@ -6291,8 +6354,8 @@ static struct omap_hwmod_ocp_if *omap44xx_hwmod_ocp_ifs[] __initdata = {
        &omap44xx_l4_cfg__l4_wkup,
        &omap44xx_mpu__mpu_private,
        &omap44xx_l4_cfg__ocp_wp_noc,
-       /* &omap44xx_l4_abe__aess, */
-       /* &omap44xx_l4_abe__aess_dma, */
+       &omap44xx_l4_abe__aess,
+       &omap44xx_l4_abe__aess_dma,
        &omap44xx_l3_main_2__c2c,
        &omap44xx_l4_wkup__counter_32k,
        &omap44xx_l4_cfg__ctrl_module_core,
diff --git a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c
new file mode 100644 (file)
index 0000000..ee7f4a9
--- /dev/null
@@ -0,0 +1,5781 @@
+/*
+ * Hardware modules present on the OMAP54xx chips
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Paul Walmsley
+ * Benoit Cousson
+ *
+ * This file is automatically generated from the OMAP hardware databases.
+ * We respectfully ask that any modifications to this file be coordinated
+ * with the public linux-omap@vger.kernel.org mailing list and the
+ * authors above to ensure that the autogeneration scripts are kept
+ * up-to-date with the file contents.
+ *
+ * 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/platform_data/gpio-omap.h>
+#include <linux/power/smartreflex.h>
+#include <linux/platform_data/omap_ocp2scp.h>
+#include <linux/i2c-omap.h>
+
+#include <linux/omap-dma.h>
+#include <linux/platform_data/spi-omap2-mcspi.h>
+#include <linux/platform_data/asoc-ti-mcbsp.h>
+#include <linux/platform_data/mailbox-omap.h>
+#include <plat/dmtimer.h>
+
+#include "omap_hwmod.h"
+#include "omap_hwmod_common_data.h"
+#include "cm1_54xx.h"
+#include "cm2_54xx.h"
+#include "prm54xx.h"
+#include "prm-regbits-54xx.h"
+#include "i2c.h"
+#include "mmc.h"
+#include "wd_timer.h"
+
+/* Base offset for all OMAP5 interrupts external to MPUSS */
+#define OMAP54XX_IRQ_GIC_START 32
+
+/* Base offset for all OMAP5 dma requests */
+#define OMAP54XX_DMA_REQ_START 1
+
+
+/*
+ * IP blocks
+ */
+
+/*
+ * 'dmm' class
+ * instance(s): dmm
+ */
+static struct omap_hwmod_class omap54xx_dmm_hwmod_class = {
+       .name   = "dmm",
+};
+
+/* dmm */
+static struct omap_hwmod_irq_info omap54xx_dmm_irqs[] = {
+       { .irq = 113 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_dmm_hwmod = {
+       .name           = "dmm",
+       .class          = &omap54xx_dmm_hwmod_class,
+       .clkdm_name     = "emif_clkdm",
+       .mpu_irqs       = omap54xx_dmm_irqs,
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_EMIF_DMM_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_EMIF_DMM_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/*
+ * 'emif_ocp_fw' class
+ * instance(s): emif_ocp_fw
+ */
+static struct omap_hwmod_class omap54xx_emif_ocp_fw_hwmod_class = {
+       .name   = "emif_ocp_fw",
+};
+
+/* emif_ocp_fw */
+static struct omap_hwmod omap54xx_emif_ocp_fw_hwmod = {
+       .name           = "emif_ocp_fw",
+       .class          = &omap54xx_emif_ocp_fw_hwmod_class,
+       .clkdm_name     = "emif_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_EMIF_EMIF_OCP_FW_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_EMIF_EMIF_OCP_FW_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/*
+ * 'l3' class
+ * instance(s): l3_instr, l3_main_1, l3_main_2, l3_main_3
+ */
+static struct omap_hwmod_class omap54xx_l3_hwmod_class = {
+       .name   = "l3",
+};
+
+/* l3_instr */
+static struct omap_hwmod omap54xx_l3_instr_hwmod = {
+       .name           = "l3_instr",
+       .class          = &omap54xx_l3_hwmod_class,
+       .clkdm_name     = "l3instr_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INSTR_L3_INSTR_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INSTR_L3_INSTR_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+};
+
+/* l3_main_1 */
+static struct omap_hwmod_irq_info omap54xx_l3_main_1_irqs[] = {
+       { .name = "dbg_err", .irq = 9 + OMAP54XX_IRQ_GIC_START },
+       { .name = "app_err", .irq = 10 + OMAP54XX_IRQ_GIC_START },
+       { .name = "stat_alarm", .irq = 16 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_l3_main_1_hwmod = {
+       .name           = "l3_main_1",
+       .class          = &omap54xx_l3_hwmod_class,
+       .clkdm_name     = "l3main1_clkdm",
+       .mpu_irqs       = omap54xx_l3_main_1_irqs,
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3MAIN1_L3_MAIN_1_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3MAIN1_L3_MAIN_1_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/* l3_main_2 */
+static struct omap_hwmod omap54xx_l3_main_2_hwmod = {
+       .name           = "l3_main_2",
+       .class          = &omap54xx_l3_hwmod_class,
+       .clkdm_name     = "l3main2_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3MAIN2_L3_MAIN_2_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3MAIN2_L3_MAIN_2_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/* l3_main_3 */
+static struct omap_hwmod omap54xx_l3_main_3_hwmod = {
+       .name           = "l3_main_3",
+       .class          = &omap54xx_l3_hwmod_class,
+       .clkdm_name     = "l3instr_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INSTR_L3_MAIN_3_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INSTR_L3_MAIN_3_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+};
+
+/*
+ * 'l4' class
+ * instance(s): l4_abe, l4_cfg, l4_per, l4_wkup
+ */
+static struct omap_hwmod_class omap54xx_l4_hwmod_class = {
+       .name   = "l4",
+};
+
+/* l4_abe */
+static struct omap_hwmod omap54xx_l4_abe_hwmod = {
+       .name           = "l4_abe",
+       .class          = &omap54xx_l4_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_L4_ABE_CLKCTRL_OFFSET,
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+};
+
+/* l4_cfg */
+static struct omap_hwmod omap54xx_l4_cfg_hwmod = {
+       .name           = "l4_cfg",
+       .class          = &omap54xx_l4_hwmod_class,
+       .clkdm_name     = "l4cfg_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4CFG_L4_CFG_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4CFG_L4_CFG_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/* l4_per */
+static struct omap_hwmod omap54xx_l4_per_hwmod = {
+       .name           = "l4_per",
+       .class          = &omap54xx_l4_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_L4_PER_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_L4_PER_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/* l4_wkup */
+static struct omap_hwmod omap54xx_l4_wkup_hwmod = {
+       .name           = "l4_wkup",
+       .class          = &omap54xx_l4_hwmod_class,
+       .clkdm_name     = "wkupaon_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_WKUPAON_L4_WKUP_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_WKUPAON_L4_WKUP_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/*
+ * 'mpu_bus' class
+ * instance(s): mpu_private
+ */
+static struct omap_hwmod_class omap54xx_mpu_bus_hwmod_class = {
+       .name   = "mpu_bus",
+};
+
+/* mpu_private */
+static struct omap_hwmod omap54xx_mpu_private_hwmod = {
+       .name           = "mpu_private",
+       .class          = &omap54xx_mpu_bus_hwmod_class,
+       .clkdm_name     = "mpu_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+};
+
+/*
+ * 'ocp_wp_noc' class
+ * instance(s): ocp_wp_noc
+ */
+static struct omap_hwmod_class omap54xx_ocp_wp_noc_hwmod_class = {
+       .name   = "ocp_wp_noc",
+};
+
+/* ocp_wp_noc */
+static struct omap_hwmod omap54xx_ocp_wp_noc_hwmod = {
+       .name           = "ocp_wp_noc",
+       .class          = &omap54xx_ocp_wp_noc_hwmod_class,
+       .clkdm_name     = "l3instr_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INSTR_OCP_WP_NOC_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INSTR_OCP_WP_NOC_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+};
+
+/*
+ * 'aess' class
+ * audio engine sub system
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_aess_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART |
+                          MSTANDBY_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_aess_hwmod_class = {
+       .name   = "aess",
+       .sysc   = &omap54xx_aess_sysc,
+};
+
+/* aess */
+static struct omap_hwmod_irq_info omap54xx_aess_irqs[] = {
+       { .irq = 99 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_aess_sdma_reqs[] = {
+       { .name = "fifo0", .dma_req = 100 + OMAP54XX_DMA_REQ_START },
+       { .name = "fifo1", .dma_req = 101 + OMAP54XX_DMA_REQ_START },
+       { .name = "fifo2", .dma_req = 102 + OMAP54XX_DMA_REQ_START },
+       { .name = "fifo3", .dma_req = 103 + OMAP54XX_DMA_REQ_START },
+       { .name = "fifo4", .dma_req = 104 + OMAP54XX_DMA_REQ_START },
+       { .name = "fifo5", .dma_req = 105 + OMAP54XX_DMA_REQ_START },
+       { .name = "fifo6", .dma_req = 106 + OMAP54XX_DMA_REQ_START },
+       { .name = "fifo7", .dma_req = 107 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_aess_hwmod = {
+       .name           = "aess",
+       .class          = &omap54xx_aess_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .mpu_irqs       = omap54xx_aess_irqs,
+       .sdma_reqs      = omap54xx_aess_sdma_reqs,
+       .main_clk       = "aess_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_AESS_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_AESS_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'bb2d' class
+ * bit blit 2d accelerator
+ */
+
+static struct omap_hwmod_class omap54xx_bb2d_hwmod_class = {
+       .name   = "bb2d",
+};
+
+/* bb2d */
+static struct omap_hwmod_irq_info omap54xx_bb2d_irqs[] = {
+       { .irq = 125 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_bb2d_hwmod = {
+       .name           = "bb2d",
+       .class          = &omap54xx_bb2d_hwmod_class,
+       .clkdm_name     = "dss_clkdm",
+       .mpu_irqs       = omap54xx_bb2d_irqs,
+       .main_clk       = "dpll_core_h24x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_DSS_BB2D_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_DSS_BB2D_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'c2c' class
+ * chip 2 chip interface used to plug the ape soc (omap) with an external modem
+ * soc
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_c2c_sysc = {
+       .rev_offs       = 0x0000,
+       .syss_offs      = 0x0008,
+       .sysc_flags     = SYSS_HAS_RESET_STATUS,
+};
+
+static struct omap_hwmod_class omap54xx_c2c_hwmod_class = {
+       .name   = "c2c",
+       .sysc   = &omap54xx_c2c_sysc,
+};
+
+/* c2c */
+static struct omap_hwmod_irq_info omap54xx_c2c_irqs[] = {
+       { .irq = 88 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_c2c_sdma_reqs[] = {
+       { .dma_req = 68 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_c2c_hwmod = {
+       .name           = "c2c",
+       .class          = &omap54xx_c2c_hwmod_class,
+       .clkdm_name     = "c2c_clkdm",
+       .mpu_irqs       = omap54xx_c2c_irqs,
+       .sdma_reqs      = omap54xx_c2c_sdma_reqs,
+       .main_clk       = "c2c_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_C2C_C2C_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_C2C_C2C_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/*
+ * 'counter' class
+ * 32-bit ordinary counter, clocked by the falling edge of the 32 khz clock
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_counter_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = SYSC_HAS_SIDLEMODE,
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_counter_hwmod_class = {
+       .name   = "counter",
+       .sysc   = &omap54xx_counter_sysc,
+};
+
+/* counter_32k */
+static struct omap_hwmod omap54xx_counter_32k_hwmod = {
+       .name           = "counter_32k",
+       .class          = &omap54xx_counter_hwmod_class,
+       .clkdm_name     = "wkupaon_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE,
+       .main_clk       = "wkupaon_iclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_WKUPAON_COUNTER_32K_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_WKUPAON_COUNTER_32K_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/*
+ * 'ctrl_module' class
+ * omap5430 core control module + omap5430 wkup control module
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_ctrl_module_sysc = {
+       .rev_offs       = 0x0000,
+};
+
+static struct omap_hwmod_class omap54xx_ctrl_module_hwmod_class = {
+       .name   = "ctrl_module",
+       .sysc   = &omap54xx_ctrl_module_sysc,
+};
+
+/* ctrl_module_core */
+static struct omap_hwmod_irq_info omap54xx_ctrl_module_core_irqs[] = {
+       { .name = "sec_evts", .irq = 8 + OMAP54XX_IRQ_GIC_START },
+       { .name = "thermal_alert", .irq = 126 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_ctrl_module_core_hwmod = {
+       .name           = "ctrl_module_core",
+       .class          = &omap54xx_ctrl_module_hwmod_class,
+       .clkdm_name     = "l4cfg_clkdm",
+       .mpu_irqs       = omap54xx_ctrl_module_core_irqs,
+       .prcm = {
+               .omap4 = {
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+};
+
+/* ctrl_module_wkup */
+static struct omap_hwmod omap54xx_ctrl_module_wkup_hwmod = {
+       .name           = "ctrl_module_wkup",
+       .class          = &omap54xx_ctrl_module_hwmod_class,
+       .clkdm_name     = "wkupaon_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+};
+
+/*
+ * 'dma' class
+ * dma controller for data exchange between memory to memory (i.e. internal or
+ * external memory) and gp peripherals to memory or memory to gp peripherals
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_dma_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x002c,
+       .syss_offs      = 0x0028,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+                          SYSC_HAS_EMUFREE | SYSC_HAS_MIDLEMODE |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET |
+                          SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_dma_hwmod_class = {
+       .name   = "dma",
+       .sysc   = &omap54xx_dma_sysc,
+};
+
+/* dma dev_attr */
+static struct omap_dma_dev_attr dma_dev_attr = {
+       .dev_caps       = RESERVE_CHANNEL | DMA_LINKED_LCH | GLOBAL_PRIORITY |
+                         IS_CSSA_32 | IS_CDSA_32 | IS_RW_PRIORITY,
+       .lch_count      = 32,
+};
+
+/* dma_system */
+static struct omap_hwmod_irq_info omap54xx_dma_system_irqs[] = {
+       { .name = "0", .irq = 12 + OMAP54XX_IRQ_GIC_START },
+       { .name = "1", .irq = 13 + OMAP54XX_IRQ_GIC_START },
+       { .name = "2", .irq = 14 + OMAP54XX_IRQ_GIC_START },
+       { .name = "3", .irq = 15 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_dma_system_hwmod = {
+       .name           = "dma_system",
+       .class          = &omap54xx_dma_hwmod_class,
+       .clkdm_name     = "dma_clkdm",
+       .mpu_irqs       = omap54xx_dma_system_irqs,
+       .main_clk       = "l3_iclk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_DMA_DMA_SYSTEM_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_DMA_DMA_SYSTEM_CONTEXT_OFFSET,
+               },
+       },
+       .dev_attr       = &dma_dev_attr,
+};
+
+/*
+ * 'dmic' class
+ * digital microphone controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_dmic_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_EMUFREE | SYSC_HAS_RESET_STATUS |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_dmic_hwmod_class = {
+       .name   = "dmic",
+       .sysc   = &omap54xx_dmic_sysc,
+};
+
+/* dmic */
+static struct omap_hwmod_irq_info omap54xx_dmic_irqs[] = {
+       { .irq = 114 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_dmic_sdma_reqs[] = {
+       { .dma_req = 66 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_dmic_hwmod = {
+       .name           = "dmic",
+       .class          = &omap54xx_dmic_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .mpu_irqs       = omap54xx_dmic_irqs,
+       .sdma_reqs      = omap54xx_dmic_sdma_reqs,
+       .main_clk       = "dmic_gfclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_DMIC_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_DMIC_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'dsp' class
+ * dsp sub-system
+ */
+
+static struct omap_hwmod_class omap54xx_dsp_hwmod_class = {
+       .name   = "dsp",
+};
+
+/* dsp */
+static struct omap_hwmod_irq_info omap54xx_dsp_irqs[] = {
+       { .irq = 28 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_rst_info omap54xx_dsp_resets[] = {
+       { .name = "dsp", .rst_shift = 0 },
+       { .name = "mmu_cache", .rst_shift = 1 },
+};
+
+static struct omap_hwmod omap54xx_dsp_hwmod = {
+       .name           = "dsp",
+       .class          = &omap54xx_dsp_hwmod_class,
+       .clkdm_name     = "dsp_clkdm",
+       .mpu_irqs       = omap54xx_dsp_irqs,
+       .rst_lines      = omap54xx_dsp_resets,
+       .rst_lines_cnt  = ARRAY_SIZE(omap54xx_dsp_resets),
+       .main_clk       = "dpll_iva_h11x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_DSP_DSP_CLKCTRL_OFFSET,
+                       .rstctrl_offs = OMAP54XX_RM_DSP_RSTCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_DSP_DSP_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+};
+
+/*
+ * 'dss' class
+ * display sub-system
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_dss_sysc = {
+       .rev_offs       = 0x0000,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = SYSS_HAS_RESET_STATUS,
+};
+
+static struct omap_hwmod_class omap54xx_dss_hwmod_class = {
+       .name   = "dss",
+       .sysc   = &omap54xx_dss_sysc,
+       .reset  = omap_dss_reset,
+};
+
+/* dss */
+static struct omap_hwmod_opt_clk dss_opt_clks[] = {
+       { .role = "32khz_clk", .clk = "dss_32khz_clk" },
+       { .role = "sys_clk", .clk = "dss_sys_clk" },
+       { .role = "hdmi_clk", .clk = "dss_48mhz_clk" },
+};
+
+static struct omap_hwmod omap54xx_dss_hwmod = {
+       .name           = "dss_core",
+       .class          = &omap54xx_dss_hwmod_class,
+       .clkdm_name     = "dss_clkdm",
+       .flags          = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
+       .main_clk       = "dss_dss_clk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_DSS_DSS_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_DSS_DSS_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .opt_clks       = dss_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(dss_opt_clks),
+};
+
+/*
+ * 'dispc' class
+ * display controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_dispc_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+                          SYSC_HAS_ENAWAKEUP | SYSC_HAS_MIDLEMODE |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET |
+                          SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_dispc_hwmod_class = {
+       .name   = "dispc",
+       .sysc   = &omap54xx_dispc_sysc,
+};
+
+/* dss_dispc */
+static struct omap_hwmod_irq_info omap54xx_dss_dispc_irqs[] = {
+       { .irq = 25 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_dss_dispc_sdma_reqs[] = {
+       { .dma_req = 5 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod_opt_clk dss_dispc_opt_clks[] = {
+       { .role = "sys_clk", .clk = "dss_sys_clk" },
+};
+
+/* dss_dispc dev_attr */
+static struct omap_dss_dispc_dev_attr dss_dispc_dev_attr = {
+       .has_framedonetv_irq    = 1,
+       .manager_count          = 4,
+};
+
+static struct omap_hwmod omap54xx_dss_dispc_hwmod = {
+       .name           = "dss_dispc",
+       .class          = &omap54xx_dispc_hwmod_class,
+       .clkdm_name     = "dss_clkdm",
+       .mpu_irqs       = omap54xx_dss_dispc_irqs,
+       .sdma_reqs      = omap54xx_dss_dispc_sdma_reqs,
+       .main_clk       = "dss_dss_clk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_DSS_DSS_CLKCTRL_OFFSET,
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+       .opt_clks       = dss_dispc_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(dss_dispc_opt_clks),
+       .dev_attr       = &dss_dispc_dev_attr,
+};
+
+/*
+ * 'dsi1' class
+ * display serial interface controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_dsi1_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+                          SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_dsi1_hwmod_class = {
+       .name   = "dsi1",
+       .sysc   = &omap54xx_dsi1_sysc,
+};
+
+/* dss_dsi1_a */
+static struct omap_hwmod_irq_info omap54xx_dss_dsi1_a_irqs[] = {
+       { .irq = 53 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_dss_dsi1_a_sdma_reqs[] = {
+       { .dma_req = 74 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod_opt_clk dss_dsi1_a_opt_clks[] = {
+       { .role = "sys_clk", .clk = "dss_sys_clk" },
+};
+
+static struct omap_hwmod omap54xx_dss_dsi1_a_hwmod = {
+       .name           = "dss_dsi1_a",
+       .class          = &omap54xx_dsi1_hwmod_class,
+       .clkdm_name     = "dss_clkdm",
+       .mpu_irqs       = omap54xx_dss_dsi1_a_irqs,
+       .sdma_reqs      = omap54xx_dss_dsi1_a_sdma_reqs,
+       .main_clk       = "dss_dss_clk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_DSS_DSS_CLKCTRL_OFFSET,
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+       .opt_clks       = dss_dsi1_a_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(dss_dsi1_a_opt_clks),
+};
+
+/* dss_dsi1_b */
+static struct omap_hwmod omap54xx_dss_dsi1_b_hwmod = {
+       .name           = "dss_dsi1_b",
+       .class          = &omap54xx_dsi1_hwmod_class,
+       .clkdm_name     = "dss_clkdm",
+       .main_clk       = "dss_dss_clk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_DSS_DSS_CLKCTRL_OFFSET,
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+};
+
+/* dss_dsi1_c */
+static struct omap_hwmod_irq_info omap54xx_dss_dsi1_c_irqs[] = {
+       { .irq = 55 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_dss_dsi1_c_sdma_reqs[] = {
+       { .dma_req = 83 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod_opt_clk dss_dsi1_c_opt_clks[] = {
+       { .role = "sys_clk", .clk = "dss_sys_clk" },
+};
+
+static struct omap_hwmod omap54xx_dss_dsi1_c_hwmod = {
+       .name           = "dss_dsi1_c",
+       .class          = &omap54xx_dsi1_hwmod_class,
+       .clkdm_name     = "dss_clkdm",
+       .mpu_irqs       = omap54xx_dss_dsi1_c_irqs,
+       .sdma_reqs      = omap54xx_dss_dsi1_c_sdma_reqs,
+       .main_clk       = "dss_dss_clk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_DSS_DSS_CLKCTRL_OFFSET,
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+       .opt_clks       = dss_dsi1_c_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(dss_dsi1_c_opt_clks),
+};
+
+/*
+ * 'hdmi' class
+ * hdmi controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_hdmi_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_RESET_STATUS | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_hdmi_hwmod_class = {
+       .name   = "hdmi",
+       .sysc   = &omap54xx_hdmi_sysc,
+};
+
+/* dss_hdmi */
+static struct omap_hwmod_irq_info omap54xx_dss_hdmi_irqs[] = {
+       { .irq = 101 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_dss_hdmi_sdma_reqs[] = {
+       { .dma_req = 75 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod_opt_clk dss_hdmi_opt_clks[] = {
+       { .role = "sys_clk", .clk = "dss_sys_clk" },
+};
+
+static struct omap_hwmod omap54xx_dss_hdmi_hwmod = {
+       .name           = "dss_hdmi",
+       .class          = &omap54xx_hdmi_hwmod_class,
+       .clkdm_name     = "dss_clkdm",
+       .mpu_irqs       = omap54xx_dss_hdmi_irqs,
+       .sdma_reqs      = omap54xx_dss_hdmi_sdma_reqs,
+       .main_clk       = "dss_48mhz_clk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_DSS_DSS_CLKCTRL_OFFSET,
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+       .opt_clks       = dss_hdmi_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(dss_hdmi_opt_clks),
+};
+
+/*
+ * 'rfbi' class
+ * remote frame buffer interface
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_rfbi_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_rfbi_hwmod_class = {
+       .name   = "rfbi",
+       .sysc   = &omap54xx_rfbi_sysc,
+};
+
+/* dss_rfbi */
+static struct omap_hwmod_dma_info omap54xx_dss_rfbi_sdma_reqs[] = {
+       { .dma_req = 13 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod_opt_clk dss_rfbi_opt_clks[] = {
+       { .role = "ick", .clk = "l3_iclk_div" },
+};
+
+static struct omap_hwmod omap54xx_dss_rfbi_hwmod = {
+       .name           = "dss_rfbi",
+       .class          = &omap54xx_rfbi_hwmod_class,
+       .clkdm_name     = "dss_clkdm",
+       .sdma_reqs      = omap54xx_dss_rfbi_sdma_reqs,
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_DSS_DSS_CLKCTRL_OFFSET,
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+       .opt_clks       = dss_rfbi_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(dss_rfbi_opt_clks),
+};
+
+/*
+ * 'elm' class
+ * bch error location module
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_elm_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET |
+                          SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_elm_hwmod_class = {
+       .name   = "elm",
+       .sysc   = &omap54xx_elm_sysc,
+};
+
+/* elm */
+static struct omap_hwmod_irq_info omap54xx_elm_irqs[] = {
+       { .irq = 4 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_elm_hwmod = {
+       .name           = "elm",
+       .class          = &omap54xx_elm_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_elm_irqs,
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_ELM_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_ELM_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/*
+ * 'emif' class
+ * external memory interface no1 (wrapper)
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_emif_sysc = {
+       .rev_offs       = 0x0000,
+};
+
+static struct omap_hwmod_class omap54xx_emif_hwmod_class = {
+       .name   = "emif",
+       .sysc   = &omap54xx_emif_sysc,
+};
+
+/* emif1 */
+static struct omap_hwmod_irq_info omap54xx_emif1_irqs[] = {
+       { .irq = 110 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_emif1_hwmod = {
+       .name           = "emif1",
+       .class          = &omap54xx_emif_hwmod_class,
+       .clkdm_name     = "emif_clkdm",
+       .flags          = HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET,
+       .mpu_irqs       = omap54xx_emif1_irqs,
+       .main_clk       = "dpll_core_h11x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_EMIF_EMIF1_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_EMIF_EMIF1_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+};
+
+/* emif2 */
+static struct omap_hwmod_irq_info omap54xx_emif2_irqs[] = {
+       { .irq = 111 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_emif2_hwmod = {
+       .name           = "emif2",
+       .class          = &omap54xx_emif_hwmod_class,
+       .clkdm_name     = "emif_clkdm",
+       .flags          = HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET,
+       .mpu_irqs       = omap54xx_emif2_irqs,
+       .main_clk       = "dpll_core_h11x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_EMIF_EMIF2_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_EMIF_EMIF2_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+};
+
+/*
+ * 'fdif' class
+ * face detection hw accelerator module
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_fdif_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       /*
+        * FDIF needs 100 OCP clk cycles delay after a softreset before
+        * accessing sysconfig again.
+        * The lowest frequency at the moment for L3 bus is 100 MHz, so
+        * 1usec delay is needed. Add an x2 margin to be safe (2 usecs).
+        *
+        * TODO: Indicate errata when available.
+        */
+       .srst_udelay    = 2,
+       .sysc_flags     = (SYSC_HAS_MIDLEMODE | SYSC_HAS_RESET_STATUS |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_fdif_hwmod_class = {
+       .name   = "fdif",
+       .sysc   = &omap54xx_fdif_sysc,
+};
+
+/* fdif */
+static struct omap_hwmod_irq_info omap54xx_fdif_irqs[] = {
+       { .irq = 69 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_fdif_hwmod = {
+       .name           = "fdif",
+       .class          = &omap54xx_fdif_hwmod_class,
+       .clkdm_name     = "cam_clkdm",
+       .mpu_irqs       = omap54xx_fdif_irqs,
+       .main_clk       = "fdif_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_CAM_FDIF_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_CAM_FDIF_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'gpio' class
+ * general purpose io module
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_gpio_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0114,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_ENAWAKEUP |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET |
+                          SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_gpio_hwmod_class = {
+       .name   = "gpio",
+       .sysc   = &omap54xx_gpio_sysc,
+       .rev    = 2,
+};
+
+/* gpio dev_attr */
+static struct omap_gpio_dev_attr gpio_dev_attr = {
+       .bank_width     = 32,
+       .dbck_flag      = true,
+};
+
+/* gpio1 */
+static struct omap_hwmod_irq_info omap54xx_gpio1_irqs[] = {
+       { .irq = 29 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk gpio1_opt_clks[] = {
+       { .role = "dbclk", .clk = "gpio1_dbclk" },
+};
+
+static struct omap_hwmod omap54xx_gpio1_hwmod = {
+       .name           = "gpio1",
+       .class          = &omap54xx_gpio_hwmod_class,
+       .clkdm_name     = "wkupaon_clkdm",
+       .mpu_irqs       = omap54xx_gpio1_irqs,
+       .main_clk       = "wkupaon_iclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_WKUPAON_GPIO1_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_WKUPAON_GPIO1_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+       .opt_clks       = gpio1_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(gpio1_opt_clks),
+       .dev_attr       = &gpio_dev_attr,
+};
+
+/* gpio2 */
+static struct omap_hwmod_irq_info omap54xx_gpio2_irqs[] = {
+       { .irq = 30 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk gpio2_opt_clks[] = {
+       { .role = "dbclk", .clk = "gpio2_dbclk" },
+};
+
+static struct omap_hwmod omap54xx_gpio2_hwmod = {
+       .name           = "gpio2",
+       .class          = &omap54xx_gpio_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
+       .mpu_irqs       = omap54xx_gpio2_irqs,
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_GPIO2_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_GPIO2_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+       .opt_clks       = gpio2_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(gpio2_opt_clks),
+       .dev_attr       = &gpio_dev_attr,
+};
+
+/* gpio3 */
+static struct omap_hwmod_irq_info omap54xx_gpio3_irqs[] = {
+       { .irq = 31 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk gpio3_opt_clks[] = {
+       { .role = "dbclk", .clk = "gpio3_dbclk" },
+};
+
+static struct omap_hwmod omap54xx_gpio3_hwmod = {
+       .name           = "gpio3",
+       .class          = &omap54xx_gpio_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
+       .mpu_irqs       = omap54xx_gpio3_irqs,
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_GPIO3_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_GPIO3_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+       .opt_clks       = gpio3_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(gpio3_opt_clks),
+       .dev_attr       = &gpio_dev_attr,
+};
+
+/* gpio4 */
+static struct omap_hwmod_irq_info omap54xx_gpio4_irqs[] = {
+       { .irq = 32 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk gpio4_opt_clks[] = {
+       { .role = "dbclk", .clk = "gpio4_dbclk" },
+};
+
+static struct omap_hwmod omap54xx_gpio4_hwmod = {
+       .name           = "gpio4",
+       .class          = &omap54xx_gpio_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
+       .mpu_irqs       = omap54xx_gpio4_irqs,
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_GPIO4_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_GPIO4_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+       .opt_clks       = gpio4_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(gpio4_opt_clks),
+       .dev_attr       = &gpio_dev_attr,
+};
+
+/* gpio5 */
+static struct omap_hwmod_irq_info omap54xx_gpio5_irqs[] = {
+       { .irq = 33 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk gpio5_opt_clks[] = {
+       { .role = "dbclk", .clk = "gpio5_dbclk" },
+};
+
+static struct omap_hwmod omap54xx_gpio5_hwmod = {
+       .name           = "gpio5",
+       .class          = &omap54xx_gpio_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
+       .mpu_irqs       = omap54xx_gpio5_irqs,
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_GPIO5_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_GPIO5_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+       .opt_clks       = gpio5_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(gpio5_opt_clks),
+       .dev_attr       = &gpio_dev_attr,
+};
+
+/* gpio6 */
+static struct omap_hwmod_irq_info omap54xx_gpio6_irqs[] = {
+       { .irq = 34 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk gpio6_opt_clks[] = {
+       { .role = "dbclk", .clk = "gpio6_dbclk" },
+};
+
+static struct omap_hwmod omap54xx_gpio6_hwmod = {
+       .name           = "gpio6",
+       .class          = &omap54xx_gpio_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
+       .mpu_irqs       = omap54xx_gpio6_irqs,
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_GPIO6_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_GPIO6_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+       .opt_clks       = gpio6_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(gpio6_opt_clks),
+       .dev_attr       = &gpio_dev_attr,
+};
+
+/* gpio7 */
+static struct omap_hwmod_irq_info omap54xx_gpio7_irqs[] = {
+       { .irq = 35 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk gpio7_opt_clks[] = {
+       { .role = "dbclk", .clk = "gpio7_dbclk" },
+};
+
+static struct omap_hwmod omap54xx_gpio7_hwmod = {
+       .name           = "gpio7",
+       .class          = &omap54xx_gpio_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
+       .mpu_irqs       = omap54xx_gpio7_irqs,
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_GPIO7_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_GPIO7_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+       .opt_clks       = gpio7_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(gpio7_opt_clks),
+       .dev_attr       = &gpio_dev_attr,
+};
+
+/* gpio8 */
+static struct omap_hwmod_irq_info omap54xx_gpio8_irqs[] = {
+       { .irq = 121 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk gpio8_opt_clks[] = {
+       { .role = "dbclk", .clk = "gpio8_dbclk" },
+};
+
+static struct omap_hwmod omap54xx_gpio8_hwmod = {
+       .name           = "gpio8",
+       .class          = &omap54xx_gpio_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
+       .mpu_irqs       = omap54xx_gpio8_irqs,
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_GPIO8_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_GPIO8_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+       .opt_clks       = gpio8_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(gpio8_opt_clks),
+       .dev_attr       = &gpio_dev_attr,
+};
+
+/*
+ * 'gpmc' class
+ * general purpose memory controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_gpmc_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_gpmc_hwmod_class = {
+       .name   = "gpmc",
+       .sysc   = &omap54xx_gpmc_sysc,
+};
+
+/* gpmc */
+static struct omap_hwmod_irq_info omap54xx_gpmc_irqs[] = {
+       { .irq = 20 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_gpmc_sdma_reqs[] = {
+       { .dma_req = 3 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_gpmc_hwmod = {
+       .name           = "gpmc",
+       .class          = &omap54xx_gpmc_hwmod_class,
+       .clkdm_name     = "l3main2_clkdm",
+       .flags          = HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET,
+       .mpu_irqs       = omap54xx_gpmc_irqs,
+       .sdma_reqs      = omap54xx_gpmc_sdma_reqs,
+       .main_clk       = "l3_iclk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3MAIN2_GPMC_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3MAIN2_GPMC_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+};
+
+/*
+ * 'gpu' class
+ * 2d/3d graphics accelerator
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_gpu_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO |
+                          MSTANDBY_SMART | MSTANDBY_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_gpu_hwmod_class = {
+       .name   = "gpu",
+       .sysc   = &omap54xx_gpu_sysc,
+};
+
+/* gpu */
+static struct omap_hwmod_irq_info omap54xx_gpu_irqs[] = {
+       { .irq = 21 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_gpu_hwmod = {
+       .name           = "gpu",
+       .class          = &omap54xx_gpu_hwmod_class,
+       .clkdm_name     = "gpu_clkdm",
+       .mpu_irqs       = omap54xx_gpu_irqs,
+       .main_clk       = "gpu_core_gclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_GPU_GPU_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_GPU_GPU_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'hdq1w' class
+ * hdq / 1-wire serial interface controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_hdq1w_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0014,
+       .syss_offs      = 0x0018,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_SOFTRESET |
+                          SYSS_HAS_RESET_STATUS),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_hdq1w_hwmod_class = {
+       .name   = "hdq1w",
+       .sysc   = &omap54xx_hdq1w_sysc,
+};
+
+/* hdq1w */
+static struct omap_hwmod_irq_info omap54xx_hdq1w_irqs[] = {
+       { .irq = 58 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_hdq1w_hwmod = {
+       .name           = "hdq1w",
+       .class          = &omap54xx_hdq1w_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_INIT_NO_RESET,
+       .mpu_irqs       = omap54xx_hdq1w_irqs,
+       .main_clk       = "func_12m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_HDQ1W_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_HDQ1W_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'hsi' class
+ * mipi high-speed synchronous serial interface (multichannel and full-duplex
+ * serial if)
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_hsi_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_EMUFREE |
+                          SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO |
+                          MSTANDBY_SMART | MSTANDBY_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_hsi_hwmod_class = {
+       .name   = "hsi",
+       .sysc   = &omap54xx_hsi_sysc,
+};
+
+/* hsi */
+static struct omap_hwmod_irq_info omap54xx_hsi_irqs[] = {
+       { .name = "mpu_p1", .irq = 67 + OMAP54XX_IRQ_GIC_START },
+       { .name = "mpu_p2", .irq = 68 + OMAP54XX_IRQ_GIC_START },
+       { .name = "mpu_dma", .irq = 71 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_hsi_hwmod = {
+       .name           = "hsi",
+       .class          = &omap54xx_hsi_hwmod_class,
+       .clkdm_name     = "l3init_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY,
+       .mpu_irqs       = omap54xx_hsi_irqs,
+       .main_clk       = "hsi_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INIT_HSI_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INIT_HSI_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+};
+
+/*
+ * 'i2c' class
+ * multimaster high-speed i2c controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_i2c_sysc = {
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0090,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+                          SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .clockact       = CLOCKACT_TEST_ICLK,
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_i2c_hwmod_class = {
+       .name   = "i2c",
+       .sysc   = &omap54xx_i2c_sysc,
+       .reset  = &omap_i2c_reset,
+       .rev    = OMAP_I2C_IP_VERSION_2,
+};
+
+/* i2c dev_attr */
+static struct omap_i2c_dev_attr i2c_dev_attr = {
+       .flags  = OMAP_I2C_FLAG_BUS_SHIFT_NONE,
+};
+
+/* i2c1 */
+static struct omap_hwmod_irq_info omap54xx_i2c1_irqs[] = {
+       { .irq = 56 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_i2c1_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 26 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 27 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_i2c1_hwmod = {
+       .name           = "i2c1",
+       .class          = &omap54xx_i2c_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_16BIT_REG | HWMOD_SET_DEFAULT_CLOCKACT,
+       .mpu_irqs       = omap54xx_i2c1_irqs,
+       .sdma_reqs      = omap54xx_i2c1_sdma_reqs,
+       .main_clk       = "func_96m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_I2C1_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_I2C1_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &i2c_dev_attr,
+};
+
+/* i2c2 */
+static struct omap_hwmod_irq_info omap54xx_i2c2_irqs[] = {
+       { .irq = 57 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_i2c2_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 28 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 29 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_i2c2_hwmod = {
+       .name           = "i2c2",
+       .class          = &omap54xx_i2c_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_16BIT_REG | HWMOD_SET_DEFAULT_CLOCKACT,
+       .mpu_irqs       = omap54xx_i2c2_irqs,
+       .sdma_reqs      = omap54xx_i2c2_sdma_reqs,
+       .main_clk       = "func_96m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_I2C2_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_I2C2_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &i2c_dev_attr,
+};
+
+/* i2c3 */
+static struct omap_hwmod_irq_info omap54xx_i2c3_irqs[] = {
+       { .irq = 61 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_i2c3_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 24 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 25 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_i2c3_hwmod = {
+       .name           = "i2c3",
+       .class          = &omap54xx_i2c_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_16BIT_REG | HWMOD_SET_DEFAULT_CLOCKACT,
+       .mpu_irqs       = omap54xx_i2c3_irqs,
+       .sdma_reqs      = omap54xx_i2c3_sdma_reqs,
+       .main_clk       = "func_96m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_I2C3_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_I2C3_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &i2c_dev_attr,
+};
+
+/* i2c4 */
+static struct omap_hwmod_irq_info omap54xx_i2c4_irqs[] = {
+       { .irq = 62 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_i2c4_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 123 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 124 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_i2c4_hwmod = {
+       .name           = "i2c4",
+       .class          = &omap54xx_i2c_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_16BIT_REG | HWMOD_SET_DEFAULT_CLOCKACT,
+       .mpu_irqs       = omap54xx_i2c4_irqs,
+       .sdma_reqs      = omap54xx_i2c4_sdma_reqs,
+       .main_clk       = "func_96m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_I2C4_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_I2C4_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &i2c_dev_attr,
+};
+
+/* i2c5 */
+static struct omap_hwmod_irq_info omap54xx_i2c5_irqs[] = {
+       { .irq = 60 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_i2c5_hwmod = {
+       .name           = "i2c5",
+       .class          = &omap54xx_i2c_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_16BIT_REG | HWMOD_SET_DEFAULT_CLOCKACT,
+       .mpu_irqs       = omap54xx_i2c5_irqs,
+       .main_clk       = "func_96m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_I2C5_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_I2C5_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &i2c_dev_attr,
+};
+
+/*
+ * 'ipu' class
+ * imaging processor unit
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_ipu_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET |
+                          SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_ipu_hwmod_class = {
+       .name   = "ipu",
+       .sysc   = &omap54xx_ipu_sysc,
+};
+
+/* ipu */
+static struct omap_hwmod_irq_info omap54xx_ipu_irqs[] = {
+       { .irq = 100 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_rst_info omap54xx_ipu_resets[] = {
+       { .name = "cpu0", .rst_shift = 0 },
+       { .name = "cpu1", .rst_shift = 1 },
+       { .name = "mmu_cache", .rst_shift = 2 },
+};
+
+static struct omap_hwmod omap54xx_ipu_hwmod = {
+       .name           = "ipu",
+       .class          = &omap54xx_ipu_hwmod_class,
+       .clkdm_name     = "ipu_clkdm",
+       .mpu_irqs       = omap54xx_ipu_irqs,
+       .rst_lines      = omap54xx_ipu_resets,
+       .rst_lines_cnt  = ARRAY_SIZE(omap54xx_ipu_resets),
+       .main_clk       = "dpll_core_h22x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_IPU_IPU_CLKCTRL_OFFSET,
+                       .rstctrl_offs = OMAP54XX_RM_IPU_RSTCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_IPU_IPU_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+};
+
+/*
+ * 'intc' class
+ * nested vectored interrupt controller
+ */
+
+static struct omap_hwmod_class omap54xx_intc_hwmod_class = {
+       .name   = "intc",
+};
+
+/* intc_ipu_c0 */
+static struct omap_hwmod omap54xx_intc_ipu_c0_hwmod = {
+       .name           = "intc_ipu_c0",
+       .class          = &omap54xx_intc_hwmod_class,
+       .clkdm_name     = "ipu_clkdm",
+       .main_clk       = "dpll_core_h22x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+};
+
+/* intc_ipu_c1 */
+static struct omap_hwmod omap54xx_intc_ipu_c1_hwmod = {
+       .name           = "intc_ipu_c1",
+       .class          = &omap54xx_intc_hwmod_class,
+       .clkdm_name     = "ipu_clkdm",
+       .main_clk       = "dpll_core_h22x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+};
+
+/*
+ * 'iss' class
+ * external images sensor pixel data processor
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_iss_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       /*
+        * ISS needs 100 OCP clk cycles delay after a softreset before
+        * accessing sysconfig again.
+        * The lowest frequency at the moment for L3 bus is 100 MHz, so
+        * 1usec delay is needed. Add an x2 margin to be safe (2 usecs).
+        *
+        * TODO: Indicate errata when available.
+        */
+       .srst_udelay    = 2,
+       .sysc_flags     = (SYSC_HAS_MIDLEMODE | SYSC_HAS_RESET_STATUS |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO |
+                          MSTANDBY_SMART | MSTANDBY_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_iss_hwmod_class = {
+       .name   = "iss",
+       .sysc   = &omap54xx_iss_sysc,
+};
+
+/* iss */
+static struct omap_hwmod_irq_info omap54xx_iss_irqs[] = {
+       { .irq = 24 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_iss_sdma_reqs[] = {
+       { .name = "1", .dma_req = 8 + OMAP54XX_DMA_REQ_START },
+       { .name = "2", .dma_req = 9 + OMAP54XX_DMA_REQ_START },
+       { .name = "3", .dma_req = 11 + OMAP54XX_DMA_REQ_START },
+       { .name = "4", .dma_req = 12 + OMAP54XX_DMA_REQ_START },
+       { .name = "5", .dma_req = 30 + OMAP54XX_DMA_REQ_START },
+       { .name = "6", .dma_req = 31 + OMAP54XX_DMA_REQ_START },
+       { .name = "7", .dma_req = 125 + OMAP54XX_DMA_REQ_START },
+       { .name = "8", .dma_req = 126 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod_opt_clk iss_opt_clks[] = {
+       { .role = "ctrlclk", .clk = "iss_ctrlclk" },
+};
+
+static struct omap_hwmod omap54xx_iss_hwmod = {
+       .name           = "iss",
+       .class          = &omap54xx_iss_hwmod_class,
+       .clkdm_name     = "cam_clkdm",
+       .flags          = HWMOD_INIT_NO_RESET,
+       .mpu_irqs       = omap54xx_iss_irqs,
+       .sdma_reqs      = omap54xx_iss_sdma_reqs,
+       .main_clk       = "dpll_core_h22x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_CAM_ISS_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_CAM_ISS_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .opt_clks       = iss_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(iss_opt_clks),
+};
+
+/*
+ * 'iva' class
+ * multi-standard video encoder/decoder hardware accelerator
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_iva_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE),
+       .idlemodes      = (SIDLE_NO | SIDLE_SMART | MSTANDBY_NO |
+                          MSTANDBY_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_iva_hwmod_class = {
+       .name   = "iva",
+       .sysc   = &omap54xx_iva_sysc,
+};
+
+/* iva */
+static struct omap_hwmod_irq_info omap54xx_iva_irqs[] = {
+       { .name = "sync_1", .irq = 103 + OMAP54XX_IRQ_GIC_START },
+       { .name = "sync_0", .irq = 104 + OMAP54XX_IRQ_GIC_START },
+       { .name = "mailbox_0", .irq = 107 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_rst_info omap54xx_iva_resets[] = {
+       { .name = "seq0", .rst_shift = 0 },
+       { .name = "seq1", .rst_shift = 1 },
+       { .name = "logic", .rst_shift = 2 },
+};
+
+static struct omap_hwmod omap54xx_iva_hwmod = {
+       .name           = "iva",
+       .class          = &omap54xx_iva_hwmod_class,
+       .clkdm_name     = "iva_clkdm",
+       .mpu_irqs       = omap54xx_iva_irqs,
+       .rst_lines      = omap54xx_iva_resets,
+       .rst_lines_cnt  = ARRAY_SIZE(omap54xx_iva_resets),
+       .main_clk       = "dpll_iva_h12x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_IVA_IVA_CLKCTRL_OFFSET,
+                       .rstctrl_offs = OMAP54XX_RM_IVA_RSTCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_IVA_IVA_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+};
+
+/*
+ * 'kbd' class
+ * keyboard controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_kbd_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_EMUFREE | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_kbd_hwmod_class = {
+       .name   = "kbd",
+       .sysc   = &omap54xx_kbd_sysc,
+};
+
+/* kbd */
+static struct omap_hwmod_irq_info omap54xx_kbd_irqs[] = {
+       { .irq = 120 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_kbd_hwmod = {
+       .name           = "kbd",
+       .class          = &omap54xx_kbd_hwmod_class,
+       .clkdm_name     = "wkupaon_clkdm",
+       .mpu_irqs       = omap54xx_kbd_irqs,
+       .main_clk       = "sys_32k_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_WKUPAON_KBD_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_WKUPAON_KBD_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'mailbox' class
+ * mailbox module allowing communication between the on-chip processors
+ * useusing a queued mailbox-interrupt mechanism.
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_mailbox_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_RESET_STATUS | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_mailbox_hwmod_class = {
+       .name   = "mailbox",
+       .sysc   = &omap54xx_mailbox_sysc,
+};
+
+/* mailbox */
+static struct omap_mbox_dev_info omap54xx_mbox_info[] = {
+       { .name = "mbox-ipu", .tx_id = 0, .rx_id = 1 },
+       { .name = "mbox-dsp", .tx_id = 3, .rx_id = 2 },
+};
+
+static struct omap_mbox_pdata omap54xx_mbox_attrs = {
+       .intr_type      = MBOX_INTR_CFG_TYPE2,
+       .info_cnt       = ARRAY_SIZE(omap54xx_mbox_info),
+       .info           = omap54xx_mbox_info,
+};
+
+static struct omap_hwmod_irq_info omap54xx_mailbox_irqs[] = {
+       { .irq = 26 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_mailbox_hwmod = {
+       .name           = "mailbox",
+       .class          = &omap54xx_mailbox_hwmod_class,
+       .clkdm_name     = "l4cfg_clkdm",
+       .mpu_irqs       = omap54xx_mailbox_irqs,
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4CFG_MAILBOX_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4CFG_MAILBOX_CONTEXT_OFFSET,
+               },
+       },
+       .dev_attr       = &omap54xx_mbox_attrs,
+};
+
+/*
+ * 'mcasp' class
+ * multi-channel audio serial port controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_mcasp_sysc = {
+       .sysc_offs      = 0x0004,
+       .sysc_flags     = SYSC_HAS_SIDLEMODE,
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type3,
+};
+
+static struct omap_hwmod_class omap54xx_mcasp_hwmod_class = {
+       .name   = "mcasp",
+       .sysc   = &omap54xx_mcasp_sysc,
+};
+
+/* mcasp */
+static struct omap_hwmod_irq_info omap54xx_mcasp_irqs[] = {
+       { .name = "arevt", .irq = 108 + OMAP54XX_IRQ_GIC_START },
+       { .name = "axevt", .irq = 109 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mcasp_sdma_reqs[] = {
+       { .name = "axevt", .dma_req = 7 + OMAP54XX_DMA_REQ_START },
+       { .name = "arevt", .dma_req = 10 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_mcasp_hwmod = {
+       .name           = "mcasp",
+       .class          = &omap54xx_mcasp_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE,
+       .mpu_irqs       = omap54xx_mcasp_irqs,
+       .sdma_reqs      = omap54xx_mcasp_sdma_reqs,
+       .main_clk       = "mcasp_gfclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_MCASP_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_MCASP_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'mcbsp' class
+ * multi channel buffered serial port controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_mcbsp_sysc = {
+       .sysc_offs      = 0x008c,
+       .sysc_flags     = (SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_ENAWAKEUP |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_mcbsp_hwmod_class = {
+       .name   = "mcbsp",
+       .sysc   = &omap54xx_mcbsp_sysc,
+       .rev    = MCBSP_CONFIG_TYPE4,
+};
+
+/* mcbsp1 */
+static struct omap_hwmod_irq_info omap54xx_mcbsp1_irqs[] = {
+       { .name = "common", .irq = 17 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mcbsp1_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 32 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 33 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod_opt_clk mcbsp1_opt_clks[] = {
+       { .role = "pad_fck", .clk = "pad_clks_ck" },
+       { .role = "prcm_fck", .clk = "mcbsp1_sync_mux_ck" },
+};
+
+static struct omap_hwmod omap54xx_mcbsp1_hwmod = {
+       .name           = "mcbsp1",
+       .class          = &omap54xx_mcbsp_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .mpu_irqs       = omap54xx_mcbsp1_irqs,
+       .sdma_reqs      = omap54xx_mcbsp1_sdma_reqs,
+       .main_clk       = "mcbsp1_gfclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_MCBSP1_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_MCBSP1_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .opt_clks       = mcbsp1_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(mcbsp1_opt_clks),
+};
+
+/* mcbsp2 */
+static struct omap_hwmod_irq_info omap54xx_mcbsp2_irqs[] = {
+       { .name = "common", .irq = 22 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mcbsp2_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 16 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 17 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod_opt_clk mcbsp2_opt_clks[] = {
+       { .role = "pad_fck", .clk = "pad_clks_ck" },
+       { .role = "prcm_fck", .clk = "mcbsp2_sync_mux_ck" },
+};
+
+static struct omap_hwmod omap54xx_mcbsp2_hwmod = {
+       .name           = "mcbsp2",
+       .class          = &omap54xx_mcbsp_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .mpu_irqs       = omap54xx_mcbsp2_irqs,
+       .sdma_reqs      = omap54xx_mcbsp2_sdma_reqs,
+       .main_clk       = "mcbsp2_gfclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_MCBSP2_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_MCBSP2_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .opt_clks       = mcbsp2_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(mcbsp2_opt_clks),
+};
+
+/* mcbsp3 */
+static struct omap_hwmod_irq_info omap54xx_mcbsp3_irqs[] = {
+       { .name = "common", .irq = 23 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mcbsp3_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 18 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 19 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod_opt_clk mcbsp3_opt_clks[] = {
+       { .role = "pad_fck", .clk = "pad_clks_ck" },
+       { .role = "prcm_fck", .clk = "mcbsp3_sync_mux_ck" },
+};
+
+static struct omap_hwmod omap54xx_mcbsp3_hwmod = {
+       .name           = "mcbsp3",
+       .class          = &omap54xx_mcbsp_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .mpu_irqs       = omap54xx_mcbsp3_irqs,
+       .sdma_reqs      = omap54xx_mcbsp3_sdma_reqs,
+       .main_clk       = "mcbsp3_gfclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_MCBSP3_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_MCBSP3_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .opt_clks       = mcbsp3_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(mcbsp3_opt_clks),
+};
+
+/*
+ * 'mcpdm' class
+ * multi channel pdm controller (proprietary interface with phoenix power
+ * ic)
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_mcpdm_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_EMUFREE | SYSC_HAS_RESET_STATUS |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_mcpdm_hwmod_class = {
+       .name   = "mcpdm",
+       .sysc   = &omap54xx_mcpdm_sysc,
+};
+
+/* mcpdm */
+static struct omap_hwmod_irq_info omap54xx_mcpdm_irqs[] = {
+       { .irq = 112 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mcpdm_sdma_reqs[] = {
+       { .name = "up_link", .dma_req = 64 + OMAP54XX_DMA_REQ_START },
+       { .name = "dn_link", .dma_req = 65 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_mcpdm_hwmod = {
+       .name           = "mcpdm",
+       .class          = &omap54xx_mcpdm_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       /*
+        * It's suspected that the McPDM requires an off-chip main
+        * functional clock, controlled via I2C.  This IP block is
+        * currently reset very early during boot, before I2C is
+        * available, so it doesn't seem that we have any choice in
+        * the kernel other than to avoid resetting it.  XXX This is
+        * really a hardware issue workaround: every IP block should
+        * be able to source its main functional clock from either
+        * on-chip or off-chip sources.  McPDM seems to be the only
+        * current exception.
+        */
+
+       .flags          = HWMOD_EXT_OPT_MAIN_CLK,
+       .mpu_irqs       = omap54xx_mcpdm_irqs,
+       .sdma_reqs      = omap54xx_mcpdm_sdma_reqs,
+       .main_clk       = "pad_clks_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_MCPDM_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_MCPDM_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'mcspi' class
+ * multichannel serial port interface (mcspi) / master/slave synchronous serial
+ * bus
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_mcspi_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_EMUFREE | SYSC_HAS_RESET_STATUS |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_mcspi_hwmod_class = {
+       .name   = "mcspi",
+       .sysc   = &omap54xx_mcspi_sysc,
+       .rev    = OMAP4_MCSPI_REV,
+};
+
+/* mcspi1 */
+static struct omap_hwmod_irq_info omap54xx_mcspi1_irqs[] = {
+       { .irq = 65 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mcspi1_sdma_reqs[] = {
+       { .name = "tx0", .dma_req = 34 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx0", .dma_req = 35 + OMAP54XX_DMA_REQ_START },
+       { .name = "tx1", .dma_req = 36 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx1", .dma_req = 37 + OMAP54XX_DMA_REQ_START },
+       { .name = "tx2", .dma_req = 38 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx2", .dma_req = 39 + OMAP54XX_DMA_REQ_START },
+       { .name = "tx3", .dma_req = 40 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx3", .dma_req = 41 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+/* mcspi1 dev_attr */
+static struct omap2_mcspi_dev_attr mcspi1_dev_attr = {
+       .num_chipselect = 4,
+};
+
+static struct omap_hwmod omap54xx_mcspi1_hwmod = {
+       .name           = "mcspi1",
+       .class          = &omap54xx_mcspi_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_mcspi1_irqs,
+       .sdma_reqs      = omap54xx_mcspi1_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_MCSPI1_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_MCSPI1_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &mcspi1_dev_attr,
+};
+
+/* mcspi2 */
+static struct omap_hwmod_irq_info omap54xx_mcspi2_irqs[] = {
+       { .irq = 66 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mcspi2_sdma_reqs[] = {
+       { .name = "tx0", .dma_req = 42 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx0", .dma_req = 43 + OMAP54XX_DMA_REQ_START },
+       { .name = "tx1", .dma_req = 44 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx1", .dma_req = 45 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+/* mcspi2 dev_attr */
+static struct omap2_mcspi_dev_attr mcspi2_dev_attr = {
+       .num_chipselect = 2,
+};
+
+static struct omap_hwmod omap54xx_mcspi2_hwmod = {
+       .name           = "mcspi2",
+       .class          = &omap54xx_mcspi_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_mcspi2_irqs,
+       .sdma_reqs      = omap54xx_mcspi2_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_MCSPI2_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_MCSPI2_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &mcspi2_dev_attr,
+};
+
+/* mcspi3 */
+static struct omap_hwmod_irq_info omap54xx_mcspi3_irqs[] = {
+       { .irq = 91 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mcspi3_sdma_reqs[] = {
+       { .name = "tx0", .dma_req = 14 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx0", .dma_req = 15 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+/* mcspi3 dev_attr */
+static struct omap2_mcspi_dev_attr mcspi3_dev_attr = {
+       .num_chipselect = 2,
+};
+
+static struct omap_hwmod omap54xx_mcspi3_hwmod = {
+       .name           = "mcspi3",
+       .class          = &omap54xx_mcspi_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_mcspi3_irqs,
+       .sdma_reqs      = omap54xx_mcspi3_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_MCSPI3_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_MCSPI3_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &mcspi3_dev_attr,
+};
+
+/* mcspi4 */
+static struct omap_hwmod_irq_info omap54xx_mcspi4_irqs[] = {
+       { .irq = 48 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mcspi4_sdma_reqs[] = {
+       { .name = "tx0", .dma_req = 69 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx0", .dma_req = 70 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+/* mcspi4 dev_attr */
+static struct omap2_mcspi_dev_attr mcspi4_dev_attr = {
+       .num_chipselect = 1,
+};
+
+static struct omap_hwmod omap54xx_mcspi4_hwmod = {
+       .name           = "mcspi4",
+       .class          = &omap54xx_mcspi_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_mcspi4_irqs,
+       .sdma_reqs      = omap54xx_mcspi4_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_MCSPI4_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_MCSPI4_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &mcspi4_dev_attr,
+};
+
+/*
+ * 'mmc' class
+ * multimedia card high-speed/sd/sdio (mmc/sd/sdio) host controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_mmc_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_EMUFREE | SYSC_HAS_MIDLEMODE |
+                          SYSC_HAS_RESET_STATUS | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO |
+                          MSTANDBY_SMART | MSTANDBY_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_mmc_hwmod_class = {
+       .name   = "mmc",
+       .sysc   = &omap54xx_mmc_sysc,
+};
+
+/* mmc1 */
+static struct omap_hwmod_irq_info omap54xx_mmc1_irqs[] = {
+       { .irq = 83 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mmc1_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 60 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 61 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod_opt_clk mmc1_opt_clks[] = {
+       { .role = "32khz_clk", .clk = "mmc1_32khz_clk" },
+};
+
+/* mmc1 dev_attr */
+static struct omap_mmc_dev_attr mmc1_dev_attr = {
+       .flags  = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
+};
+
+static struct omap_hwmod omap54xx_mmc1_hwmod = {
+       .name           = "mmc1",
+       .class          = &omap54xx_mmc_hwmod_class,
+       .clkdm_name     = "l3init_clkdm",
+       .mpu_irqs       = omap54xx_mmc1_irqs,
+       .sdma_reqs      = omap54xx_mmc1_sdma_reqs,
+       .main_clk       = "mmc1_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INIT_MMC1_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INIT_MMC1_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .opt_clks       = mmc1_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(mmc1_opt_clks),
+       .dev_attr       = &mmc1_dev_attr,
+};
+
+/* mmc2 */
+static struct omap_hwmod_irq_info omap54xx_mmc2_irqs[] = {
+       { .irq = 86 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mmc2_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 46 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 47 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_mmc2_hwmod = {
+       .name           = "mmc2",
+       .class          = &omap54xx_mmc_hwmod_class,
+       .clkdm_name     = "l3init_clkdm",
+       .mpu_irqs       = omap54xx_mmc2_irqs,
+       .sdma_reqs      = omap54xx_mmc2_sdma_reqs,
+       .main_clk       = "mmc2_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INIT_MMC2_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INIT_MMC2_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* mmc3 */
+static struct omap_hwmod_irq_info omap54xx_mmc3_irqs[] = {
+       { .irq = 94 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mmc3_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 76 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 77 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_mmc3_hwmod = {
+       .name           = "mmc3",
+       .class          = &omap54xx_mmc_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_mmc3_irqs,
+       .sdma_reqs      = omap54xx_mmc3_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_MMC3_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_MMC3_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* mmc4 */
+static struct omap_hwmod_irq_info omap54xx_mmc4_irqs[] = {
+       { .irq = 96 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mmc4_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 56 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 57 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_mmc4_hwmod = {
+       .name           = "mmc4",
+       .class          = &omap54xx_mmc_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_mmc4_irqs,
+       .sdma_reqs      = omap54xx_mmc4_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_MMC4_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_MMC4_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* mmc5 */
+static struct omap_hwmod_irq_info omap54xx_mmc5_irqs[] = {
+       { .irq = 59 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_mmc5_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 58 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 59 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_mmc5_hwmod = {
+       .name           = "mmc5",
+       .class          = &omap54xx_mmc_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_mmc5_irqs,
+       .sdma_reqs      = omap54xx_mmc5_sdma_reqs,
+       .main_clk       = "func_96m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_MMC5_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_MMC5_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'mpu' class
+ * mpu sub-system
+ */
+
+static struct omap_hwmod_class omap54xx_mpu_hwmod_class = {
+       .name   = "mpu",
+};
+
+/* mpu */
+static struct omap_hwmod_irq_info omap54xx_mpu_irqs[] = {
+       { .name = "mpu_cluster", .irq = 132 + OMAP54XX_IRQ_GIC_START },
+       { .name = "wd_timer_mpu_c0", .irq = 139 + OMAP54XX_IRQ_GIC_START },
+       { .name = "wd_timer_mpu_c1", .irq = 140 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_mpu_hwmod = {
+       .name           = "mpu",
+       .class          = &omap54xx_mpu_hwmod_class,
+       .clkdm_name     = "mpu_clkdm",
+       .flags          = HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET,
+       .mpu_irqs       = omap54xx_mpu_irqs,
+       .main_clk       = "dpll_mpu_m2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_MPU_MPU_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_MPU_MPU_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/*
+ * 'ocmc_ram' class
+ * top-level core on-chip ram
+ */
+
+static struct omap_hwmod_class omap54xx_ocmc_ram_hwmod_class = {
+       .name   = "ocmc_ram",
+};
+
+/* ocmc_ram */
+static struct omap_hwmod omap54xx_ocmc_ram_hwmod = {
+       .name           = "ocmc_ram",
+       .class          = &omap54xx_ocmc_ram_hwmod_class,
+       .clkdm_name     = "l3main2_clkdm",
+       .main_clk       = "l3_iclk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3MAIN2_OCMC_RAM_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3MAIN2_OCMC_RAM_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/*
+ * 'ocp2scp' class
+ * bridge to transform ocp interface protocol to scp (serial control port)
+ * protocol
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_ocp2scp_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_ocp2scp_hwmod_class = {
+       .name   = "ocp2scp",
+       .sysc   = &omap54xx_ocp2scp_sysc,
+};
+
+/* ocp2scp1 */
+static struct omap_hwmod omap54xx_ocp2scp1_hwmod = {
+       .name           = "ocp2scp1",
+       .class          = &omap54xx_ocp2scp_hwmod_class,
+       .clkdm_name     = "l3init_clkdm",
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INIT_OCP2SCP1_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INIT_OCP2SCP1_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+};
+
+static struct resource omap54xx_sata_phy_addrs[] = {
+       {
+               .name           = "sata_phy_rx",
+               .start          = 0x4A096000,
+               .end            = 0x4A096080,
+               .flags          = IORESOURCE_MEM,
+       },
+       {
+               .name           = "sata_phy_tx",
+               .start          = 0x4A096400,
+               .end            = 0x4A096464,
+               .flags          = IORESOURCE_MEM,
+       },
+       {
+               .name           = "sata_pll",
+               .start          = 0x4A096800,
+               .end            = 0x4A096840,
+               .flags          = IORESOURCE_MEM,
+       },
+       { }
+};
+
+static struct omap_ocp2scp_dev ocp2scp3_dev_attr[] = {
+       {
+               .drv_name       = "omap-sata",
+               .res            = omap54xx_sata_phy_addrs,
+       },
+       { }
+};
+
+/* ocp2scp3 */
+static struct omap_hwmod omap54xx_ocp2scp3_hwmod;
+static struct omap_hwmod_addr_space omap54xx_ocp2scp3_addrs[] = {
+       {
+               .name           = "ocp2scp3",
+               .pa_start       = 0x4a090000,
+               .pa_end         = 0x4a09001f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> ocp2scp3 */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__ocp2scp3 = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_ocp2scp3_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_ocp2scp3_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod omap54xx_ocp2scp3_hwmod = {
+       .name           = "ocp2scp3",
+       .class          = &omap54xx_ocp2scp_hwmod_class,
+       .clkdm_name     = "l3init_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INIT_OCP2SCP3_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INIT_OCP2SCP3_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+       .dev_attr       = ocp2scp3_dev_attr,
+};
+
+/*
+ * 'sata' class
+ * sata:  serial ata interface  gen2 compliant   ( 1 rx/ 1 tx)
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_sata_sysc = {
+       .sysc_offs      = 0x0000,
+       .sysc_flags     = (SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO |
+                          MSTANDBY_SMART | MSTANDBY_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_sata_hwmod_class = {
+       .name   = "sata",
+       .sysc   = &omap54xx_sata_sysc,
+};
+
+/* sata */
+static struct omap_hwmod_irq_info omap54xx_sata_irqs[] = {
+       { .irq = 54 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk sata_opt_clks[] = {
+       { .role = "ref_clk", .clk = "sata_ref_clk" },
+};
+
+static struct omap_hwmod omap54xx_sata_hwmod = {
+       .name           = "sata",
+       .class          = &omap54xx_sata_hwmod_class,
+       .clkdm_name     = "l3init_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY,
+       .mpu_irqs       = omap54xx_sata_irqs,
+       .main_clk       = "func_48m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INIT_SATA_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INIT_SATA_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .opt_clks       = sata_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(sata_opt_clks),
+};
+
+/*
+ * 'scrm' class
+ * system clock and reset manager
+ */
+
+static struct omap_hwmod_class omap54xx_scrm_hwmod_class = {
+       .name   = "scrm",
+};
+
+/* scrm */
+static struct omap_hwmod omap54xx_scrm_hwmod = {
+       .name           = "scrm",
+       .class          = &omap54xx_scrm_hwmod_class,
+       .clkdm_name     = "wkupaon_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_WKUPAON_SCRM_CLKCTRL_OFFSET,
+                       .flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT,
+               },
+       },
+};
+
+/*
+ * 'slimbus' class
+ * bidirectional, multi-drop, multi-channel two-line serial interface between
+ * the device and external components
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_slimbus_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_RESET_STATUS | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_slimbus_hwmod_class = {
+       .name   = "slimbus",
+       .sysc   = &omap54xx_slimbus_sysc,
+};
+
+/* slimbus1 */
+static struct omap_hwmod_irq_info omap54xx_slimbus1_irqs[] = {
+       { .irq = 97 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_slimbus1_sdma_reqs[] = {
+       { .name = "tx0", .dma_req = 84 + OMAP54XX_DMA_REQ_START },
+       { .name = "tx1", .dma_req = 85 + OMAP54XX_DMA_REQ_START },
+       { .name = "tx2", .dma_req = 86 + OMAP54XX_DMA_REQ_START },
+       { .name = "tx3", .dma_req = 87 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx0", .dma_req = 88 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx1", .dma_req = 89 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx2", .dma_req = 90 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx3", .dma_req = 91 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod_opt_clk slimbus1_opt_clks[] = {
+       { .role = "slimbus_clk", .clk = "slimbus1_slimbus_clk" },
+};
+
+static struct omap_hwmod omap54xx_slimbus1_hwmod = {
+       .name           = "slimbus1",
+       .class          = &omap54xx_slimbus_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .mpu_irqs       = omap54xx_slimbus1_irqs,
+       .sdma_reqs      = omap54xx_slimbus1_sdma_reqs,
+       .main_clk       = "abe_iclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_SLIMBUS1_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_SLIMBUS1_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .opt_clks       = slimbus1_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(slimbus1_opt_clks),
+};
+
+/*
+ * 'smartreflex' class
+ * smartreflex module (monitor silicon performance and outputs a measure of
+ * performance error)
+ */
+
+/* The IP is not compliant to type1 / type2 scheme */
+static struct omap_hwmod_sysc_fields omap_hwmod_sysc_type_smartreflex = {
+       .sidle_shift    = 24,
+       .enwkup_shift   = 26,
+};
+
+static struct omap_hwmod_class_sysconfig omap54xx_smartreflex_sysc = {
+       .sysc_offs      = 0x0038,
+       .sysc_flags     = (SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type_smartreflex,
+};
+
+static struct omap_hwmod_class omap54xx_smartreflex_hwmod_class = {
+       .name   = "smartreflex",
+       .sysc   = &omap54xx_smartreflex_sysc,
+       .rev    = 2,
+};
+
+/* smartreflex_core */
+static struct omap_hwmod_irq_info omap54xx_smartreflex_core_irqs[] = {
+       { .irq = 19 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+/* smartreflex_core dev_attr */
+static struct omap_smartreflex_dev_attr smartreflex_core_dev_attr = {
+       .sensor_voltdm_name     = "core",
+};
+
+static struct omap_hwmod omap54xx_smartreflex_core_hwmod = {
+       .name           = "smartreflex_core",
+       .class          = &omap54xx_smartreflex_hwmod_class,
+       .clkdm_name     = "coreaon_clkdm",
+       .mpu_irqs       = omap54xx_smartreflex_core_irqs,
+       .main_clk       = "wkupaon_iclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_COREAON_SMARTREFLEX_CORE_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_COREAON_SMARTREFLEX_CORE_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &smartreflex_core_dev_attr,
+};
+
+/* smartreflex_mm */
+static struct omap_hwmod_irq_info omap54xx_smartreflex_mm_irqs[] = {
+       { .irq = 102 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+/* smartreflex_mm dev_attr */
+static struct omap_smartreflex_dev_attr smartreflex_mm_dev_attr = {
+       .sensor_voltdm_name     = "mm",
+};
+
+static struct omap_hwmod omap54xx_smartreflex_mm_hwmod = {
+       .name           = "smartreflex_mm",
+       .class          = &omap54xx_smartreflex_hwmod_class,
+       .clkdm_name     = "coreaon_clkdm",
+       .mpu_irqs       = omap54xx_smartreflex_mm_irqs,
+       .main_clk       = "wkupaon_iclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_COREAON_SMARTREFLEX_MM_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_COREAON_SMARTREFLEX_MM_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &smartreflex_mm_dev_attr,
+};
+
+/* smartreflex_mpu */
+static struct omap_hwmod_irq_info omap54xx_smartreflex_mpu_irqs[] = {
+       { .irq = 18 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+/* smartreflex_mpu dev_attr */
+static struct omap_smartreflex_dev_attr smartreflex_mpu_dev_attr = {
+       .sensor_voltdm_name     = "mpu",
+};
+
+static struct omap_hwmod omap54xx_smartreflex_mpu_hwmod = {
+       .name           = "smartreflex_mpu",
+       .class          = &omap54xx_smartreflex_hwmod_class,
+       .clkdm_name     = "coreaon_clkdm",
+       .mpu_irqs       = omap54xx_smartreflex_mpu_irqs,
+       .main_clk       = "wkupaon_iclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_COREAON_SMARTREFLEX_MPU_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_COREAON_SMARTREFLEX_MPU_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .dev_attr       = &smartreflex_mpu_dev_attr,
+};
+
+/*
+ * 'spinlock' class
+ * spinlock provides hardware assistance for synchronizing the processes
+ * running on multiple processors
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_spinlock_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+                          SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_spinlock_hwmod_class = {
+       .name   = "spinlock",
+       .sysc   = &omap54xx_spinlock_sysc,
+};
+
+/* spinlock */
+static struct omap_hwmod omap54xx_spinlock_hwmod = {
+       .name           = "spinlock",
+       .class          = &omap54xx_spinlock_hwmod_class,
+       .clkdm_name     = "l4cfg_clkdm",
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4CFG_SPINLOCK_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4CFG_SPINLOCK_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/*
+ * 'timer' class
+ * general purpose timer module with accurate 1ms tick
+ * This class contains several variants: ['timer_1ms', 'timer']
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_timer_1ms_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_EMUFREE | SYSC_HAS_RESET_STATUS |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+       .clockact       = CLOCKACT_TEST_ICLK,
+};
+
+static struct omap_hwmod_class omap54xx_timer_1ms_hwmod_class = {
+       .name   = "timer",
+       .sysc   = &omap54xx_timer_1ms_sysc,
+};
+
+static struct omap_hwmod_class_sysconfig omap54xx_timer_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_EMUFREE | SYSC_HAS_RESET_STATUS |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_timer_hwmod_class = {
+       .name   = "timer",
+       .sysc   = &omap54xx_timer_sysc,
+};
+
+/* timer1 */
+static struct omap_hwmod_irq_info omap54xx_timer1_irqs[] = {
+       { .irq = 37 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_timer1_hwmod = {
+       .name           = "timer1",
+       .class          = &omap54xx_timer_1ms_hwmod_class,
+       .clkdm_name     = "wkupaon_clkdm",
+       .mpu_irqs       = omap54xx_timer1_irqs,
+       .flags          = HWMOD_SET_DEFAULT_CLOCKACT,
+       .main_clk       = "timer1_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_WKUPAON_TIMER1_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_WKUPAON_TIMER1_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* timer2 */
+static struct omap_hwmod_irq_info omap54xx_timer2_irqs[] = {
+       { .irq = 38 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_timer2_hwmod = {
+       .name           = "timer2",
+       .class          = &omap54xx_timer_1ms_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_timer2_irqs,
+       .flags          = HWMOD_SET_DEFAULT_CLOCKACT,
+       .main_clk       = "timer2_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_TIMER2_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_TIMER2_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* timer3 */
+static struct omap_hwmod_irq_info omap54xx_timer3_irqs[] = {
+       { .irq = 39 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_timer3_hwmod = {
+       .name           = "timer3",
+       .class          = &omap54xx_timer_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_timer3_irqs,
+       .main_clk       = "timer3_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_TIMER3_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_TIMER3_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* timer4 */
+static struct omap_hwmod_irq_info omap54xx_timer4_irqs[] = {
+       { .irq = 40 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_timer4_hwmod = {
+       .name           = "timer4",
+       .class          = &omap54xx_timer_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_timer4_irqs,
+       .main_clk       = "timer4_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_TIMER4_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_TIMER4_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* timer5 */
+static struct omap_hwmod_irq_info omap54xx_timer5_irqs[] = {
+       { .irq = 41 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_timer5_hwmod = {
+       .name           = "timer5",
+       .class          = &omap54xx_timer_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .mpu_irqs       = omap54xx_timer5_irqs,
+       .main_clk       = "timer5_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_TIMER5_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_TIMER5_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* timer6 */
+static struct omap_hwmod_irq_info omap54xx_timer6_irqs[] = {
+       { .irq = 42 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_timer6_hwmod = {
+       .name           = "timer6",
+       .class          = &omap54xx_timer_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .mpu_irqs       = omap54xx_timer6_irqs,
+       .main_clk       = "timer6_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_TIMER6_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_TIMER6_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* timer7 */
+static struct omap_hwmod_irq_info omap54xx_timer7_irqs[] = {
+       { .irq = 43 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_timer7_hwmod = {
+       .name           = "timer7",
+       .class          = &omap54xx_timer_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .mpu_irqs       = omap54xx_timer7_irqs,
+       .main_clk       = "timer7_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_TIMER7_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_TIMER7_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* timer8 */
+static struct omap_hwmod_irq_info omap54xx_timer8_irqs[] = {
+       { .irq = 44 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_timer8_hwmod = {
+       .name           = "timer8",
+       .class          = &omap54xx_timer_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .mpu_irqs       = omap54xx_timer8_irqs,
+       .main_clk       = "timer8_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_TIMER8_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_TIMER8_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* timer9 */
+static struct omap_hwmod_irq_info omap54xx_timer9_irqs[] = {
+       { .irq = 45 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_timer9_hwmod = {
+       .name           = "timer9",
+       .class          = &omap54xx_timer_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_timer9_irqs,
+       .main_clk       = "timer9_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_TIMER9_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_TIMER9_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* timer10 */
+static struct omap_hwmod_irq_info omap54xx_timer10_irqs[] = {
+       { .irq = 46 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_timer10_hwmod = {
+       .name           = "timer10",
+       .class          = &omap54xx_timer_1ms_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_timer10_irqs,
+       .flags          = HWMOD_SET_DEFAULT_CLOCKACT,
+       .main_clk       = "timer10_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_TIMER10_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_TIMER10_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* timer11 */
+static struct omap_hwmod_irq_info omap54xx_timer11_irqs[] = {
+       { .irq = 47 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_timer11_hwmod = {
+       .name           = "timer11",
+       .class          = &omap54xx_timer_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_timer11_irqs,
+       .main_clk       = "timer11_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_TIMER11_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_TIMER11_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'uart' class
+ * universal asynchronous receiver/transmitter (uart)
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_uart_sysc = {
+       .rev_offs       = 0x0050,
+       .sysc_offs      = 0x0054,
+       .syss_offs      = 0x0058,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_ENAWAKEUP |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET |
+                          SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_uart_hwmod_class = {
+       .name   = "uart",
+       .sysc   = &omap54xx_uart_sysc,
+};
+
+/* uart1 */
+static struct omap_hwmod_irq_info omap54xx_uart1_irqs[] = {
+       { .irq = 72 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_uart1_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 48 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 49 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_uart1_hwmod = {
+       .name           = "uart1",
+       .class          = &omap54xx_uart_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_uart1_irqs,
+       .sdma_reqs      = omap54xx_uart1_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_UART1_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_UART1_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* uart2 */
+static struct omap_hwmod_irq_info omap54xx_uart2_irqs[] = {
+       { .irq = 73 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_uart2_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 50 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 51 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_uart2_hwmod = {
+       .name           = "uart2",
+       .class          = &omap54xx_uart_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_uart2_irqs,
+       .sdma_reqs      = omap54xx_uart2_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_UART2_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_UART2_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* uart3 */
+static struct omap_hwmod_irq_info omap54xx_uart3_irqs[] = {
+       { .irq = 74 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_uart3_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 52 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 53 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_uart3_hwmod = {
+       .name           = "uart3",
+       .class          = &omap54xx_uart_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .flags          = HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET |
+                               HWMOD_SWSUP_SIDLE_ACT,
+       .mpu_irqs       = omap54xx_uart3_irqs,
+       .sdma_reqs      = omap54xx_uart3_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_UART3_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_UART3_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* uart4 */
+static struct omap_hwmod_irq_info omap54xx_uart4_irqs[] = {
+       { .irq = 70 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_uart4_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 54 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 55 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_uart4_hwmod = {
+       .name           = "uart4",
+       .class          = &omap54xx_uart_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_uart4_irqs,
+       .sdma_reqs      = omap54xx_uart4_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_UART4_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_UART4_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* uart5 */
+static struct omap_hwmod_irq_info omap54xx_uart5_irqs[] = {
+       { .irq = 105 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_uart5_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 62 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 63 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_uart5_hwmod = {
+       .name           = "uart5",
+       .class          = &omap54xx_uart_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_uart5_irqs,
+       .sdma_reqs      = omap54xx_uart5_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_UART5_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_UART5_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* uart6 */
+static struct omap_hwmod_irq_info omap54xx_uart6_irqs[] = {
+       { .irq = 106 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_dma_info omap54xx_uart6_sdma_reqs[] = {
+       { .name = "tx", .dma_req = 78 + OMAP54XX_DMA_REQ_START },
+       { .name = "rx", .dma_req = 79 + OMAP54XX_DMA_REQ_START },
+       { .dma_req = -1 }
+};
+
+static struct omap_hwmod omap54xx_uart6_hwmod = {
+       .name           = "uart6",
+       .class          = &omap54xx_uart_hwmod_class,
+       .clkdm_name     = "l4per_clkdm",
+       .mpu_irqs       = omap54xx_uart6_irqs,
+       .sdma_reqs      = omap54xx_uart6_sdma_reqs,
+       .main_clk       = "func_48m_fclk",
+       .flags          = HWMOD_SWSUP_SIDLE_ACT,
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L4PER_UART6_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L4PER_UART6_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/*
+ * 'usb_host_hs' class
+ * high-speed multi-port usb host controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_usb_host_hs_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_MIDLEMODE | SYSC_HAS_RESET_STATUS |
+                          SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO |
+                          MSTANDBY_SMART | MSTANDBY_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_usb_host_hs_hwmod_class = {
+       .name   = "usb_host_hs",
+       .sysc   = &omap54xx_usb_host_hs_sysc,
+};
+
+/* usb_host_hs */
+static struct omap_hwmod_irq_info omap54xx_usb_host_hs_irqs[] = {
+       { .name = "ohci-irq", .irq = 76 + OMAP54XX_IRQ_GIC_START },
+       { .name = "ehci-irq", .irq = 77 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk usb_host_hs_opt_clks[] = {
+       { .role = "hsic60m_p2_clk", .clk = "usb_host_hs_hsic60m_p2_clk" },
+       { .role = "hsic60m_p3_clk", .clk = "usb_host_hs_hsic60m_p3_clk" },
+       { .role = "utmi_p1_clk", .clk = "usb_host_hs_utmi_p1_clk" },
+       { .role = "utmi_p2_clk", .clk = "usb_host_hs_utmi_p2_clk" },
+       { .role = "utmi_p3_clk", .clk = "usb_host_hs_utmi_p3_clk" },
+       { .role = "hsic480m_p1_clk", .clk = "usb_host_hs_hsic480m_p1_clk" },
+       { .role = "hsic60m_p1_clk", .clk = "usb_host_hs_hsic60m_p1_clk" },
+       { .role = "hsic480m_p3_clk", .clk = "usb_host_hs_hsic480m_p3_clk" },
+       { .role = "hsic480m_p2_clk", .clk = "usb_host_hs_hsic480m_p2_clk" },
+};
+
+static struct omap_hwmod omap54xx_usb_host_hs_hwmod = {
+       .name           = "usb_host_hs",
+       .class          = &omap54xx_usb_host_hs_hwmod_class,
+       .clkdm_name     = "l3init_clkdm",
+       /*
+        * Errata: USBHOST Configured In Smart-Idle Can Lead To a Deadlock
+        * id: i660
+        *
+        * Description:
+        * In the following configuration :
+        * - USBHOST module is set to smart-idle mode
+        * - PRCM asserts idle_req to the USBHOST module ( This typically
+        *   happens when the system is going to a low power mode : all ports
+        *   have been suspended, the master part of the USBHOST module has
+        *   entered the standby state, and SW has cut the functional clocks)
+        * - an USBHOST interrupt occurs before the module is able to answer
+        *   idle_ack, typically a remote wakeup IRQ.
+        * Then the USB HOST module will enter a deadlock situation where it
+        * is no more accessible nor functional.
+        *
+        * Workaround:
+        * Don't use smart idle; use only force idle, hence HWMOD_SWSUP_SIDLE
+        */
+
+       /*
+        * Errata: USB host EHCI may stall when entering smart-standby mode
+        * Id: i571
+        *
+        * Description:
+        * When the USBHOST module is set to smart-standby mode, and when it is
+        * ready to enter the standby state (i.e. all ports are suspended and
+        * all attached devices are in suspend mode), then it can wrongly assert
+        * the Mstandby signal too early while there are still some residual OCP
+        * transactions ongoing. If this condition occurs, the internal state
+        * machine may go to an undefined state and the USB link may be stuck
+        * upon the next resume.
+        *
+        * Workaround:
+        * Don't use smart standby; use only force standby,
+        * hence HWMOD_SWSUP_MSTANDBY
+        */
+
+       /*
+        * During system boot; If the hwmod framework resets the module
+        * the module will have smart idle settings; which can lead to deadlock
+        * (above Errata Id:i660); so, dont reset the module during boot;
+        * Use HWMOD_INIT_NO_RESET.
+        */
+
+       .flags          = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY |
+                         HWMOD_INIT_NO_RESET,
+       .mpu_irqs       = omap54xx_usb_host_hs_irqs,
+       .main_clk       = "l3init_60m_fclk",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INIT_USB_HOST_HS_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INIT_USB_HOST_HS_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+       .opt_clks       = usb_host_hs_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(usb_host_hs_opt_clks),
+};
+
+/*
+ * 'usb_otg_ss' class
+ * 2.0 super speed (usb_otg_ss) controller
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_usb_otg_ss_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .sysc_flags     = (SYSC_HAS_DMADISABLE | SYSC_HAS_MIDLEMODE |
+                          SYSC_HAS_SIDLEMODE),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO |
+                          MSTANDBY_SMART | MSTANDBY_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap54xx_usb_otg_ss_hwmod_class = {
+       .name   = "usb_otg_ss",
+       .sysc   = &omap54xx_usb_otg_ss_sysc,
+};
+
+/* usb_otg_ss */
+static struct omap_hwmod_irq_info omap54xx_usb_otg_ss_irqs[] = {
+       { .name = "core", .irq = 92 + OMAP54XX_IRQ_GIC_START },
+       { .name = "wrp", .irq = 93 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk usb_otg_ss_opt_clks[] = {
+       { .role = "refclk960m", .clk = "usb_otg_ss_refclk960m" },
+};
+
+static struct omap_hwmod omap54xx_usb_otg_ss_hwmod = {
+       .name           = "usb_otg_ss",
+       .class          = &omap54xx_usb_otg_ss_hwmod_class,
+       .clkdm_name     = "l3init_clkdm",
+       .flags          = HWMOD_SWSUP_SIDLE,
+       .mpu_irqs       = omap54xx_usb_otg_ss_irqs,
+       .main_clk       = "dpll_core_h13x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INIT_USB_OTG_SS_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INIT_USB_OTG_SS_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+       .opt_clks       = usb_otg_ss_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(usb_otg_ss_opt_clks),
+};
+
+/*
+ * 'usb_tll_hs' class
+ * usb_tll_hs module is the adapter on the usb_host_hs ports
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_usb_tll_hs_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+                          SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_usb_tll_hs_hwmod_class = {
+       .name   = "usb_tll_hs",
+       .sysc   = &omap54xx_usb_tll_hs_sysc,
+};
+
+/* usb_tll_hs */
+static struct omap_hwmod_irq_info omap54xx_usb_tll_hs_irqs[] = {
+       { .irq = 78 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod_opt_clk usb_tll_hs_opt_clks[] = {
+       { .role = "usb_ch2_clk", .clk = "usb_tll_hs_usb_ch2_clk" },
+       { .role = "usb_ch0_clk", .clk = "usb_tll_hs_usb_ch0_clk" },
+       { .role = "usb_ch1_clk", .clk = "usb_tll_hs_usb_ch1_clk" },
+};
+
+static struct omap_hwmod omap54xx_usb_tll_hs_hwmod = {
+       .name           = "usb_tll_hs",
+       .class          = &omap54xx_usb_tll_hs_hwmod_class,
+       .clkdm_name     = "l3init_clkdm",
+       .mpu_irqs       = omap54xx_usb_tll_hs_irqs,
+       .main_clk       = "l4_root_clk_div",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_L3INIT_USB_TLL_HS_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_L3INIT_USB_TLL_HS_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_HWCTRL,
+               },
+       },
+       .opt_clks       = usb_tll_hs_opt_clks,
+       .opt_clks_cnt   = ARRAY_SIZE(usb_tll_hs_opt_clks),
+};
+
+/*
+ * 'wd_timer' class
+ * 32-bit watchdog upward counter that generates a pulse on the reset pin on
+ * overflow condition
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_wd_timer_sysc = {
+       .rev_offs       = 0x0000,
+       .sysc_offs      = 0x0010,
+       .syss_offs      = 0x0014,
+       .sysc_flags     = (SYSC_HAS_EMUFREE | SYSC_HAS_SIDLEMODE |
+                          SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+       .idlemodes      = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+                          SIDLE_SMART_WKUP),
+       .sysc_fields    = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_wd_timer_hwmod_class = {
+       .name           = "wd_timer",
+       .sysc           = &omap54xx_wd_timer_sysc,
+       .pre_shutdown   = &omap2_wd_timer_disable,
+};
+
+/* wd_timer2 */
+static struct omap_hwmod_irq_info omap54xx_wd_timer2_irqs[] = {
+       { .irq = 80 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_wd_timer2_hwmod = {
+       .name           = "wd_timer2",
+       .class          = &omap54xx_wd_timer_hwmod_class,
+       .clkdm_name     = "wkupaon_clkdm",
+       .mpu_irqs       = omap54xx_wd_timer2_irqs,
+       .main_clk       = "sys_32k_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_WKUPAON_WD_TIMER2_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_WKUPAON_WD_TIMER2_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* wd_timer3 */
+static struct omap_hwmod_irq_info omap54xx_wd_timer3_irqs[] = {
+       { .irq = 36 + OMAP54XX_IRQ_GIC_START },
+       { .irq = -1 }
+};
+
+static struct omap_hwmod omap54xx_wd_timer3_hwmod = {
+       .name           = "wd_timer3",
+       .class          = &omap54xx_wd_timer_hwmod_class,
+       .clkdm_name     = "abe_clkdm",
+       .mpu_irqs       = omap54xx_wd_timer3_irqs,
+       .main_clk       = "sys_32k_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_ABE_WD_TIMER3_CLKCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_ABE_WD_TIMER3_CONTEXT_OFFSET,
+                       .modulemode   = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+
+/*
+ * Interfaces
+ */
+
+static struct omap_hwmod_addr_space omap54xx_dmm_addrs[] = {
+       {
+               .pa_start       = 0x4e000000,
+               .pa_end         = 0x4e0007ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_1 -> dmm */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_1__dmm = {
+       .master         = &omap54xx_l3_main_1_hwmod,
+       .slave          = &omap54xx_dmm_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_dmm_addrs,
+       .user           = OCP_USER_SDMA,
+};
+
+/* dmm -> emif_ocp_fw */
+static struct omap_hwmod_ocp_if omap54xx_dmm__emif_ocp_fw = {
+       .master         = &omap54xx_dmm_hwmod,
+       .slave          = &omap54xx_emif_ocp_fw_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_emif_ocp_fw_addrs[] = {
+       {
+               .pa_start       = 0x4a20c000,
+               .pa_end         = 0x4a20c0ff,
+       },
+       { }
+};
+
+/* l4_cfg -> emif_ocp_fw */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__emif_ocp_fw = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_emif_ocp_fw_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_emif_ocp_fw_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+/* l3_main_3 -> l3_instr */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_3__l3_instr = {
+       .master         = &omap54xx_l3_main_3_hwmod,
+       .slave          = &omap54xx_l3_instr_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* ocp_wp_noc -> l3_instr */
+static struct omap_hwmod_ocp_if omap54xx_ocp_wp_noc__l3_instr = {
+       .master         = &omap54xx_ocp_wp_noc_hwmod,
+       .slave          = &omap54xx_l3_instr_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l3_main_2 -> l3_main_1 */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__l3_main_1 = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_l3_main_1_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l4_cfg -> l3_main_1 */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__l3_main_1 = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_l3_main_1_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_l3_main_1_addrs[] = {
+       {
+               .pa_start       = 0x44000000,
+               .pa_end         = 0x44001fff,
+       },
+       { }
+};
+
+/* mpu -> l3_main_1 */
+static struct omap_hwmod_ocp_if omap54xx_mpu__l3_main_1 = {
+       .master         = &omap54xx_mpu_hwmod,
+       .slave          = &omap54xx_l3_main_1_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_l3_main_1_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_l3_main_2_addrs[] = {
+       {
+               .pa_start       = 0x44800000,
+               .pa_end         = 0x44802fff,
+       },
+       { }
+};
+
+/* l3_main_1 -> l3_main_2 */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_1__l3_main_2 = {
+       .master         = &omap54xx_l3_main_1_hwmod,
+       .slave          = &omap54xx_l3_main_2_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_l3_main_2_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+/* l4_cfg -> l3_main_2 */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__l3_main_2 = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_l3_main_2_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_l3_main_3_addrs[] = {
+       {
+               .pa_start       = 0x45000000,
+               .pa_end         = 0x45003fff,
+       },
+       { }
+};
+
+/* l3_main_1 -> l3_main_3 */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_1__l3_main_3 = {
+       .master         = &omap54xx_l3_main_1_hwmod,
+       .slave          = &omap54xx_l3_main_3_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_l3_main_3_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+/* l3_main_2 -> l3_main_3 */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__l3_main_3 = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_l3_main_3_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l4_cfg -> l3_main_3 */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__l3_main_3 = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_l3_main_3_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l3_main_1 -> l4_abe */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_1__l4_abe = {
+       .master         = &omap54xx_l3_main_1_hwmod,
+       .slave          = &omap54xx_l4_abe_hwmod,
+       .clk            = "abe_iclk",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* mpu -> l4_abe */
+static struct omap_hwmod_ocp_if omap54xx_mpu__l4_abe = {
+       .master         = &omap54xx_mpu_hwmod,
+       .slave          = &omap54xx_l4_abe_hwmod,
+       .clk            = "abe_iclk",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l3_main_1 -> l4_cfg */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_1__l4_cfg = {
+       .master         = &omap54xx_l3_main_1_hwmod,
+       .slave          = &omap54xx_l4_cfg_hwmod,
+       .clk            = "l4_root_clk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l3_main_2 -> l4_per */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__l4_per = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_l4_per_hwmod,
+       .clk            = "l4_root_clk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l3_main_1 -> l4_wkup */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_1__l4_wkup = {
+       .master         = &omap54xx_l3_main_1_hwmod,
+       .slave          = &omap54xx_l4_wkup_hwmod,
+       .clk            = "wkupaon_iclk_mux",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* mpu -> mpu_private */
+static struct omap_hwmod_ocp_if omap54xx_mpu__mpu_private = {
+       .master         = &omap54xx_mpu_hwmod,
+       .slave          = &omap54xx_mpu_private_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l3_main_3 -> ocp_wp_noc */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_3__ocp_wp_noc = {
+       .master         = &omap54xx_l3_main_3_hwmod,
+       .slave          = &omap54xx_ocp_wp_noc_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_ocp_wp_noc_addrs[] = {
+       {
+               .pa_start       = 0x4a102000,
+               .pa_end         = 0x4a10207f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> ocp_wp_noc */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__ocp_wp_noc = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_ocp_wp_noc_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_ocp_wp_noc_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_aess_addrs[] = {
+       {
+               .name           = "aess",
+               .pa_start       = 0x401f1000,
+               .pa_end         = 0x401f13ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> aess */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__aess = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_aess_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_aess_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+/* l3_main_2 -> bb2d */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__bb2d = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_bb2d_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_c2c_addrs[] = {
+       {
+               .pa_start       = 0x5c000000,
+               .pa_end         = 0x5c0000ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> c2c */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__c2c = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_c2c_hwmod,
+       .clk            = "c2c_fclk",
+       .addr           = omap54xx_c2c_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_counter_32k_addrs[] = {
+       {
+               .pa_start       = 0x4ae04000,
+               .pa_end         = 0x4ae0403f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_wkup -> counter_32k */
+static struct omap_hwmod_ocp_if omap54xx_l4_wkup__counter_32k = {
+       .master         = &omap54xx_l4_wkup_hwmod,
+       .slave          = &omap54xx_counter_32k_hwmod,
+       .clk            = "wkupaon_iclk_mux",
+       .addr           = omap54xx_counter_32k_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_ctrl_module_core_addrs[] = {
+       {
+               .name           = "omap_control_core_core",
+               .pa_start       = 0x4a002000,
+               .pa_end         = 0x4a0027ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       {
+               .name           = "omap_control_core_pad",
+               .pa_start       = 0x4a002800,
+               .pa_end         = 0x4a002fff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> ctrl_module_core */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__ctrl_module_core = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_ctrl_module_core_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_ctrl_module_core_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_ctrl_module_wkup_addrs[] = {
+       {
+               .name           = "omap_control_wkup_core",
+               .pa_start       = 0x4ae0c000,
+               .pa_end         = 0x4ae0c7ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       {
+               .name           = "omap_control_wkup_pad",
+               .pa_start       = 0x4ae0c800,
+               .pa_end         = 0x4ae0cfff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_wkup -> ctrl_module_wkup */
+static struct omap_hwmod_ocp_if omap54xx_l4_wkup__ctrl_module_wkup = {
+       .master         = &omap54xx_l4_wkup_hwmod,
+       .slave          = &omap54xx_ctrl_module_wkup_hwmod,
+       .clk            = "wkupaon_iclk_mux",
+       .addr           = omap54xx_ctrl_module_wkup_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_dma_system_addrs[] = {
+       {
+               .pa_start       = 0x4a056000,
+               .pa_end         = 0x4a056fff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> dma_system */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__dma_system = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_dma_system_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_dma_system_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_dmic_addrs[] = {
+       {
+               .name           = "mpu",
+               .pa_start       = 0x4012e000,
+               .pa_end         = 0x4012e07f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> dmic */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__dmic = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_dmic_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_dmic_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+/* l4_cfg -> dsp */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__dsp = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_dsp_hwmod,
+       .clk            = "l4_root_clk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_dss_addrs[] = {
+       {
+               .pa_start       = 0x58000000,
+               .pa_end         = 0x5800007f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> dss */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__dss = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_dss_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_dss_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_dss_dispc_addrs[] = {
+       {
+               .pa_start       = 0x58001000,
+               .pa_end         = 0x58001fff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> dss_dispc */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__dss_dispc = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_dss_dispc_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_dss_dispc_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_dss_dsi1_a_addrs[] = {
+       {
+               .pa_start       = 0x58004000,
+               .pa_end         = 0x580041ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> dss_dsi1_a */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__dss_dsi1_a = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_dss_dsi1_a_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_dss_dsi1_a_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_dss_dsi1_b_addrs[] = {
+       {
+               .pa_start       = 0x58005000,
+               .pa_end         = 0x580051ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> dss_dsi1_b */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__dss_dsi1_b = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_dss_dsi1_b_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_dss_dsi1_b_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_dss_dsi1_c_addrs[] = {
+       {
+               .pa_start       = 0x58009000,
+               .pa_end         = 0x580091ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> dss_dsi1_c */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__dss_dsi1_c = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_dss_dsi1_c_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_dss_dsi1_c_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_dss_hdmi_addrs[] = {
+       {
+               .name           = "hdmi_wp",
+               .pa_start       = 0x58040000,
+               .pa_end         = 0x580400ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       {
+               .name           = "pllctrl",
+               .pa_start       = 0x58040200,
+               .pa_end         = 0x5804023f,
+       },
+       {
+               .name           = "hdmitxphy",
+               .pa_start       = 0x58040300,
+               .pa_end         = 0x5804033f,
+       },
+       {
+               .name           = "hdmi_core",
+               .pa_start       = 0x58060000,
+               .pa_end         = 0x58078fff,
+       },
+       { }
+};
+
+/* l3_main_2 -> dss_hdmi */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__dss_hdmi = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_dss_hdmi_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_dss_hdmi_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_dss_rfbi_addrs[] = {
+       {
+               .pa_start       = 0x58002000,
+               .pa_end         = 0x580020ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> dss_rfbi */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__dss_rfbi = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_dss_rfbi_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_dss_rfbi_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_elm_addrs[] = {
+       {
+               .pa_start       = 0x48078000,
+               .pa_end         = 0x48078fff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> elm */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__elm = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_elm_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_elm_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* emif_ocp_fw -> emif1 */
+static struct omap_hwmod_ocp_if omap54xx_emif_ocp_fw__emif1 = {
+       .master         = &omap54xx_emif_ocp_fw_hwmod,
+       .slave          = &omap54xx_emif1_hwmod,
+       .clk            = "dpll_core_h11x2_ck",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_emif1_addrs[] = {
+       {
+               .pa_start       = 0x4c000000,
+               .pa_end         = 0x4c0003ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* mpu -> emif1 */
+static struct omap_hwmod_ocp_if omap54xx_mpu__emif1 = {
+       .master         = &omap54xx_mpu_hwmod,
+       .slave          = &omap54xx_emif1_hwmod,
+       .clk            = "dpll_core_h11x2_ck",
+       .addr           = omap54xx_emif1_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+/* emif_ocp_fw -> emif2 */
+static struct omap_hwmod_ocp_if omap54xx_emif_ocp_fw__emif2 = {
+       .master         = &omap54xx_emif_ocp_fw_hwmod,
+       .slave          = &omap54xx_emif2_hwmod,
+       .clk            = "dpll_core_h11x2_ck",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_emif2_addrs[] = {
+       {
+               .pa_start       = 0x4d000000,
+               .pa_end         = 0x4d0003ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* mpu -> emif2 */
+static struct omap_hwmod_ocp_if omap54xx_mpu__emif2 = {
+       .master         = &omap54xx_mpu_hwmod,
+       .slave          = &omap54xx_emif2_hwmod,
+       .clk            = "dpll_core_h11x2_ck",
+       .addr           = omap54xx_emif2_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_fdif_addrs[] = {
+       {
+               .pa_start       = 0x4a10a000,
+               .pa_end         = 0x4a10a3ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> fdif */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__fdif = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_fdif_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_fdif_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_gpio1_addrs[] = {
+       {
+               .pa_start       = 0x4ae10000,
+               .pa_end         = 0x4ae101ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_wkup -> gpio1 */
+static struct omap_hwmod_ocp_if omap54xx_l4_wkup__gpio1 = {
+       .master         = &omap54xx_l4_wkup_hwmod,
+       .slave          = &omap54xx_gpio1_hwmod,
+       .clk            = "wkupaon_iclk_mux",
+       .addr           = omap54xx_gpio1_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_gpio2_addrs[] = {
+       {
+               .pa_start       = 0x48055000,
+               .pa_end         = 0x480551ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> gpio2 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__gpio2 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_gpio2_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_gpio2_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_gpio3_addrs[] = {
+       {
+               .pa_start       = 0x48057000,
+               .pa_end         = 0x480571ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> gpio3 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__gpio3 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_gpio3_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_gpio3_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_gpio4_addrs[] = {
+       {
+               .pa_start       = 0x48059000,
+               .pa_end         = 0x480591ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> gpio4 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__gpio4 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_gpio4_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_gpio4_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_gpio5_addrs[] = {
+       {
+               .pa_start       = 0x4805b000,
+               .pa_end         = 0x4805b1ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> gpio5 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__gpio5 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_gpio5_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_gpio5_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_gpio6_addrs[] = {
+       {
+               .pa_start       = 0x4805d000,
+               .pa_end         = 0x4805d1ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> gpio6 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__gpio6 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_gpio6_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_gpio6_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_gpio7_addrs[] = {
+       {
+               .pa_start       = 0x48051000,
+               .pa_end         = 0x480511ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> gpio7 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__gpio7 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_gpio7_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_gpio7_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_gpio8_addrs[] = {
+       {
+               .pa_start       = 0x48053000,
+               .pa_end         = 0x480531ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> gpio8 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__gpio8 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_gpio8_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_gpio8_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_gpmc_addrs[] = {
+       {
+               .pa_start       = 0x50000000,
+               .pa_end         = 0x500003ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> gpmc */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__gpmc = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_gpmc_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_gpmc_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_gpu_addrs[] = {
+       {
+               .name           = "klio",
+               .pa_start       = 0x56000000,
+               .pa_end         = 0x56001fff,
+       },
+       {
+               .name           = "hydra2",
+               .pa_start       = 0x56004000,
+               .pa_end         = 0x56004fff,
+       },
+       {
+               .name           = "klio_0",
+               .pa_start       = 0x56008000,
+               .pa_end         = 0x56009fff,
+       },
+       {
+               .name           = "klio_1",
+               .pa_start       = 0x5600c000,
+               .pa_end         = 0x5600dfff,
+       },
+       {
+               .name           = "klio_hl",
+               .pa_start       = 0x5600fe00,
+               .pa_end         = 0x5600ffff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> gpu */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__gpu = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_gpu_hwmod,
+       .clk            = "gpu_l3_iclk",
+       .addr           = omap54xx_gpu_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_hdq1w_addrs[] = {
+       {
+               .pa_start       = 0x480b2000,
+               .pa_end         = 0x480b201f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> hdq1w */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__hdq1w = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_hdq1w_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_hdq1w_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_hsi_addrs[] = {
+       {
+               .name           = "system_32",
+               .pa_start       = 0x4a058000,
+               .pa_end         = 0x4a059fff,
+               .flags          = ADDR_TYPE_RT
+       },
+       {
+               .name           = "dte_channels",
+               .pa_start       = 0x4a059800,
+               .pa_end         = 0x4a059bff,
+       },
+       {
+               .name           = "hsi_32",
+               .pa_start       = 0x4a05a000,
+               .pa_end         = 0x4a05bfff,
+       },
+       { }
+};
+
+/* l4_cfg -> hsi */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__hsi = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_hsi_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_hsi_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_i2c1_addrs[] = {
+       {
+               .pa_start       = 0x48070000,
+               .pa_end         = 0x480700ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> i2c1 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__i2c1 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_i2c1_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_i2c1_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_i2c2_addrs[] = {
+       {
+               .pa_start       = 0x48072000,
+               .pa_end         = 0x480720ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> i2c2 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__i2c2 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_i2c2_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_i2c2_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_i2c3_addrs[] = {
+       {
+               .pa_start       = 0x48060000,
+               .pa_end         = 0x480600ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> i2c3 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__i2c3 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_i2c3_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_i2c3_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_i2c4_addrs[] = {
+       {
+               .pa_start       = 0x4807a000,
+               .pa_end         = 0x4807a0ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> i2c4 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__i2c4 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_i2c4_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_i2c4_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_i2c5_addrs[] = {
+       {
+               .pa_start       = 0x4807c000,
+               .pa_end         = 0x4807c0ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> i2c5 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__i2c5 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_i2c5_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_i2c5_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_ipu_addrs[] = {
+       {
+               .name           = "unicache_mmu",
+               .pa_start       = 0x55080800,
+               .pa_end         = 0x55080fff,
+       },
+       {
+               .name           = "teslass_mmu",
+               .pa_start       = 0x55082000,
+               .pa_end         = 0x550820ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> ipu */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__ipu = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_ipu_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_ipu_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_intc_ipu_c0_addrs[] = {
+       {
+               .pa_start       = 0x48211000,
+               .pa_end         = 0x48211fff,
+       },
+       { }
+};
+
+/* l3_main_2 -> intc_ipu_c0 */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__intc_ipu_c0 = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_intc_ipu_c0_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_intc_ipu_c0_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_intc_ipu_c1_addrs[] = {
+       {
+               .pa_start       = 0x48211000,
+               .pa_end         = 0x48211fff,
+       },
+       { }
+};
+
+/* l3_main_2 -> intc_ipu_c1 */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__intc_ipu_c1 = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_intc_ipu_c1_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_intc_ipu_c1_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_iss_addrs[] = {
+       {
+               .pa_start       = 0x52000000,
+               .pa_end         = 0x520000ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> iss */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__iss = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_iss_hwmod,
+       .clk            = "dpll_core_h23x2_ck",
+       .addr           = omap54xx_iss_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_iva_addrs[] = {
+       {
+               .pa_start       = 0x5a05a400,
+               .pa_end         = 0x5a05a47f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l3_main_2 -> iva */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__iva = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_iva_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_iva_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_kbd_addrs[] = {
+       {
+               .pa_start       = 0x4ae1c000,
+               .pa_end         = 0x4ae1c07f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_wkup -> kbd */
+static struct omap_hwmod_ocp_if omap54xx_l4_wkup__kbd = {
+       .master         = &omap54xx_l4_wkup_hwmod,
+       .slave          = &omap54xx_kbd_hwmod,
+       .clk            = "wkupaon_iclk_mux",
+       .addr           = omap54xx_kbd_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mailbox_addrs[] = {
+       {
+               .pa_start       = 0x4a0f4000,
+               .pa_end         = 0x4a0f41ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> mailbox */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__mailbox = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_mailbox_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_mailbox_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mcasp_addrs[] = {
+       {
+               .name           = "cfg",
+               .pa_start       = 0x40128000,
+               .pa_end         = 0x401283ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       {
+               .name           = "dat",
+               .pa_start       = 0x4012a000,
+               .pa_end         = 0x4012a3ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> mcasp */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__mcasp = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_mcasp_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_mcasp_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mcbsp1_addrs[] = {
+       {
+               .name           = "mpu",
+               .pa_start       = 0x40122000,
+               .pa_end         = 0x401220ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> mcbsp1 */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__mcbsp1 = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_mcbsp1_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_mcbsp1_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mcbsp2_addrs[] = {
+       {
+               .name           = "mpu",
+               .pa_start       = 0x40124000,
+               .pa_end         = 0x401240ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> mcbsp2 */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__mcbsp2 = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_mcbsp2_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_mcbsp2_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mcbsp3_addrs[] = {
+       {
+               .name           = "mpu",
+               .pa_start       = 0x40126000,
+               .pa_end         = 0x401260ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> mcbsp3 */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__mcbsp3 = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_mcbsp3_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_mcbsp3_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mcpdm_addrs[] = {
+       {
+               .name           = "mpu",
+               .pa_start       = 0x40132000,
+               .pa_end         = 0x4013207f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> mcpdm */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__mcpdm = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_mcpdm_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_mcpdm_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mcspi1_addrs[] = {
+       {
+               .pa_start       = 0x48098000,
+               .pa_end         = 0x480981ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> mcspi1 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__mcspi1 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_mcspi1_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_mcspi1_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mcspi2_addrs[] = {
+       {
+               .pa_start       = 0x4809a000,
+               .pa_end         = 0x4809a1ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> mcspi2 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__mcspi2 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_mcspi2_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_mcspi2_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mcspi3_addrs[] = {
+       {
+               .pa_start       = 0x480b8000,
+               .pa_end         = 0x480b81ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> mcspi3 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__mcspi3 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_mcspi3_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_mcspi3_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mcspi4_addrs[] = {
+       {
+               .pa_start       = 0x480ba000,
+               .pa_end         = 0x480ba1ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> mcspi4 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__mcspi4 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_mcspi4_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_mcspi4_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mmc1_addrs[] = {
+       {
+               .pa_start       = 0x4809c000,
+               .pa_end         = 0x4809c3ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> mmc1 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__mmc1 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_mmc1_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_mmc1_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mmc2_addrs[] = {
+       {
+               .pa_start       = 0x480b4000,
+               .pa_end         = 0x480b43ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> mmc2 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__mmc2 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_mmc2_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_mmc2_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mmc3_addrs[] = {
+       {
+               .pa_start       = 0x480ad000,
+               .pa_end         = 0x480ad3ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> mmc3 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__mmc3 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_mmc3_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_mmc3_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mmc4_addrs[] = {
+       {
+               .pa_start       = 0x480d1000,
+               .pa_end         = 0x480d13ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> mmc4 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__mmc4 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_mmc4_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_mmc4_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mmc5_addrs[] = {
+       {
+               .pa_start       = 0x480d5000,
+               .pa_end         = 0x480d53ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> mmc5 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__mmc5 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_mmc5_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_mmc5_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_mpu_addrs[] = {
+       {
+               .pa_start       = 0x48211000,
+               .pa_end         = 0x482af27f,
+       },
+       { }
+};
+
+/* l4_cfg -> mpu */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__mpu = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_mpu_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_mpu_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l3_main_2 -> ocmc_ram */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__ocmc_ram = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_ocmc_ram_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_ocp2scp1_addrs[] = {
+       {
+               .pa_start       = 0x4a080000,
+               .pa_end         = 0x4a08001f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> ocp2scp1 */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__ocp2scp1 = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_ocp2scp1_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_ocp2scp1_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_sata_addrs[] = {
+       {
+               .name           = "ahci",
+               .pa_start       = 0x4a140000,
+               .pa_end         = 0x4a1401ff,
+       },
+       {
+               .name           = "sysc",
+               .pa_start       = 0x4a141100,
+               .pa_end         = 0x4a141107,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> sata */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__sata = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_sata_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_sata_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_scrm_addrs[] = {
+       {
+               .pa_start       = 0x4ae0a000,
+               .pa_end         = 0x4ae0a7ff,
+       },
+       { }
+};
+
+/* l4_wkup -> scrm */
+static struct omap_hwmod_ocp_if omap54xx_l4_wkup__scrm = {
+       .master         = &omap54xx_l4_wkup_hwmod,
+       .slave          = &omap54xx_scrm_hwmod,
+       .clk            = "wkupaon_iclk_mux",
+       .addr           = omap54xx_scrm_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_slimbus1_addrs[] = {
+       {
+               .name           = "mpu",
+               .pa_start       = 0x4012c000,
+               .pa_end         = 0x4012c3ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> slimbus1 */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__slimbus1 = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_slimbus1_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_slimbus1_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_smartreflex_core_addrs[] = {
+       {
+               .pa_start       = 0x4a0dd000,
+               .pa_end         = 0x4a0dd03f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> smartreflex_core */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__smartreflex_core = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_smartreflex_core_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_smartreflex_core_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_smartreflex_mm_addrs[] = {
+       {
+               .pa_start       = 0x4a0db000,
+               .pa_end         = 0x4a0db03f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> smartreflex_mm */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__smartreflex_mm = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_smartreflex_mm_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_smartreflex_mm_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_smartreflex_mpu_addrs[] = {
+       {
+               .pa_start       = 0x4a0d9000,
+               .pa_end         = 0x4a0d903f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> smartreflex_mpu */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__smartreflex_mpu = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_smartreflex_mpu_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_smartreflex_mpu_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_spinlock_addrs[] = {
+       {
+               .pa_start       = 0x4a0f6000,
+               .pa_end         = 0x4a0f6fff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> spinlock */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__spinlock = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_spinlock_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_spinlock_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_timer1_addrs[] = {
+       {
+               .pa_start       = 0x4ae18000,
+               .pa_end         = 0x4ae1807f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_wkup -> timer1 */
+static struct omap_hwmod_ocp_if omap54xx_l4_wkup__timer1 = {
+       .master         = &omap54xx_l4_wkup_hwmod,
+       .slave          = &omap54xx_timer1_hwmod,
+       .clk            = "wkupaon_iclk_mux",
+       .addr           = omap54xx_timer1_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_timer2_addrs[] = {
+       {
+               .pa_start       = 0x48032000,
+               .pa_end         = 0x4803207f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> timer2 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__timer2 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_timer2_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_timer2_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_timer3_addrs[] = {
+       {
+               .pa_start       = 0x48034000,
+               .pa_end         = 0x4803407f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> timer3 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__timer3 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_timer3_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_timer3_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_timer4_addrs[] = {
+       {
+               .pa_start       = 0x48036000,
+               .pa_end         = 0x4803607f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> timer4 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__timer4 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_timer4_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_timer4_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_timer5_addrs[] = {
+       {
+               .name           = "mpu",
+               .pa_start       = 0x40138000,
+               .pa_end         = 0x4013807f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> timer5 */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__timer5 = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_timer5_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_timer5_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_timer6_addrs[] = {
+       {
+               .name           = "mpu",
+               .pa_start       = 0x4013a000,
+               .pa_end         = 0x4013a07f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> timer6 */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__timer6 = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_timer6_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_timer6_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_timer7_addrs[] = {
+       {
+               .name           = "mpu",
+               .pa_start       = 0x4013c000,
+               .pa_end         = 0x4013c07f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> timer7 */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__timer7 = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_timer7_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_timer7_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_timer8_addrs[] = {
+       {
+               .name           = "mpu",
+               .pa_start       = 0x4013e000,
+               .pa_end         = 0x4013e07f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> timer8 */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__timer8 = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_timer8_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_timer8_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_addr_space omap54xx_timer9_addrs[] = {
+       {
+               .pa_start       = 0x4803e000,
+               .pa_end         = 0x4803e07f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> timer9 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__timer9 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_timer9_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_timer9_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_timer10_addrs[] = {
+       {
+               .pa_start       = 0x48086000,
+               .pa_end         = 0x4808607f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> timer10 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__timer10 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_timer10_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_timer10_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_timer11_addrs[] = {
+       {
+               .pa_start       = 0x48088000,
+               .pa_end         = 0x4808807f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> timer11 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__timer11 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_timer11_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_timer11_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_uart1_addrs[] = {
+       {
+               .pa_start       = 0x4806a000,
+               .pa_end         = 0x4806a0ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> uart1 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__uart1 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_uart1_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_uart1_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_uart2_addrs[] = {
+       {
+               .pa_start       = 0x4806c000,
+               .pa_end         = 0x4806c0ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> uart2 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__uart2 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_uart2_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_uart2_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_uart3_addrs[] = {
+       {
+               .pa_start       = 0x48020000,
+               .pa_end         = 0x480200ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> uart3 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__uart3 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_uart3_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_uart3_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_uart4_addrs[] = {
+       {
+               .pa_start       = 0x4806e000,
+               .pa_end         = 0x4806e0ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> uart4 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__uart4 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_uart4_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_uart4_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_uart5_addrs[] = {
+       {
+               .pa_start       = 0x48066000,
+               .pa_end         = 0x480660ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> uart5 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__uart5 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_uart5_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_uart5_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_uart6_addrs[] = {
+       {
+               .pa_start       = 0x48068000,
+               .pa_end         = 0x480680ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_per -> uart6 */
+static struct omap_hwmod_ocp_if omap54xx_l4_per__uart6 = {
+       .master         = &omap54xx_l4_per_hwmod,
+       .slave          = &omap54xx_uart6_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_uart6_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_usb_host_hs_addrs[] = {
+       {
+               .name           = "uhh",
+               .pa_start       = 0x4a064000,
+               .pa_end         = 0x4a0647ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       {
+               .name           = "ohci",
+               .pa_start       = 0x4a064800,
+               .pa_end         = 0x4a06487f,
+       },
+       {
+               .name           = "ehci",
+               .pa_start       = 0x4a064c00,
+               .pa_end         = 0x4a064cff,
+       },
+       { }
+};
+
+/* l4_cfg -> usb_host_hs */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__usb_host_hs = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_usb_host_hs_hwmod,
+       .clk            = "l3_iclk_div",
+       .addr           = omap54xx_usb_host_hs_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_usb_otg_ss_addrs[] = {
+       {
+               .name           = "wrapper",
+               .pa_start       = 0x4a020000,
+               .pa_end         = 0x4a0201ff,
+               .flags          = ADDR_TYPE_RT
+       },
+       {
+               .name           = "dwc_usb3",
+               .pa_start       = 0x4a030000,
+               .pa_end         = 0x4a0300ff,
+       },
+       { }
+};
+
+/* l4_cfg -> usb_otg_ss */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__usb_otg_ss = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_usb_otg_ss_hwmod,
+       .clk            = "dpll_core_h13x2_ck",
+       .addr           = omap54xx_usb_otg_ss_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_usb_tll_hs_addrs[] = {
+       {
+               .name           = "tll",
+               .pa_start       = 0x4a062000,
+               .pa_end         = 0x4a062fff,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_cfg -> usb_tll_hs */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__usb_tll_hs = {
+       .master         = &omap54xx_l4_cfg_hwmod,
+       .slave          = &omap54xx_usb_tll_hs_hwmod,
+       .clk            = "l4_root_clk_div",
+       .addr           = omap54xx_usb_tll_hs_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_wd_timer2_addrs[] = {
+       {
+               .pa_start       = 0x4ae14000,
+               .pa_end         = 0x4ae1407f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_wkup -> wd_timer2 */
+static struct omap_hwmod_ocp_if omap54xx_l4_wkup__wd_timer2 = {
+       .master         = &omap54xx_l4_wkup_hwmod,
+       .slave          = &omap54xx_wd_timer2_hwmod,
+       .clk            = "wkupaon_iclk_mux",
+       .addr           = omap54xx_wd_timer2_addrs,
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+static struct omap_hwmod_addr_space omap54xx_wd_timer3_addrs[] = {
+       {
+               .name           = "mpu",
+               .pa_start       = 0x40130000,
+               .pa_end         = 0x4013007f,
+               .flags          = ADDR_TYPE_RT
+       },
+       { }
+};
+
+/* l4_abe -> wd_timer3 */
+static struct omap_hwmod_ocp_if omap54xx_l4_abe__wd_timer3 = {
+       .master         = &omap54xx_l4_abe_hwmod,
+       .slave          = &omap54xx_wd_timer3_hwmod,
+       .clk            = "abe_iclk",
+       .addr           = omap54xx_wd_timer3_addrs,
+       .user           = OCP_USER_MPU,
+};
+
+static struct omap_hwmod_ocp_if *omap54xx_hwmod_ocp_ifs[] __initdata = {
+       &omap54xx_l3_main_1__dmm,
+       &omap54xx_dmm__emif_ocp_fw,
+       &omap54xx_l4_cfg__emif_ocp_fw,
+       &omap54xx_l3_main_3__l3_instr,
+       &omap54xx_ocp_wp_noc__l3_instr,
+       &omap54xx_l3_main_2__l3_main_1,
+       &omap54xx_l4_cfg__l3_main_1,
+       &omap54xx_mpu__l3_main_1,
+       &omap54xx_l3_main_1__l3_main_2,
+       &omap54xx_l4_cfg__l3_main_2,
+       &omap54xx_l3_main_1__l3_main_3,
+       &omap54xx_l3_main_2__l3_main_3,
+       &omap54xx_l4_cfg__l3_main_3,
+       &omap54xx_l3_main_1__l4_abe,
+       &omap54xx_mpu__l4_abe,
+       &omap54xx_l3_main_1__l4_cfg,
+       &omap54xx_l3_main_2__l4_per,
+       &omap54xx_l3_main_1__l4_wkup,
+       &omap54xx_mpu__mpu_private,
+       &omap54xx_l3_main_3__ocp_wp_noc,
+       &omap54xx_l4_cfg__ocp_wp_noc,
+       &omap54xx_l4_abe__aess,
+       &omap54xx_l3_main_2__bb2d,
+       &omap54xx_l3_main_2__c2c,
+       &omap54xx_l4_wkup__counter_32k,
+       &omap54xx_l4_cfg__ctrl_module_core,
+       &omap54xx_l4_wkup__ctrl_module_wkup,
+       &omap54xx_l4_cfg__dma_system,
+       &omap54xx_l4_abe__dmic,
+       &omap54xx_l4_cfg__dsp,
+       &omap54xx_l3_main_2__dss,
+       &omap54xx_l3_main_2__dss_dispc,
+       &omap54xx_l3_main_2__dss_dsi1_a,
+       &omap54xx_l3_main_2__dss_dsi1_b,
+       &omap54xx_l3_main_2__dss_dsi1_c,
+       &omap54xx_l3_main_2__dss_hdmi,
+       &omap54xx_l3_main_2__dss_rfbi,
+       &omap54xx_l4_per__elm,
+       &omap54xx_emif_ocp_fw__emif1,
+       &omap54xx_mpu__emif1,
+       &omap54xx_emif_ocp_fw__emif2,
+       &omap54xx_mpu__emif2,
+       &omap54xx_l4_cfg__fdif,
+       &omap54xx_l4_wkup__gpio1,
+       &omap54xx_l4_per__gpio2,
+       &omap54xx_l4_per__gpio3,
+       &omap54xx_l4_per__gpio4,
+       &omap54xx_l4_per__gpio5,
+       &omap54xx_l4_per__gpio6,
+       &omap54xx_l4_per__gpio7,
+       &omap54xx_l4_per__gpio8,
+       &omap54xx_l3_main_2__gpmc,
+       &omap54xx_l3_main_2__gpu,
+       &omap54xx_l4_per__hdq1w,
+       &omap54xx_l4_cfg__hsi,
+       &omap54xx_l4_per__i2c1,
+       &omap54xx_l4_per__i2c2,
+       &omap54xx_l4_per__i2c3,
+       &omap54xx_l4_per__i2c4,
+       &omap54xx_l4_per__i2c5,
+       &omap54xx_l3_main_2__ipu,
+       &omap54xx_l3_main_2__intc_ipu_c0,
+       &omap54xx_l3_main_2__intc_ipu_c1,
+       &omap54xx_l3_main_2__iss,
+       &omap54xx_l3_main_2__iva,
+       &omap54xx_l4_wkup__kbd,
+       &omap54xx_l4_cfg__mailbox,
+       &omap54xx_l4_abe__mcasp,
+       &omap54xx_l4_abe__mcbsp1,
+       &omap54xx_l4_abe__mcbsp2,
+       &omap54xx_l4_abe__mcbsp3,
+       &omap54xx_l4_abe__mcpdm,
+       &omap54xx_l4_per__mcspi1,
+       &omap54xx_l4_per__mcspi2,
+       &omap54xx_l4_per__mcspi3,
+       &omap54xx_l4_per__mcspi4,
+       &omap54xx_l4_per__mmc1,
+       &omap54xx_l4_per__mmc2,
+       &omap54xx_l4_per__mmc3,
+       &omap54xx_l4_per__mmc4,
+       &omap54xx_l4_per__mmc5,
+       &omap54xx_l4_cfg__mpu,
+       &omap54xx_l3_main_2__ocmc_ram,
+       &omap54xx_l4_cfg__ocp2scp1,
+       &omap54xx_l4_cfg__ocp2scp3,
+       &omap54xx_l4_cfg__sata,
+       &omap54xx_l4_wkup__scrm,
+       &omap54xx_l4_abe__slimbus1,
+       &omap54xx_l4_cfg__smartreflex_core,
+       &omap54xx_l4_cfg__smartreflex_mm,
+       &omap54xx_l4_cfg__smartreflex_mpu,
+       &omap54xx_l4_cfg__spinlock,
+       &omap54xx_l4_wkup__timer1,
+       &omap54xx_l4_per__timer2,
+       &omap54xx_l4_per__timer3,
+       &omap54xx_l4_per__timer4,
+       &omap54xx_l4_abe__timer5,
+       &omap54xx_l4_abe__timer6,
+       &omap54xx_l4_abe__timer7,
+       &omap54xx_l4_abe__timer8,
+       &omap54xx_l4_per__timer9,
+       &omap54xx_l4_per__timer10,
+       &omap54xx_l4_per__timer11,
+       &omap54xx_l4_per__uart1,
+       &omap54xx_l4_per__uart2,
+       &omap54xx_l4_per__uart3,
+       &omap54xx_l4_per__uart4,
+       &omap54xx_l4_per__uart5,
+       &omap54xx_l4_per__uart6,
+       &omap54xx_l4_cfg__usb_host_hs,
+       &omap54xx_l4_cfg__usb_otg_ss,
+       &omap54xx_l4_cfg__usb_tll_hs,
+       &omap54xx_l4_wkup__wd_timer2,
+       &omap54xx_l4_abe__wd_timer3,
+       NULL,
+};
+
+int __init omap54xx_hwmod_init(void)
+{
+       omap_hwmod_init();
+       return omap_hwmod_register_links(omap54xx_hwmod_ocp_ifs);
+}
+
index cfcce299177c537750d7143919e294ff6aec2532..6e04ff7065e11b0b84a520d40f50afd9a1ab3547 100644 (file)
@@ -78,6 +78,8 @@ extern struct omap_hwmod omap2xxx_mcspi2_hwmod;
 extern struct omap_hwmod omap2xxx_counter_32k_hwmod;
 extern struct omap_hwmod omap2xxx_gpmc_hwmod;
 extern struct omap_hwmod omap2xxx_rng_hwmod;
+extern struct omap_hwmod omap2xxx_sham_hwmod;
+extern struct omap_hwmod omap2xxx_aes_hwmod;
 
 /* Common interface data across OMAP2xxx */
 extern struct omap_hwmod_ocp_if omap2xxx_l3_main__l4_core;
@@ -105,6 +107,8 @@ extern struct omap_hwmod_ocp_if omap2xxx_l4_core__dss_dispc;
 extern struct omap_hwmod_ocp_if omap2xxx_l4_core__dss_rfbi;
 extern struct omap_hwmod_ocp_if omap2xxx_l4_core__dss_venc;
 extern struct omap_hwmod_ocp_if omap2xxx_l4_core__rng;
+extern struct omap_hwmod_ocp_if omap2xxx_l4_core__sham;
+extern struct omap_hwmod_ocp_if omap2xxx_l4_core__aes;
 
 /* Common IP block data */
 extern struct omap_hwmod_dma_info omap2_uart1_sdma_reqs[];
diff --git a/arch/arm/mach-omap2/omap_hwmod_reset.c b/arch/arm/mach-omap2/omap_hwmod_reset.c
new file mode 100644 (file)
index 0000000..65e186c
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * OMAP IP block custom reset and preprogramming stubs
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ * Paul Walmsley
+ *
+ * A small number of IP blocks need custom reset and preprogramming
+ * functions.  The stubs in this file provide a standard way for the
+ * hwmod code to call these functions, which are to be located under
+ * drivers/.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include <sound/aess.h>
+
+#include "omap_hwmod.h"
+
+/**
+ * omap_hwmod_aess_preprogram - enable AESS internal autogating
+ * @oh: struct omap_hwmod *
+ *
+ * The AESS will not IdleAck to the PRCM until its internal autogating
+ * is enabled.  Since internal autogating is disabled by default after
+ * AESS reset, we must enable autogating after the hwmod code resets
+ * the AESS.  Returns 0.
+ */
+int omap_hwmod_aess_preprogram(struct omap_hwmod *oh)
+{
+       void __iomem *va;
+
+       va = omap_hwmod_get_mpu_rt_va(oh);
+       if (!va)
+               return -EINVAL;
+
+       aess_enable_autogating(va);
+
+       return 0;
+}
index d470b728e72085b7bcfad5d24fdd9c0a0919cc97..cfd93afc8f20ba3eee5536614d6a3fab80873616 100644 (file)
@@ -83,7 +83,11 @@ static struct omap_opp_def __initdata omap443x_opp_def_list[] = {
        OPP_INITIALIZER("iva", true, 266100000, OMAP4430_VDD_IVA_OPP100_UV),
        /* IVA OPP3 - OPP-Turbo */
        OPP_INITIALIZER("iva", false, 332000000, OMAP4430_VDD_IVA_OPPTURBO_UV),
-       /* TODO: add DSP, aess, fdif, gpu */
+       /* ABE OPP1 - OPP50 */
+       OPP_INITIALIZER("aess", true, 98304000, OMAP4430_VDD_IVA_OPP50_UV),
+       /* ABE OPP2 - OPP100 */
+       OPP_INITIALIZER("aess", true, 196608000, OMAP4430_VDD_IVA_OPP100_UV),
+       /* TODO: add DSP, fdif, gpu */
 };
 
 #define OMAP4460_VDD_MPU_OPP50_UV              1025000
index e2c291f52f925b360253add1dc7f4921233bec8b..7f7b1d8d006a1409a1f3358104548c05d9d45745 100644 (file)
@@ -52,98 +52,6 @@ enum {
        DEBUG_FILE_TIMERS,
 };
 
-static const char pwrdm_state_names[][PWRDM_MAX_PWRSTS] = {
-       "OFF",
-       "RET",
-       "INA",
-       "ON"
-};
-
-void pm_dbg_update_time(struct powerdomain *pwrdm, int prev)
-{
-       s64 t;
-
-       if (!pm_dbg_init_done)
-               return ;
-
-       /* Update timer for previous state */
-       t = sched_clock();
-
-       pwrdm->state_timer[prev] += t - pwrdm->timer;
-
-       pwrdm->timer = t;
-}
-
-static int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user)
-{
-       struct seq_file *s = (struct seq_file *)user;
-
-       if (strcmp(clkdm->name, "emu_clkdm") == 0 ||
-               strcmp(clkdm->name, "wkup_clkdm") == 0 ||
-               strncmp(clkdm->name, "dpll", 4) == 0)
-               return 0;
-
-       seq_printf(s, "%s->%s (%d)", clkdm->name,
-                       clkdm->pwrdm.ptr->name,
-                       atomic_read(&clkdm->usecount));
-       seq_printf(s, "\n");
-
-       return 0;
-}
-
-static int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user)
-{
-       struct seq_file *s = (struct seq_file *)user;
-       int i;
-
-       if (strcmp(pwrdm->name, "emu_pwrdm") == 0 ||
-               strcmp(pwrdm->name, "wkup_pwrdm") == 0 ||
-               strncmp(pwrdm->name, "dpll", 4) == 0)
-               return 0;
-
-       if (pwrdm->state != pwrdm_read_pwrst(pwrdm))
-               printk(KERN_ERR "pwrdm state mismatch(%s) %d != %d\n",
-                       pwrdm->name, pwrdm->state, pwrdm_read_pwrst(pwrdm));
-
-       seq_printf(s, "%s (%s)", pwrdm->name,
-                       pwrdm_state_names[pwrdm->state]);
-       for (i = 0; i < PWRDM_MAX_PWRSTS; i++)
-               seq_printf(s, ",%s:%d", pwrdm_state_names[i],
-                       pwrdm->state_counter[i]);
-
-       seq_printf(s, ",RET-LOGIC-OFF:%d", pwrdm->ret_logic_off_counter);
-       for (i = 0; i < pwrdm->banks; i++)
-               seq_printf(s, ",RET-MEMBANK%d-OFF:%d", i + 1,
-                               pwrdm->ret_mem_off_counter[i]);
-
-       seq_printf(s, "\n");
-
-       return 0;
-}
-
-static int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *user)
-{
-       struct seq_file *s = (struct seq_file *)user;
-       int i;
-
-       if (strcmp(pwrdm->name, "emu_pwrdm") == 0 ||
-               strcmp(pwrdm->name, "wkup_pwrdm") == 0 ||
-               strncmp(pwrdm->name, "dpll", 4) == 0)
-               return 0;
-
-       pwrdm_state_switch(pwrdm);
-
-       seq_printf(s, "%s (%s)", pwrdm->name,
-               pwrdm_state_names[pwrdm->state]);
-
-       for (i = 0; i < 4; i++)
-               seq_printf(s, ",%s:%lld", pwrdm_state_names[i],
-                       pwrdm->state_timer[i]);
-
-       seq_printf(s, "\n");
-       return 0;
-}
-
 static int pm_dbg_show_counters(struct seq_file *s, void *unused)
 {
        pwrdm_for_each(pwrdm_dbg_show_counter, s);
@@ -172,10 +80,10 @@ static int pm_dbg_open(struct inode *inode, struct file *file)
 }
 
 static const struct file_operations debug_fops = {
-       .open           = pm_dbg_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
+       .open           = pm_dbg_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
 };
 
 static int pwrdm_suspend_get(void *data, u64 *val)
@@ -204,17 +112,8 @@ DEFINE_SIMPLE_ATTRIBUTE(pwrdm_suspend_fops, pwrdm_suspend_get,
 
 static int __init pwrdms_setup(struct powerdomain *pwrdm, void *dir)
 {
-       int i;
-       s64 t;
        struct dentry *d;
 
-       t = sched_clock();
-
-       for (i = 0; i < 4; i++)
-               pwrdm->state_timer[i] = 0;
-
-       pwrdm->timer = t;
-
        if (strncmp(pwrdm->name, "dpll", 4) == 0)
                return 0;
 
index f4b3143a8b1d23f104419181c0d266b07e31ad78..37fd00ab1cb0c9fd435ea0da7d7b483aac28410e 100644 (file)
@@ -108,79 +108,18 @@ static void __init omap2_init_processor_devices(void)
        }
 }
 
-/* Types of sleep_switch used in omap_set_pwrdm_state */
-#define FORCEWAKEUP_SWITCH     0
-#define LOWPOWERSTATE_SWITCH   1
-
 int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused)
 {
+       /* XXX The usecount test is racy */
        if ((clkdm->flags & CLKDM_CAN_ENABLE_AUTO) &&
            !(clkdm->flags & CLKDM_MISSING_IDLE_REPORTING))
                clkdm_allow_idle(clkdm);
        else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP &&
-                atomic_read(&clkdm->usecount) == 0)
+                clkdm->usecount == 0)
                clkdm_sleep(clkdm);
        return 0;
 }
 
-/*
- * This sets pwrdm state (other than mpu & core. Currently only ON &
- * RET are supported.
- */
-int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 pwrst)
-{
-       u8 curr_pwrst, next_pwrst;
-       int sleep_switch = -1, ret = 0, hwsup = 0;
-
-       if (!pwrdm || IS_ERR(pwrdm))
-               return -EINVAL;
-
-       while (!(pwrdm->pwrsts & (1 << pwrst))) {
-               if (pwrst == PWRDM_POWER_OFF)
-                       return ret;
-               pwrst--;
-       }
-
-       next_pwrst = pwrdm_read_next_pwrst(pwrdm);
-       if (next_pwrst == pwrst)
-               return ret;
-
-       curr_pwrst = pwrdm_read_pwrst(pwrdm);
-       if (curr_pwrst < PWRDM_POWER_ON) {
-               if ((curr_pwrst > pwrst) &&
-                       (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) {
-                       sleep_switch = LOWPOWERSTATE_SWITCH;
-               } else {
-                       hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
-                       clkdm_wakeup(pwrdm->pwrdm_clkdms[0]);
-                       sleep_switch = FORCEWAKEUP_SWITCH;
-               }
-       }
-
-       ret = pwrdm_set_next_pwrst(pwrdm, pwrst);
-       if (ret)
-               pr_err("%s: unable to set power state of powerdomain: %s\n",
-                      __func__, pwrdm->name);
-
-       switch (sleep_switch) {
-       case FORCEWAKEUP_SWITCH:
-               if (hwsup)
-                       clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]);
-               else
-                       clkdm_sleep(pwrdm->pwrdm_clkdms[0]);
-               break;
-       case LOWPOWERSTATE_SWITCH:
-               pwrdm_set_lowpwrstchange(pwrdm);
-               pwrdm_wait_transition(pwrdm);
-               pwrdm_state_switch(pwrdm);
-               break;
-       }
-
-       return ret;
-}
-
-
-
 /*
  * This API is to be called during init to set the various voltage
  * domains to the voltage as per the opp table. Typically we boot up
@@ -345,19 +284,19 @@ int __init omap2_common_pm_late_init(void)
         * a completely different mechanism.
         * Disable this part if a DT blob is available.
         */
-       if (of_have_populated_dt())
-               return 0;
+       if (!of_have_populated_dt()) {
 
-       /* Init the voltage layer */
-       omap_pmic_late_init();
-       omap_voltage_late_init();
+               /* Init the voltage layer */
+               omap_pmic_late_init();
+               omap_voltage_late_init();
 
-       /* Initialize the voltages */
-       omap3_init_voltages();
-       omap4_init_voltages();
+               /* Initialize the voltages */
+               omap3_init_voltages();
+               omap4_init_voltages();
 
-       /* Smartreflex device init */
-       omap_devinit_smartreflex();
+               /* Smartreflex device init */
+               omap_devinit_smartreflex();
+       }
 
 #ifdef CONFIG_SUSPEND
        suspend_set_ops(&omap_pm_ops);
index c22503b17abdcea08d58126c0eefd714e0fec841..118ebde8d9127d3d32551e42382f0ab1f55e7739 100644 (file)
@@ -33,7 +33,6 @@ static inline int omap4_idle_init(void)
 extern void *omap3_secure_ram_storage;
 extern void omap3_pm_off_mode_enable(int);
 extern void omap_sram_idle(void);
-extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state);
 extern int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused);
 extern int (*omap_pm_suspend)(void);
 
@@ -52,7 +51,7 @@ static inline int omap4_opp_init(void)
 #endif
 
 extern int omap3_pm_get_suspend_state(struct powerdomain *pwrdm);
-extern int omap3_pm_set_suspend_state(struct powerdomain *pwrdm, int state);
+extern int omap3_pm_set_suspend_state(struct powerdomain *pwrdm, u8 fpwrst);
 
 #ifdef CONFIG_PM_DEBUG
 extern u32 enable_off_mode;
@@ -60,12 +59,6 @@ extern u32 enable_off_mode;
 #define enable_off_mode 0
 #endif
 
-#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
-extern void pm_dbg_update_time(struct powerdomain *pwrdm, int prev);
-#else
-#define pm_dbg_update_time(pwrdm, prev) do {} while (0);
-#endif /* CONFIG_PM_DEBUG */
-
 /* 24xx */
 extern void omap24xx_idle_loop_suspend(void);
 extern unsigned int omap24xx_idle_loop_suspend_sz;
@@ -83,6 +76,13 @@ extern unsigned int omap3_do_wfi_sz;
 /* ... and its pointer from SRAM after copy */
 extern void (*omap3_do_wfi_sram)(void);
 
+/* am33xx_do_wfi function pointer and size, for copy to SRAM */
+extern void am33xx_do_wfi(void);
+extern unsigned int am33xx_do_wfi_sz;
+extern unsigned int am33xx_resume_offset;
+/* ... and its pointer from SRAM after copy */
+extern void (*am33xx_do_wfi_sram)(void);
+
 /* save_secure_ram_context function pointer and size, for copy to SRAM */
 extern int save_secure_ram_context(u32 *addr);
 extern unsigned int save_secure_ram_context_sz;
index c333fa6dffa8193ec8f7b8dd8290609c8ac53c92..c18886c1b5fb2ad90920d29245393701e1b48ad5 100644 (file)
@@ -90,12 +90,8 @@ static int omap2_enter_full_retention(void)
        omap2_prm_write_mod_reg(0xffffffff, CORE_MOD, OMAP24XX_PM_WKST2);
        omap2_prm_write_mod_reg(0xffffffff, WKUP_MOD, PM_WKST);
 
-       /*
-        * Set MPU powerdomain's next power state to RETENTION;
-        * preserve logic state during retention
-        */
-       pwrdm_set_logic_retst(mpu_pwrdm, PWRDM_POWER_RET);
-       pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_RET);
+       WARN_ON(pwrdm_set_next_fpwrst(core_pwrdm, PWRDM_FUNC_PWRST_CSWR));
+       WARN_ON(pwrdm_set_next_fpwrst(mpu_pwrdm, PWRDM_FUNC_PWRST_CSWR));
 
        /* Workaround to kill USB */
        l = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0) | OMAP24XX_USBSTANDBYCTRL;
@@ -103,6 +99,8 @@ static int omap2_enter_full_retention(void)
 
        omap2_gpio_prepare_for_idle(0);
 
+       pwrdm_pre_transition(NULL);
+
        /* One last check for pending IRQs to avoid extra latency due
         * to sleeping unnecessarily. */
        if (omap_irq_pending())
@@ -114,6 +112,8 @@ static int omap2_enter_full_retention(void)
                           OMAP_SDRC_REGADDR(SDRC_POWER));
 
 no_sleep:
+       pwrdm_post_transition(NULL);
+
        omap2_gpio_resume_after_idle();
 
        clk_enable(osc_ck);
@@ -137,6 +137,9 @@ no_sleep:
        /* Mask future PRCM-to-MPU interrupts */
        omap2_prm_write_mod_reg(0x0, OCP_MOD, OMAP2_PRCM_IRQSTATUS_MPU_OFFSET);
 
+       WARN_ON(pwrdm_set_next_fpwrst(mpu_pwrdm, PWRDM_FUNC_PWRST_ON));
+       WARN_ON(pwrdm_set_next_fpwrst(core_pwrdm, PWRDM_FUNC_PWRST_ON));
+
        return 0;
 }
 
@@ -186,17 +189,20 @@ static void omap2_enter_mpu_retention(void)
                omap2_prm_write_mod_reg(0xffffffff, WKUP_MOD, PM_WKST);
 
                /* Try to enter MPU retention */
-               omap2_prm_write_mod_reg((0x01 << OMAP_POWERSTATE_SHIFT) |
-                                 OMAP_LOGICRETSTATE_MASK,
-                                 MPU_MOD, OMAP2_PM_PWSTCTRL);
+               WARN_ON(pwrdm_set_next_fpwrst(mpu_pwrdm,
+                                             PWRDM_FUNC_PWRST_CSWR));
        } else {
                /* Block MPU retention */
-
-               omap2_prm_write_mod_reg(OMAP_LOGICRETSTATE_MASK, MPU_MOD,
-                                                OMAP2_PM_PWSTCTRL);
+               WARN_ON(pwrdm_set_next_fpwrst(mpu_pwrdm, PWRDM_FUNC_PWRST_ON));
        }
 
+       pwrdm_pre_transition(mpu_pwrdm);
+
        omap2_sram_idle();
+
+       pwrdm_post_transition(mpu_pwrdm);
+
+       WARN_ON(pwrdm_set_next_fpwrst(mpu_pwrdm, PWRDM_FUNC_PWRST_ON));
 }
 
 static int omap2_can_sleep(void)
@@ -213,27 +219,21 @@ static int omap2_can_sleep(void)
 
 static void omap2_pm_idle(void)
 {
-       local_fiq_disable();
-
        if (!omap2_can_sleep()) {
                if (omap_irq_pending())
-                       goto out;
+                       return;
                omap2_enter_mpu_retention();
-               goto out;
+               return;
        }
 
        if (omap_irq_pending())
-               goto out;
+               return;
 
        omap2_enter_full_retention();
-
-out:
-       local_fiq_enable();
 }
 
 static void __init prcm_setup_regs(void)
 {
-       int i, num_mem_banks;
        struct powerdomain *pwrdm;
 
        /*
@@ -243,33 +243,13 @@ static void __init prcm_setup_regs(void)
        omap2_prm_write_mod_reg(OMAP24XX_AUTOIDLE_MASK, OCP_MOD,
                          OMAP2_PRCM_SYSCONFIG_OFFSET);
 
-       /*
-        * Set CORE powerdomain memory banks to retain their contents
-        * during RETENTION
-        */
-       num_mem_banks = pwrdm_get_mem_bank_count(core_pwrdm);
-       for (i = 0; i < num_mem_banks; i++)
-               pwrdm_set_mem_retst(core_pwrdm, i, PWRDM_POWER_RET);
-
-       /* Set CORE powerdomain's next power state to RETENTION */
-       pwrdm_set_next_pwrst(core_pwrdm, PWRDM_POWER_RET);
-
-       /*
-        * Set MPU powerdomain's next power state to RETENTION;
-        * preserve logic state during retention
-        */
-       pwrdm_set_logic_retst(mpu_pwrdm, PWRDM_POWER_RET);
-       pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_RET);
-
        /* Force-power down DSP, GFX powerdomains */
 
        pwrdm = clkdm_get_pwrdm(dsp_clkdm);
-       pwrdm_set_next_pwrst(pwrdm, PWRDM_POWER_OFF);
-       clkdm_sleep(dsp_clkdm);
+       WARN_ON(pwrdm_set_next_fpwrst(pwrdm, PWRDM_FUNC_PWRST_OFF));
 
        pwrdm = clkdm_get_pwrdm(gfx_clkdm);
-       pwrdm_set_next_pwrst(pwrdm, PWRDM_POWER_OFF);
-       clkdm_sleep(gfx_clkdm);
+       WARN_ON(pwrdm_set_next_fpwrst(pwrdm, PWRDM_FUNC_PWRST_OFF));
 
        /* Enable hardware-supervised idle for all clkdms */
        clkdm_for_each(omap_pm_clkdms_setup, NULL);
diff --git a/arch/arm/mach-omap2/pm33xx.c b/arch/arm/mach-omap2/pm33xx.c
new file mode 100644 (file)
index 0000000..93f970f
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * AM33XX Power Management Routines
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Vaibhav Bedia <vaibhav.bedia@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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/suspend.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/mailbox.h>
+#include <linux/interrupt.h>
+
+#include <asm/suspend.h>
+#include <asm/proc-fns.h>
+#include <asm/sizes.h>
+#include <asm/fncpy.h>
+#include <asm/system_misc.h>
+
+#include "pm.h"
+#include "cm33xx.h"
+#include "pm33xx.h"
+#include "control.h"
+#include "clockdomain.h"
+#include "powerdomain.h"
+#include "omap_hwmod.h"
+#include "omap_device.h"
+#include "soc.h"
+#include "sram.h"
+
+void (*am33xx_do_wfi_sram)(void);
+
+static void __iomem *am33xx_emif_base;
+static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm;
+static struct clockdomain *gfx_l4ls_clkdm;
+static struct omap_hwmod *usb_oh, *cpsw_oh, *tptc0_oh, *tptc1_oh, *tptc2_oh;
+static struct wkup_m3_context *wkup_m3;
+
+static DECLARE_COMPLETION(wkup_m3_sync);
+
+#ifdef CONFIG_SUSPEND
+static int am33xx_do_sram_idle(long unsigned int unused)
+{
+       am33xx_do_wfi_sram();
+       return 0;
+}
+
+static int am33xx_pm_suspend(void)
+{
+       int status, ret = 0;
+
+       /*
+        * By default the following IPs do not have MSTANDBY asserted
+        * which is necessary for PER domain transition. If the drivers
+        * are not compiled into the kernel HWMOD code will not change the
+        * state of the IPs if the IP was not never enabled. To ensure
+        * that there no issues with or without the drivers being compiled
+        * in the kernel, we forcefully put these IPs to idle.
+        */
+       omap_hwmod_enable(usb_oh);
+       omap_hwmod_enable(tptc0_oh);
+       omap_hwmod_enable(tptc1_oh);
+       omap_hwmod_enable(tptc2_oh);
+       omap_hwmod_enable(cpsw_oh);
+
+       omap_hwmod_idle(usb_oh);
+       omap_hwmod_idle(tptc0_oh);
+       omap_hwmod_idle(tptc1_oh);
+       omap_hwmod_idle(tptc2_oh);
+       omap_hwmod_idle(cpsw_oh);
+
+       /* Try to put GFX to sleep */
+       pwrdm_set_next_fpwrst(gfx_pwrdm, PWRDM_FUNC_PWRST_OFF);
+
+       ret = cpu_suspend(0, am33xx_do_sram_idle);
+
+       status = pwrdm_read_fpwrst(gfx_pwrdm);
+       if (status != PWRDM_FUNC_PWRST_OFF)
+               pr_err("GFX domain did not transition\n");
+       else
+               pr_info("GFX domain entered low power state\n");
+
+       /*
+        * GFX_L4LS clock domain needs to be woken up to
+        * ensure thet L4LS clock domain does not get stuck in transition
+        * If that happens L3 module does not get disabled, thereby leading
+        * to PER power domain transition failing
+        *
+        * The clock framework should take care of ensuring
+        * that the clock domain is in the right state when
+        * GFX driver is active.
+        */
+       clkdm_wakeup(gfx_l4ls_clkdm);
+       clkdm_sleep(gfx_l4ls_clkdm);
+
+       if (ret) {
+               pr_err("Kernel suspend failure\n");
+       } else {
+               status = omap_ctrl_readl(AM33XX_CONTROL_IPC_MSG_REG1);
+               status &= IPC_RESP_MASK;
+               status >>= __ffs(IPC_RESP_MASK);
+
+               switch (status) {
+               case 0:
+                       pr_info("Successfully put all powerdomains to target state\n");
+                       /*
+                        * XXX: Leads to loss of logic state in PER power domain
+                        * Use SOC specific ops for this?
+                        */
+                       break;
+               case 1:
+                       pr_err("Could not transition all powerdomains to target state\n");
+                       ret = -1;
+                       break;
+               default:
+                       pr_err("Something went wrong :(\nStatus = %d\n",
+                               status);
+                       ret = -1;
+               }
+       }
+
+       return ret;
+}
+
+static int am33xx_pm_enter(suspend_state_t suspend_state)
+{
+       int ret = 0;
+
+       switch (suspend_state) {
+       case PM_SUSPEND_STANDBY:
+       case PM_SUSPEND_MEM:
+               ret = am33xx_pm_suspend();
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int am33xx_pm_begin(suspend_state_t state)
+{
+       int ret = 0;
+       struct mailbox_msg dummy_msg;
+
+       disable_hlt();
+
+       MAILBOX_FILL_MSG(dummy_msg, 0, 0xABCDABCD, 0);
+
+       wkup_m3->ipc_data.sleep_mode = IPC_CMD_DS0;
+       wkup_m3->ipc_data.param1  = DS_IPC_DEFAULT;
+       wkup_m3->ipc_data.param2  = DS_IPC_DEFAULT;
+
+       am33xx_wkup_m3_ipc_cmd(&wkup_m3->ipc_data);
+
+       wkup_m3->state = M3_STATE_MSG_FOR_LP;
+
+       mailbox_enable_irq(wkup_m3->mbox, IRQ_RX);
+
+       ret = mailbox_msg_send(wkup_m3->mbox, &dummy_msg);
+       if (ret) {
+               pr_err("A8<->CM3 MSG for LP failed\n");
+               am33xx_m3_state_machine_reset();
+               ret = -1;
+       }
+
+       /* Give some time to M3 to respond. 500msec is a random value here */
+       if (!wait_for_completion_timeout(&wkup_m3_sync,
+                                       msecs_to_jiffies(500))) {
+               pr_err("A8<->CM3 sync failure\n");
+               am33xx_m3_state_machine_reset();
+               ret = -1;
+       } else {
+               pr_debug("Message sent for entering DeepSleep mode\n");
+               mailbox_disable_irq(wkup_m3->mbox, IRQ_RX);
+       }
+
+       return ret;
+}
+
+static void am33xx_pm_end(void)
+{
+       mailbox_enable_irq(wkup_m3->mbox, IRQ_RX);
+
+       am33xx_m3_state_machine_reset();
+
+       enable_hlt();
+
+       return;
+}
+
+static const struct platform_suspend_ops am33xx_pm_ops = {
+       .begin          = am33xx_pm_begin,
+       .end            = am33xx_pm_end,
+       .enter          = am33xx_pm_enter,
+       .valid          = suspend_valid_only_mem,
+};
+
+static void am33xx_m3_state_machine_reset(void)
+{
+       int ret = 0;
+       struct mailbox_msg dummy_msg;
+
+       MAILBOX_FILL_MSG(dummy_msg, 0, 0xABCDABCD, 0);
+
+       wkup_m3->ipc_data.sleep_mode    = IPC_CMD_RESET;
+       wkup_m3->ipc_data.param1        = DS_IPC_DEFAULT;
+       wkup_m3->ipc_data.param2        = DS_IPC_DEFAULT;
+
+       am33xx_wkup_m3_ipc_cmd(&wkup_m3->ipc_data);
+
+       wkup_m3->state = M3_STATE_MSG_FOR_RESET;
+
+       ret = mailbox_msg_send(wkup_m3->mbox, &dummy_msg);
+       if (!ret) {
+               pr_debug("Message sent for resetting M3 state machine\n");
+               /* Give some to M3 to respond. 500msec is a random value here */
+               if (!wait_for_completion_timeout(&wkup_m3_sync,
+                                               msecs_to_jiffies(500)))
+                       pr_err("A8<->CM3 sync failure\n");
+       } else {
+               pr_err("Could not reset M3 state machine!!!\n");
+               wkup_m3->state = M3_STATE_UNKNOWN;
+       }
+}
+#endif /* CONFIG_SUSPEND */
+
+/*
+ * Dummy notifier for the mailbox
+ * XXX: Get rid of this requirement once the MBX driver has been finalized
+ */
+static int wkup_mbox_msg(struct notifier_block *self, unsigned long len,
+               void *msg)
+{
+       return 0;
+}
+
+static struct notifier_block wkup_mbox_notifier = {
+       .notifier_call = wkup_mbox_msg,
+};
+
+static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
+{
+       am33xx_txev_eoi();
+
+       switch (wkup_m3->state) {
+       case M3_STATE_RESET:
+               wkup_m3->state = M3_STATE_INITED;
+               break;
+       case M3_STATE_MSG_FOR_RESET:
+               wkup_m3->state = M3_STATE_INITED;
+               mailbox_empty_tx(wkup_m3->mbox);
+               complete(&wkup_m3_sync);
+               break;
+       case M3_STATE_MSG_FOR_LP:
+               mailbox_empty_tx(wkup_m3->mbox);
+               complete(&wkup_m3_sync);
+               break;
+       case M3_STATE_UNKNOWN:
+               pr_err("IRQ %d with WKUP_M3 in unknown state\n", irq);
+               mailbox_empty_tx(wkup_m3->mbox);
+               return IRQ_NONE;
+       }
+
+       am33xx_txev_enable();
+       return IRQ_HANDLED;
+}
+
+static void am33xx_pm_firmware_cb(const struct firmware *fw, void *context)
+{
+       struct wkup_m3_context *wkup_m3_context = context;
+       struct platform_device *pdev = to_platform_device(wkup_m3_context->dev);
+       int ret = 0;
+
+       /* no firmware found */
+       if (!fw) {
+               dev_err(wkup_m3_context->dev, "request_firmware failed\n");
+               goto err;
+       }
+
+       memcpy((void *)wkup_m3_context->code, fw->data, fw->size);
+       pr_info("Copied the M3 firmware to UMEM\n");
+
+       wkup_m3->state = M3_STATE_RESET;
+
+       ret = omap_device_deassert_hardreset(pdev, "wkup_m3");
+       if (ret) {
+               pr_err("Could not deassert the reset for WKUP_M3\n");
+               goto err;
+       } else {
+#ifdef CONFIG_SUSPEND
+               suspend_set_ops(&am33xx_pm_ops);
+               /*
+                * Physical resume address to be used by ROM code
+                */
+               wkup_m3->ipc_data.resume_addr = (AM33XX_OCMC_END -
+                               am33xx_do_wfi_sz + am33xx_resume_offset + 0x4);
+#endif
+               return;
+       }
+
+err:
+       mailbox_put(wkup_m3_context->mbox, &wkup_mbox_notifier);
+}
+
+static int wkup_m3_init(void)
+{
+       int irq, ret = 0;
+       struct resource *mem;
+       struct platform_device *pdev = to_platform_device(wkup_m3->dev);
+
+       omap_device_enable_hwmods(to_omap_device(pdev));
+
+       /* Reserve the MBOX for sending messages to M3 */
+       wkup_m3->mbox = mailbox_get("wkup_m3", &wkup_mbox_notifier);
+       if (IS_ERR(wkup_m3->mbox)) {
+               pr_err("Could not reserve mailbox for A8->M3 IPC\n");
+               ret = -ENODEV;
+               goto exit;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (!irq) {
+               dev_err(wkup_m3->dev, "no irq resource\n");
+               ret = -ENXIO;
+               goto err;
+       }
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(wkup_m3->dev, "no memory resource\n");
+               ret = -ENXIO;
+               goto err;
+       }
+
+       wkup_m3->code = devm_request_and_ioremap(wkup_m3->dev, mem);
+       if (!wkup_m3->code) {
+               dev_err(wkup_m3->dev, "could not ioremap\n");
+               ret = -EADDRNOTAVAIL;
+               goto err;
+       }
+
+       ret = devm_request_irq(wkup_m3->dev, irq, wkup_m3_txev_handler,
+                 IRQF_DISABLED, "wkup_m3_txev", NULL);
+       if (ret) {
+               dev_err(wkup_m3->dev, "request_irq failed\n");
+               goto err;
+       } else {
+               am33xx_txev_enable();
+       }
+
+       pr_info("Trying to load am335x-pm-firmware.bin");
+
+       /* We don't want to delay boot */
+       request_firmware_nowait(THIS_MODULE, 0, "am335x-pm-firmware.bin",
+                               wkup_m3->dev, GFP_KERNEL, wkup_m3,
+                               am33xx_pm_firmware_cb);
+       return 0;
+
+err:
+       mailbox_put(wkup_m3->mbox, &wkup_mbox_notifier);
+exit:
+       return ret;
+}
+
+/*
+ * Push the minimal suspend-resume code to SRAM
+ */
+void am33xx_push_sram_idle(void)
+{
+       am33xx_do_wfi_sram = (void *)omap_sram_push
+                                       (am33xx_do_wfi, am33xx_do_wfi_sz);
+}
+
+static int __init am33xx_map_emif(void)
+{
+       am33xx_emif_base = ioremap(AM33XX_EMIF_BASE, SZ_32K);
+
+       if (!am33xx_emif_base)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void __iomem *am33xx_get_emif_base(void)
+{
+       return am33xx_emif_base;
+}
+
+int __init am33xx_pm_init(void)
+{
+       int ret;
+
+       if (!soc_is_am33xx())
+               return -ENODEV;
+
+       pr_info("Power Management for AM33XX family\n");
+
+       /*
+        * By default the following IPs do not have MSTANDBY asserted
+        * which is necessary for PER domain transition. If the drivers
+        * are not compiled into the kernel HWMOD code will not change the
+        * state of the IPs if the IP was not never enabled
+        */
+       usb_oh          = omap_hwmod_lookup("usb_otg_hs");
+       tptc0_oh        = omap_hwmod_lookup("tptc0");
+       tptc1_oh        = omap_hwmod_lookup("tptc1");
+       tptc2_oh        = omap_hwmod_lookup("tptc2");
+       cpsw_oh         = omap_hwmod_lookup("cpgmac0");
+
+       gfx_pwrdm = pwrdm_lookup("gfx_pwrdm");
+       per_pwrdm = pwrdm_lookup("per_pwrdm");
+
+       gfx_l4ls_clkdm = clkdm_lookup("gfx_l4ls_gfx_clkdm");
+
+       if ((!usb_oh) || (!tptc0_oh) || (!tptc1_oh) || (!tptc2_oh) ||
+               (!cpsw_oh) || (!gfx_pwrdm) || (!per_pwrdm) ||
+               (!gfx_l4ls_clkdm)) {
+               ret = -ENODEV;
+               goto err;
+       }
+
+       wkup_m3 = kzalloc(sizeof(struct wkup_m3_context), GFP_KERNEL);
+       if (!wkup_m3) {
+               pr_err("Memory allocation failed\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ret = am33xx_map_emif();
+       if (ret) {
+               pr_err("Could not ioremap EMIF\n");
+               goto err;
+       }
+
+       (void) clkdm_for_each(omap_pm_clkdms_setup, NULL);
+
+       /* CEFUSE domain can be turned off post bootup */
+       cefuse_pwrdm = pwrdm_lookup("cefuse_pwrdm");
+       if (cefuse_pwrdm)
+               pwrdm_set_next_fpwrst(cefuse_pwrdm, PWRDM_FUNC_PWRST_OFF);
+       else
+               pr_err("Failed to get cefuse_pwrdm\n");
+
+       wkup_m3->dev = omap_device_get_by_hwmod_name("wkup_m3");
+
+       ret = wkup_m3_init();
+       if (ret)
+               pr_err("Could not initialise firmware loading\n");
+
+err:
+       return ret;
+}
diff --git a/arch/arm/mach-omap2/pm33xx.h b/arch/arm/mach-omap2/pm33xx.h
new file mode 100644 (file)
index 0000000..13a2c85
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * AM33XX Power Management Routines
+ *
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Vaibhav Bedia <vaibhav.bedia@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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __ARCH_ARM_MACH_OMAP2_PM33XX_H
+#define __ARCH_ARM_MACH_OMAP2_PM33XX_H
+
+#include "control.h"
+
+#ifndef __ASSEMBLER__
+struct wkup_m3_context {
+       struct am33xx_ipc_data  ipc_data;
+       struct device           *dev;
+       struct firmware         *firmware;
+       struct mailbox          *mbox;
+       void __iomem            *code;
+       u8                      state;
+};
+
+#ifdef CONFIG_SUSPEND
+static void am33xx_m3_state_machine_reset(void);
+#else
+static inline void am33xx_m3_state_machine_reset(void) {}
+#endif /* CONFIG_SUSPEND */
+
+extern void __iomem *am33xx_get_emif_base(void);
+#endif
+
+#define        IPC_CMD_DS0                     0x3
+#define IPC_CMD_RESET                   0xe
+#define DS_IPC_DEFAULT                 0xffffffff
+
+#define IPC_RESP_SHIFT                 16
+#define IPC_RESP_MASK                  (0xffff << 16)
+
+#define M3_STATE_UNKNOWN               0
+#define M3_STATE_RESET                 1
+#define M3_STATE_INITED                        2
+#define M3_STATE_MSG_FOR_LP            3
+#define M3_STATE_MSG_FOR_RESET         4
+
+#define AM33XX_OCMC_END                        0x40310000
+#define AM33XX_EMIF_BASE               0x4C000000
+
+#endif
index 7be3622cfc850711bea6f5b1385b7631ea7033dd..5ef171fdc11cafa48b342fbac1742febb02adcaf 100644 (file)
@@ -56,9 +56,9 @@ u16 pm34xx_errata;
 
 struct power_state {
        struct powerdomain *pwrdm;
-       u32 next_state;
+       u8 next_fpwrst;
 #ifdef CONFIG_SUSPEND
-       u32 saved_state;
+       u8 saved_fpwrst;
 #endif
        struct list_head node;
 };
@@ -111,24 +111,26 @@ static void omap3_core_restore_context(void)
 static void omap3_save_secure_ram_context(void)
 {
        u32 ret;
-       int mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm);
+       int mpu_next_fpwrst;
 
-       if (omap_type() != OMAP2_DEVICE_TYPE_GP) {
-               /*
-                * MPU next state must be set to POWER_ON temporarily,
-                * otherwise the WFI executed inside the ROM code
-                * will hang the system.
-                */
-               pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON);
-               ret = _omap_save_secure_sram((u32 *)
-                               __pa(omap3_secure_ram_storage));
-               pwrdm_set_next_pwrst(mpu_pwrdm, mpu_next_state);
-               /* Following is for error tracking, it should not happen */
-               if (ret) {
-                       pr_err("save_secure_sram() returns %08x\n", ret);
-                       while (1)
-                               ;
-               }
+       if (omap_type() == OMAP2_DEVICE_TYPE_GP)
+               return;
+
+       /*
+        * MPU next state must be set to POWER_ON temporarily,
+        * otherwise the WFI executed inside the ROM code will hang
+        * the system.
+        */
+       mpu_next_fpwrst = pwrdm_read_next_fpwrst(mpu_pwrdm);
+       pwrdm_set_next_fpwrst(mpu_pwrdm, PWRDM_FUNC_PWRST_ON);
+       ret = _omap_save_secure_sram((u32 *)__pa(omap3_secure_ram_storage));
+       pwrdm_set_next_fpwrst(mpu_pwrdm, mpu_next_fpwrst);
+       /* Following is for error tracking, it should not happen */
+       /* XXX This needs to be converted to a BUG() or removed */
+       if (ret) {
+               pr_err("save_secure_sram() returns %08x\n", ret);
+               while (1)
+                       ;
        }
 }
 
@@ -241,21 +243,20 @@ void omap_sram_idle(void)
        /* save_state = 2 => Only L2 lost */
        /* save_state = 3 => L1, L2 and logic lost */
        int save_state = 0;
-       int mpu_next_state = PWRDM_POWER_ON;
-       int per_next_state = PWRDM_POWER_ON;
-       int core_next_state = PWRDM_POWER_ON;
-       int per_going_off;
-       int core_prev_state;
+       int mpu_next_fpwrst, per_next_fpwrst, core_next_fpwrst;
+       int per_going_off, core_prev_fpwrst;
        u32 sdrc_pwr = 0;
 
-       mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm);
-       switch (mpu_next_state) {
-       case PWRDM_POWER_ON:
-       case PWRDM_POWER_RET:
+       mpu_next_fpwrst = pwrdm_read_next_fpwrst(mpu_pwrdm);
+       switch (mpu_next_fpwrst) {
+       case PWRDM_FUNC_PWRST_ON:
+       case PWRDM_FUNC_PWRST_CSWR:
+       case PWRDM_FUNC_PWRST_INACTIVE:
                /* No need to save context */
                save_state = 0;
                break;
-       case PWRDM_POWER_OFF:
+       case PWRDM_FUNC_PWRST_OSWR: /* XXX accurate? */
+       case PWRDM_FUNC_PWRST_OFF:
                save_state = 3;
                break;
        default:
@@ -265,33 +266,34 @@ void omap_sram_idle(void)
        }
 
        /* NEON control */
-       if (pwrdm_read_pwrst(neon_pwrdm) == PWRDM_POWER_ON)
-               pwrdm_set_next_pwrst(neon_pwrdm, mpu_next_state);
+       if (pwrdm_read_fpwrst(neon_pwrdm) == PWRDM_FUNC_PWRST_ON)
+               WARN_ON(pwrdm_set_next_fpwrst(neon_pwrdm, mpu_next_fpwrst));
 
-       /* Enable IO-PAD and IO-CHAIN wakeups */
-       per_next_state = pwrdm_read_next_pwrst(per_pwrdm);
-       core_next_state = pwrdm_read_next_pwrst(core_pwrdm);
+       per_next_fpwrst = pwrdm_read_next_fpwrst(per_pwrdm);
+       core_next_fpwrst = pwrdm_read_next_fpwrst(core_pwrdm);
 
        pwrdm_pre_transition(NULL);
 
        /* PER */
-       if (per_next_state < PWRDM_POWER_ON) {
-               per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0;
+       if (per_next_fpwrst != PWRDM_FUNC_PWRST_ON &&
+           per_next_fpwrst != PWRDM_FUNC_PWRST_INACTIVE) {
+               per_going_off =
+                       (per_next_fpwrst == PWRDM_FUNC_PWRST_OFF) ? 1 : 0;
                omap2_gpio_prepare_for_idle(per_going_off);
        }
 
        /* CORE */
-       if (core_next_state < PWRDM_POWER_ON) {
-               if (core_next_state == PWRDM_POWER_OFF) {
-                       omap3_core_save_context();
-                       omap3_cm_save_context();
-               }
+       /* XXX Does the context save need to happen for OSWR? */
+       if (core_next_fpwrst == PWRDM_FUNC_PWRST_OFF ||
+           core_next_fpwrst == PWRDM_FUNC_PWRST_OSWR) {
+               omap3_core_save_context();
+               omap3_cm_save_context();
        }
 
        omap3_intc_prepare_idle();
 
        /*
-        * On EMU/HS devices ROM code restores a SRDC value
+        * On EMU/HS devices ROM code restores a SDRC value
         * from scratchpad which has automatic self refresh on timeout
         * of AUTO_CNT = 1 enabled. This takes care of erratum ID i443.
         * Hence store/restore the SDRC_POWER register here.
@@ -299,7 +301,7 @@ void omap_sram_idle(void)
        if (cpu_is_omap3430() && omap_rev() >= OMAP3430_REV_ES3_0 &&
            (omap_type() == OMAP2_DEVICE_TYPE_EMU ||
             omap_type() == OMAP2_DEVICE_TYPE_SEC) &&
-           core_next_state == PWRDM_POWER_OFF)
+           core_next_fpwrst == PWRDM_FUNC_PWRST_OFF)
                sdrc_pwr = sdrc_read_reg(SDRC_POWER);
 
        /*
@@ -318,38 +320,39 @@ void omap_sram_idle(void)
        if (cpu_is_omap3430() && omap_rev() >= OMAP3430_REV_ES3_0 &&
            (omap_type() == OMAP2_DEVICE_TYPE_EMU ||
             omap_type() == OMAP2_DEVICE_TYPE_SEC) &&
-           core_next_state == PWRDM_POWER_OFF)
+           core_next_fpwrst == PWRDM_FUNC_PWRST_OFF)
                sdrc_write_reg(sdrc_pwr, SDRC_POWER);
 
        /* CORE */
-       if (core_next_state < PWRDM_POWER_ON) {
-               core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm);
-               if (core_prev_state == PWRDM_POWER_OFF) {
+       if (core_next_fpwrst != PWRDM_FUNC_PWRST_ON &&
+           core_next_fpwrst != PWRDM_FUNC_PWRST_INACTIVE) {
+               core_prev_fpwrst = pwrdm_read_prev_fpwrst(core_pwrdm);
+               if (core_prev_fpwrst == PWRDM_FUNC_PWRST_OFF ||
+                   core_prev_fpwrst == PWRDM_FUNC_PWRST_OSWR) {
                        omap3_core_restore_context();
                        omap3_cm_restore_context();
                        omap3_sram_restore_context();
                        omap2_sms_restore_context();
                }
-               if (core_next_state == PWRDM_POWER_OFF)
+               if (core_next_fpwrst == PWRDM_FUNC_PWRST_OFF)
                        omap2_prm_clear_mod_reg_bits(OMAP3430_AUTO_OFF_MASK,
-                                              OMAP3430_GR_MOD,
-                                              OMAP3_PRM_VOLTCTRL_OFFSET);
+                                                    OMAP3430_GR_MOD,
+                                                    OMAP3_PRM_VOLTCTRL_OFFSET);
        }
        omap3_intc_resume_idle();
 
        pwrdm_post_transition(NULL);
 
        /* PER */
-       if (per_next_state < PWRDM_POWER_ON)
+       if (per_next_fpwrst != PWRDM_FUNC_PWRST_ON &&
+           per_next_fpwrst != PWRDM_FUNC_PWRST_INACTIVE)
                omap2_gpio_resume_after_idle();
 }
 
 static void omap3_pm_idle(void)
 {
-       local_fiq_disable();
-
        if (omap_irq_pending())
-               goto out;
+               return;
 
        trace_power_start(POWER_CSTATE, 1, smp_processor_id());
        trace_cpu_idle(1, smp_processor_id());
@@ -358,23 +361,21 @@ static void omap3_pm_idle(void)
 
        trace_power_end(smp_processor_id());
        trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id());
-
-out:
-       local_fiq_enable();
 }
 
 #ifdef CONFIG_SUSPEND
 static int omap3_pm_suspend(void)
 {
        struct power_state *pwrst;
-       int state, ret = 0;
+       int prev_fpwrst;
+       int ret = 0;
 
        /* Read current next_pwrsts */
        list_for_each_entry(pwrst, &pwrst_list, node)
-               pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
+               pwrst->saved_fpwrst = pwrdm_read_next_fpwrst(pwrst->pwrdm);
        /* Set ones wanted by suspend */
        list_for_each_entry(pwrst, &pwrst_list, node) {
-               if (omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state))
+               if (pwrdm_set_fpwrst(pwrst->pwrdm, pwrst->next_fpwrst))
                        goto restore;
                if (pwrdm_clear_all_prev_pwrst(pwrst->pwrdm))
                        goto restore;
@@ -387,13 +388,22 @@ static int omap3_pm_suspend(void)
 restore:
        /* Restore next_pwrsts */
        list_for_each_entry(pwrst, &pwrst_list, node) {
-               state = pwrdm_read_prev_pwrst(pwrst->pwrdm);
-               if (state > pwrst->next_state) {
-                       pr_info("Powerdomain (%s) didn't enter target state %d\n",
-                               pwrst->pwrdm->name, pwrst->next_state);
+               prev_fpwrst = pwrdm_read_prev_fpwrst(pwrst->pwrdm);
+               /*
+                * The SGX powerdomain will report its previous state as
+                * INACTIVE when it's been programmed to ON.  This seems to
+                * be the only OMAP3 powerdomain that does this.
+                */
+               if (prev_fpwrst == PWRDM_FUNC_PWRST_INACTIVE)
+                       prev_fpwrst = PWRDM_FUNC_PWRST_ON;
+               if (prev_fpwrst != pwrst->next_fpwrst) {
+                       pr_info("Powerdomain %s didn't enter target state %s - entered state %s instead\n",
+                               pwrst->pwrdm->name,
+                               pwrdm_convert_fpwrst_to_name(pwrst->next_fpwrst),
+                               pwrdm_convert_fpwrst_to_name(prev_fpwrst));
                        ret = -1;
                }
-               omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
+               WARN_ON(pwrdm_set_fpwrst(pwrst->pwrdm, pwrst->saved_fpwrst));
        }
        if (ret)
                pr_err("Could not enter target state in pm_suspend\n");
@@ -562,24 +572,31 @@ static void __init prcm_setup_regs(void)
 void omap3_pm_off_mode_enable(int enable)
 {
        struct power_state *pwrst;
-       u32 state;
+       u8 fpwrst;
 
-       if (enable)
-               state = PWRDM_POWER_OFF;
-       else
-               state = PWRDM_POWER_RET;
+       fpwrst = (enable) ? PWRDM_FUNC_PWRST_OFF : PWRDM_FUNC_PWRST_CSWR;
 
+       /*
+        * XXX This should be replaced by explicit lists of
+        * powerdomains with specific powerstates to set
+        */
        list_for_each_entry(pwrst, &pwrst_list, node) {
                if (IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583) &&
-                               pwrst->pwrdm == core_pwrdm &&
-                               state == PWRDM_POWER_OFF) {
-                       pwrst->next_state = PWRDM_POWER_RET;
+                   pwrst->pwrdm == core_pwrdm &&
+                   fpwrst == PWRDM_FUNC_PWRST_OFF) {
+                       pwrst->next_fpwrst = PWRDM_FUNC_PWRST_CSWR;
                        pr_warn("%s: Core OFF disabled due to errata i583\n",
                                __func__);
                } else {
-                       pwrst->next_state = state;
+                       pwrst->next_fpwrst = fpwrst;
+                       if (!pwrdm_supports_fpwrst(pwrst->pwrdm,
+                                                  pwrst->next_fpwrst))
+                               pwrst->next_fpwrst = PWRDM_FUNC_PWRST_CSWR;
+                       if (!pwrdm_supports_fpwrst(pwrst->pwrdm,
+                                                  pwrst->next_fpwrst))
+                               pwrst->next_fpwrst = PWRDM_FUNC_PWRST_ON;
                }
-               omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
+               WARN_ON(pwrdm_set_fpwrst(pwrst->pwrdm, pwrst->next_fpwrst));
        }
 }
 
@@ -587,20 +604,27 @@ int omap3_pm_get_suspend_state(struct powerdomain *pwrdm)
 {
        struct power_state *pwrst;
 
-       list_for_each_entry(pwrst, &pwrst_list, node) {
+       list_for_each_entry(pwrst, &pwrst_list, node)
                if (pwrst->pwrdm == pwrdm)
-                       return pwrst->next_state;
-       }
+                       return pwrst->next_fpwrst;
        return -EINVAL;
 }
 
-int omap3_pm_set_suspend_state(struct powerdomain *pwrdm, int state)
+int omap3_pm_set_suspend_state(struct powerdomain *pwrdm, u8 fpwrst)
 {
        struct power_state *pwrst;
 
+       /*
+        * XXX This should be replaced by per-powerdomain suspend
+        * power state files
+        */
        list_for_each_entry(pwrst, &pwrst_list, node) {
                if (pwrst->pwrdm == pwrdm) {
-                       pwrst->next_state = state;
+                       pwrst->next_fpwrst = fpwrst;
+                       if (!pwrdm_supports_fpwrst(pwrdm, pwrst->next_fpwrst))
+                               pwrst->next_fpwrst = PWRDM_FUNC_PWRST_CSWR;
+                       if (!pwrdm_supports_fpwrst(pwrdm, pwrst->next_fpwrst))
+                               pwrst->next_fpwrst = PWRDM_FUNC_PWRST_ON;
                        return 0;
                }
        }
@@ -618,13 +642,20 @@ static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
        if (!pwrst)
                return -ENOMEM;
        pwrst->pwrdm = pwrdm;
-       pwrst->next_state = PWRDM_POWER_RET;
+
+       /*
+        * XXX This should be replaced by explicit lists of
+        * powerdomains with specific powerstates to set
+        */
+       pwrst->next_fpwrst = PWRDM_FUNC_PWRST_CSWR;
+       if (!pwrdm_supports_fpwrst(pwrdm, pwrst->next_fpwrst))
+               pwrst->next_fpwrst = PWRDM_FUNC_PWRST_ON;
        list_add(&pwrst->node, &pwrst_list);
 
        if (pwrdm_has_hdwr_sar(pwrdm))
                pwrdm_enable_hdwr_sar(pwrdm);
 
-       return omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
+       return WARN_ON(pwrdm_set_fpwrst(pwrst->pwrdm, pwrst->next_fpwrst));
 }
 
 /*
@@ -759,14 +790,12 @@ int __init omap3_pm_init(void)
                        pr_err("Memory allocation failed when allocating for secure sram context\n");
 
                local_irq_disable();
-               local_fiq_disable();
 
                omap_dma_global_context_save();
                omap3_save_secure_ram_context();
                omap_dma_global_context_restore();
 
                local_irq_enable();
-               local_fiq_enable();
        }
 
        omap3_save_scratchpad_contents();
similarity index 57%
rename from arch/arm/mach-omap2/pm44xx.c
rename to arch/arm/mach-omap2/pm_omap4plus.c
index aa6fd98f606e30aa96c164b2ce3dd40719777e0c..0265ed2802c60ffa02e5f52ea3938790154ec4d6 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * OMAP4 Power Management Routines
+ * OMAP4PLUS Power Management Routines
  *
- * Copyright (C) 2010-2011 Texas Instruments, Inc.
+ * Copyright (C) 2010-2013 Texas Instruments, Inc.
  * Rajendra Nayak <rnayak@ti.com>
  * Santosh Shilimkar <santosh.shilimkar@ti.com>
  *
 
 struct power_state {
        struct powerdomain *pwrdm;
-       u32 next_state;
+       u8 next_fpwrst;
 #ifdef CONFIG_SUSPEND
-       u32 saved_state;
-       u32 saved_logic_state;
+       u8 saved_fpwrst;
 #endif
        struct list_head node;
 };
@@ -40,20 +39,19 @@ static LIST_HEAD(pwrst_list);
 static int omap4_pm_suspend(void)
 {
        struct power_state *pwrst;
-       int state, ret = 0;
+       int prev_fpwrst;
+       int ret = 0;
        u32 cpu_id = smp_processor_id();
 
+       /* XXX Seems like these two loops could be combined into one loop? */
+
        /* Save current powerdomain state */
-       list_for_each_entry(pwrst, &pwrst_list, node) {
-               pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
-               pwrst->saved_logic_state = pwrdm_read_logic_retst(pwrst->pwrdm);
-       }
+       list_for_each_entry(pwrst, &pwrst_list, node)
+               pwrst->saved_fpwrst = pwrdm_read_next_fpwrst(pwrst->pwrdm);
 
        /* Set targeted power domain states by suspend */
-       list_for_each_entry(pwrst, &pwrst_list, node) {
-               omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
-               pwrdm_set_logic_retst(pwrst->pwrdm, PWRDM_POWER_OFF);
-       }
+       list_for_each_entry(pwrst, &pwrst_list, node)
+               WARN_ON(pwrdm_set_fpwrst(pwrst->pwrdm, pwrst->next_fpwrst));
 
        /*
         * For MPUSS to hit power domain retention(CSWR or OSWR),
@@ -64,18 +62,19 @@ static int omap4_pm_suspend(void)
         * domain CSWR is not supported by hardware.
         * More details can be found in OMAP4430 TRM section 4.3.4.2.
         */
-       omap4_enter_lowpower(cpu_id, PWRDM_POWER_OFF);
+       omap4_mpuss_enter_lowpower(cpu_id, PWRDM_FUNC_PWRST_OFF);
 
        /* Restore next powerdomain state */
        list_for_each_entry(pwrst, &pwrst_list, node) {
-               state = pwrdm_read_prev_pwrst(pwrst->pwrdm);
-               if (state > pwrst->next_state) {
-                       pr_info("Powerdomain (%s) didn't enter target state %d\n",
-                               pwrst->pwrdm->name, pwrst->next_state);
+               prev_fpwrst = pwrdm_read_prev_fpwrst(pwrst->pwrdm);
+               if (prev_fpwrst != pwrst->next_fpwrst) {
+                       pr_info("Powerdomain (%s) didn't enter target state %s - entered state %s instead\n",
+                               pwrst->pwrdm->name,
+                               pwrdm_convert_fpwrst_to_name(pwrst->next_fpwrst),
+                               pwrdm_convert_fpwrst_to_name(prev_fpwrst));
                        ret = -1;
                }
-               omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
-               pwrdm_set_logic_retst(pwrst->pwrdm, pwrst->saved_logic_state);
+               WARN_ON(pwrdm_set_fpwrst(pwrst->pwrdm, pwrst->saved_fpwrst));
        }
        if (ret)
                pr_crit("Could not enter target state in pm_suspend\n");
@@ -106,10 +105,16 @@ static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
                return -ENOMEM;
 
        pwrst->pwrdm = pwrdm;
-       pwrst->next_state = PWRDM_POWER_RET;
+       /*
+        * XXX This should be replaced by explicit lists of
+        * powerdomains with specific powerstates to set
+        */
+       pwrst->next_fpwrst = PWRDM_FUNC_PWRST_CSWR;
+       if (!pwrdm_supports_fpwrst(pwrdm, pwrst->next_fpwrst))
+               pwrst->next_fpwrst = PWRDM_FUNC_PWRST_ON;
        list_add(&pwrst->node, &pwrst_list);
 
-       return omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
+       return WARN_ON(pwrdm_set_fpwrst(pwrst->pwrdm, pwrst->next_fpwrst));
 }
 
 /**
@@ -121,48 +126,27 @@ static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
  */
 static void omap_default_idle(void)
 {
-       local_fiq_disable();
-
        omap_do_wfi();
-
-       local_fiq_enable();
 }
 
 /**
- * omap4_pm_init - Init routine for OMAP4 PM
+ * omap4_init_static_deps - Init static clkdm dependencies on OMAP4
  *
- * Initializes all powerdomain and clockdomain target states
- * and all PRCM settings.
+ * The dynamic dependency between MPUSS -> MEMIF and
+ * MPUSS -> L4_PER/L3_* and DUCATI -> L3_* doesn't work as
+ * expected. The hardware recommendation is to enable static
+ * dependencies for these to avoid system lock ups or random crashes.
+ * The L4 wakeup depedency is added to workaround the OCP sync hardware
+ * BUG with 32K synctimer which lead to incorrect timer value read
+ * from the 32K counter. The BUG applies for GPTIMER1 and WDT2 which
+ * are part of L4 wakeup clockdomain.
  */
-int __init omap4_pm_init(void)
+static inline int omap4_init_static_deps(void)
 {
        int ret;
        struct clockdomain *emif_clkdm, *mpuss_clkdm, *l3_1_clkdm, *l4wkup;
        struct clockdomain *ducati_clkdm, *l3_2_clkdm, *l4_per_clkdm;
 
-       if (omap_rev() == OMAP4430_REV_ES1_0) {
-               WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
-               return -ENODEV;
-       }
-
-       pr_err("Power Management for TI OMAP4.\n");
-
-       ret = pwrdm_for_each(pwrdms_setup, NULL);
-       if (ret) {
-               pr_err("Failed to setup powerdomains\n");
-               goto err2;
-       }
-
-       /*
-        * The dynamic dependency between MPUSS -> MEMIF and
-        * MPUSS -> L4_PER/L3_* and DUCATI -> L3_* doesn't work as
-        * expected. The hardware recommendation is to enable static
-        * dependencies for these to avoid system lock ups or random crashes.
-        * The L4 wakeup depedency is added to workaround the OCP sync hardware
-        * BUG with 32K synctimer which lead to incorrect timer value read
-        * from the 32K counter. The BUG applies for GPTIMER1 and WDT2 which
-        * are part of L4 wakeup clockdomain.
-        */
        mpuss_clkdm = clkdm_lookup("mpuss_clkdm");
        emif_clkdm = clkdm_lookup("l3_emif_clkdm");
        l3_1_clkdm = clkdm_lookup("l3_1_clkdm");
@@ -172,7 +156,7 @@ int __init omap4_pm_init(void)
        ducati_clkdm = clkdm_lookup("ducati_clkdm");
        if ((!mpuss_clkdm) || (!emif_clkdm) || (!l3_1_clkdm) || (!l4wkup) ||
                (!l3_2_clkdm) || (!ducati_clkdm) || (!l4_per_clkdm))
-               goto err2;
+               return -EINVAL;
 
        ret = clkdm_add_wkdep(mpuss_clkdm, emif_clkdm);
        ret |= clkdm_add_wkdep(mpuss_clkdm, l3_1_clkdm);
@@ -183,6 +167,67 @@ int __init omap4_pm_init(void)
        ret |= clkdm_add_wkdep(ducati_clkdm, l3_2_clkdm);
        if (ret) {
                pr_err("Failed to add MPUSS -> L3/EMIF/L4PER, DUCATI -> L3 wakeup dependency\n");
+       }
+
+       return ret;
+}
+
+/**
+ * omap5_init_static_deps - Init static clkdm dependencies on OMAP5
+ *
+ * The dynamic dependency between MPUSS -> EMIF is broken and has
+ * not worked as expected. The hardware recommendation is to
+ * enable static dependencies for these to avoid system
+ * lock ups or random crashes.
+ */
+static inline int omap5_init_static_deps(void)
+{
+       struct clockdomain *mpuss_clkdm, *emif_clkdm;
+       int ret;
+
+       mpuss_clkdm = clkdm_lookup("mpu_clkdm");
+       emif_clkdm = clkdm_lookup("emif_clkdm");
+       if (!mpuss_clkdm || !emif_clkdm)
+               return -EINVAL;
+
+       ret = clkdm_add_wkdep(mpuss_clkdm, emif_clkdm);
+       if (ret)
+               pr_err("Failed to add MPUSS -> EMIF wakeup dependency\n");
+
+       return ret;
+}
+
+
+/**
+ * omap4_pm_init - Init routine for OMAP4+ devices
+ *
+ * Initializes all powerdomain and clockdomain target states
+ * and all PRCM settings.
+ */
+int __init omap4_pm_init(void)
+{
+       int ret;
+
+       if (omap_rev() == OMAP4430_REV_ES1_0) {
+               WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
+               return -ENODEV;
+       }
+
+       pr_info("Power Management for TI OMAP4PLUS devices.\n");
+
+       ret = pwrdm_for_each(pwrdms_setup, NULL);
+       if (ret) {
+               pr_err("Failed to setup powerdomains.\n");
+               goto err2;
+       }
+
+       if (cpu_is_omap44xx())
+               ret = omap4_init_static_deps();
+       else if (soc_is_omap54xx())
+               ret = omap5_init_static_deps();
+
+       if (ret) {
+               pr_err("Failed to initialise static dependencies.\n");
                goto err2;
        }
 
@@ -198,10 +243,11 @@ int __init omap4_pm_init(void)
        omap_pm_suspend = omap4_pm_suspend;
 #endif
 
-       /* Overwrite the default cpu_do_idle() */
+       /* Overwrite the default arch_idle() */
        arm_pm_idle = omap_default_idle;
 
-       omap4_idle_init();
+       if (cpu_is_omap44xx() || soc_is_omap54xx())
+               omap4_idle_init();
 
 err2:
        return ret;
index dea62a9aad0751d77796b5c3fbbba513c9ae6f02..72d6ce042edc2a02da2af948f90328439403fb23 100644 (file)
@@ -1,13 +1,17 @@
 /*
  * OMAP powerdomain control
  *
- * Copyright (C) 2007-2008, 2011 Texas Instruments, Inc.
+ * Copyright (C) 2007-2008, 2011-2012 Texas Instruments, Inc.
  * Copyright (C) 2007-2011 Nokia Corporation
  *
  * Written by Paul Walmsley
  * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com>
  * State counting code by Tero Kristo <tero.kristo@nokia.com>
  *
+ * Contains some code previously from mach-omap2/pm-debug.c, which was:
+ * Copyright (C) 2005 Texas Instruments, Inc.
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+
 #include <trace/events/power.h>
 
 #include "cm2xxx_3xxx.h"
 
 #define PWRDM_TRACE_STATES_FLAG        (1<<31)
 
-enum {
-       PWRDM_STATE_NOW = 0,
-       PWRDM_STATE_PREV,
-};
-
+/* Types of sleep_switch used in pwrdm_set_fpwrst() */
+#define ALREADYACTIVE_SWITCH           0
+#define FORCEWAKEUP_SWITCH             1
+#define LOWPOWERSTATE_SWITCH           2
+#define ERROR_SWITCH                   3
 
 /* pwrdm_list contains all registered struct powerdomains */
 static LIST_HEAD(pwrdm_list);
 
 static struct pwrdm_ops *arch_pwrdm;
 
+/*
+ * _fpwrst_names: human-readable functional powerstate names - should match
+ *    the enum pwrdm_func_state order and names
+ */
+static const char * const _fpwrst_names[] = {
+       "OFF", "OSWR", "CSWR", "INACTIVE", "ON"
+};
+
 /* Private functions */
 
 static struct powerdomain *_pwrdm_lookup(const char *name)
@@ -79,7 +95,7 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
        int i;
        struct voltagedomain *voltdm;
 
-       if (!pwrdm || !pwrdm->name)
+       if (!pwrdm->name)
                return -EINVAL;
 
        if (cpu_is_omap44xx() &&
@@ -101,101 +117,595 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
        pwrdm->voltdm.ptr = voltdm;
        INIT_LIST_HEAD(&pwrdm->voltdm_node);
        voltdm_add_pwrdm(voltdm, pwrdm);
+       spin_lock_init(&pwrdm->_lock);
 
        list_add(&pwrdm->node, &pwrdm_list);
 
        /* Initialize the powerdomain's state counter */
-       for (i = 0; i < PWRDM_MAX_PWRSTS; i++)
-               pwrdm->state_counter[i] = 0;
-
-       pwrdm->ret_logic_off_counter = 0;
-       for (i = 0; i < pwrdm->banks; i++)
-               pwrdm->ret_mem_off_counter[i] = 0;
-
-       pwrdm_wait_transition(pwrdm);
-       pwrdm->state = pwrdm_read_pwrst(pwrdm);
-       pwrdm->state_counter[pwrdm->state] = 1;
-
-       pr_debug("powerdomain: registered %s\n", pwrdm->name);
+       for (i = 0; i < PWRDM_FPWRSTS_COUNT; i++)
+               pwrdm->fpwrst_counter[i] = 0;
+
+       arch_pwrdm->pwrdm_wait_transition(pwrdm);
+       pwrdm->fpwrst = pwrdm_read_fpwrst(pwrdm);
+       pwrdm->fpwrst_counter[pwrdm->fpwrst - PWRDM_FPWRST_OFFSET] = 1;
+#ifdef CONFIG_PM_DEBUG
+       pwrdm->timer = sched_clock();
+#endif
 
        return 0;
 }
 
-static void _update_logic_membank_counters(struct powerdomain *pwrdm)
+/**
+ * _pwrdm_save_clkdm_state_and_activate - prepare for power state change
+ * @pwrdm: struct powerdomain * to operate on
+ * @pwrst: power state to switch to
+ * @hwsup: ptr to a bool to return whether the clkdm is hardware-supervised
+ *
+ * Determine whether the powerdomain needs to be turned on before
+ * attempting to switch power states.  Called by
+ * pwrdm_set_fpwrst().  NOTE that if the powerdomain contains
+ * multiple clockdomains, this code assumes that the first clockdomain
+ * supports software-supervised wakeup mode - potentially a problem.
+ * Returns the power state switch mode currently in use (see the
+ * "Types of sleep_switch" comment above).
+ */
+static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
+                                              u8 pwrst, bool *hwsup)
 {
-       int i;
-       u8 prev_logic_pwrst, prev_mem_pwrst;
+       int curr_pwrst;
+       u8 sleep_switch;
 
-       prev_logic_pwrst = pwrdm_read_prev_logic_pwrst(pwrdm);
-       if ((pwrdm->pwrsts_logic_ret == PWRSTS_OFF_RET) &&
-           (prev_logic_pwrst == PWRDM_POWER_OFF))
-               pwrdm->ret_logic_off_counter++;
+       curr_pwrst = arch_pwrdm->pwrdm_read_pwrst(pwrdm);
+       if (curr_pwrst < 0) {
+               WARN_ON(1);
+               sleep_switch = ERROR_SWITCH;
+       } else if (curr_pwrst < PWRDM_POWER_ON) {
+               if (curr_pwrst > pwrst &&
+                   pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE &&
+                   arch_pwrdm->pwrdm_set_lowpwrstchange) {
+                       sleep_switch = LOWPOWERSTATE_SWITCH;
+               } else {
+                       *hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
+                       clkdm_wakeup_nolock(pwrdm->pwrdm_clkdms[0]);
+                       sleep_switch = FORCEWAKEUP_SWITCH;
+               }
+       } else {
+               sleep_switch = ALREADYACTIVE_SWITCH;
+       }
 
-       for (i = 0; i < pwrdm->banks; i++) {
-               prev_mem_pwrst = pwrdm_read_prev_mem_pwrst(pwrdm, i);
+       return sleep_switch;
+}
 
-               if ((pwrdm->pwrsts_mem_ret[i] == PWRSTS_OFF_RET) &&
-                   (prev_mem_pwrst == PWRDM_POWER_OFF))
-                       pwrdm->ret_mem_off_counter[i]++;
+/**
+ * _pwrdm_restore_clkdm_state - restore the clkdm hwsup state after pwrst change
+ * @pwrdm: struct powerdomain * to operate on
+ * @sleep_switch: return value from _pwrdm_save_clkdm_state_and_activate()
+ * @hwsup: should @pwrdm's first clockdomain be set to hardware-supervised mode?
+ *
+ * Restore the clockdomain state perturbed by
+ * _pwrdm_save_clkdm_state_and_activate(), and call the power state
+ * bookkeeping code.  Called by pwrdm_set_fpwrst().  NOTE that if
+ * the powerdomain contains multiple clockdomains, this assumes that
+ * the first associated clockdomain supports either
+ * hardware-supervised idle control in the register, or
+ * software-supervised sleep.  No return value.
+ */
+static void _pwrdm_restore_clkdm_state(struct powerdomain *pwrdm,
+                                      u8 sleep_switch, bool hwsup)
+{
+       switch (sleep_switch) {
+       case FORCEWAKEUP_SWITCH:
+               if (hwsup)
+                       clkdm_allow_idle_nolock(pwrdm->pwrdm_clkdms[0]);
+               else
+                       clkdm_sleep_nolock(pwrdm->pwrdm_clkdms[0]);
+               break;
+       case LOWPOWERSTATE_SWITCH:
+               if (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE &&
+                   arch_pwrdm->pwrdm_set_lowpwrstchange)
+                       arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm);
+               pwrdm_state_switch_nolock(pwrdm);
+               break;
        }
 }
 
-static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
+/**
+ * _pwrdm_pwrst_is_controllable - can software change the powerdomain pwrst?
+ * @pwrdm: struct powerdomain * to test
+ *
+ * If the kernel can program the power state that the powerdomain
+ * @pwrdm should enter next, return 1; otherwise, return 0.
+ */
+static bool _pwrdm_pwrst_is_controllable(struct powerdomain *pwrdm)
 {
+       return (!pwrdm->pwrsts || pwrdm->pwrsts == PWRSTS_ON) ? 0 : 1;
+}
 
-       int prev, state, trace_state = 0;
+/**
+ * _pwrdm_pwrst_can_change - can the power state of @pwrdm change?
+ * @pwrdm: struct powerdomain * to test
+ *
+ * If the power state of the powerdomain represented by @pwrdm can
+ * change (i.e., is not always on), and the kernel has some way to
+ * detect this, return 1; otherwise, return 0.  XXX The current
+ * implementation of this is based on an assumption and has not been
+ * verified against all OMAPs.
+ */
+static bool _pwrdm_pwrst_can_change(struct powerdomain *pwrdm)
+{
+       return _pwrdm_pwrst_is_controllable(pwrdm);
+}
 
-       if (pwrdm == NULL)
-               return -EINVAL;
+/**
+ * _pwrdm_logic_retst_is_controllable - can software change the logic retst?
+ * @pwrdm: struct powerdomain * to test
+ *
+ * If the kernel can program the power state that the powerdomain
+ * @pwrdm logic should enter when the @pwrdm enters the RETENTION
+ * power state, return 1; otherwise, return 0.
+ */
+static bool _pwrdm_logic_retst_is_controllable(struct powerdomain *pwrdm)
+{
+       return (!pwrdm->pwrsts_logic_ret ||
+               pwrdm->pwrsts_logic_ret == PWRSTS_RET) ? 0 : 1;
+}
 
-       state = pwrdm_read_pwrst(pwrdm);
+/**
+ * _pwrdm_logic_retst_can_change - can the logic retst change on @pwrdm?
+ * @pwrdm: struct powerdomain * to test
+ *
+ * If the logic powerstate for the powerdomain represented by @pwrdm
+ * can ever be something other than the powerdomain's powerstate, and
+ * the kernel has some way to detect this, return 1; otherwise, return
+ * 0.  XXX The current implementation of this is based on an
+ * assumption and has not been verified against all OMAPs.
+ */
+static bool _pwrdm_logic_retst_can_change(struct powerdomain *pwrdm)
+{
+       return _pwrdm_logic_retst_is_controllable(pwrdm);
+}
 
-       switch (flag) {
-       case PWRDM_STATE_NOW:
-               prev = pwrdm->state;
+/**
+ * Search down then up for a valid state from a list of allowed
+ * states.  Used by states conversion functions (_pwrdm_fpwrst_to_*)
+ * to look for allowed power and logic states for a powerdomain.
+ * _pwrdm_fpwrst_to_pwrst - Convert functional (i.e. logical) to
+ * internal (i.e. registers) values for the power domains states.
+ * @pwrdm: struct powerdomain * to convert the values for
+ * @fpwrst: functional power state
+ * @pwrdm_pwrst: ptr to u8 to return the power state in
+ * @logic_retst: ptr to u8 to return the logic retention state in
+ *
+ * Returns the internal power state value for the power domain, or
+ * -EINVAL in case of invalid parameters passed in.
+ */
+static int _pwrdm_fpwrst_to_pwrst(struct powerdomain *pwrdm, u8 fpwrst,
+                                 u8 *pwrdm_pwrst, u8 *logic_retst)
+{
+       switch (fpwrst) {
+       case PWRDM_FUNC_PWRST_ON:
+               *pwrdm_pwrst = PWRDM_POWER_ON;
+               *logic_retst = PWRDM_POWER_RET;
+               break;
+       case PWRDM_FUNC_PWRST_INACTIVE:
+               *pwrdm_pwrst = PWRDM_POWER_INACTIVE;
+               *logic_retst = PWRDM_POWER_RET;
                break;
-       case PWRDM_STATE_PREV:
-               prev = pwrdm_read_prev_pwrst(pwrdm);
-               if (pwrdm->state != prev)
-                       pwrdm->state_counter[prev]++;
-               if (prev == PWRDM_POWER_RET)
-                       _update_logic_membank_counters(pwrdm);
+       case PWRDM_FUNC_PWRST_CSWR:
+               *pwrdm_pwrst = PWRDM_POWER_RET;
+               *logic_retst = PWRDM_POWER_RET;
+               break;
+       case PWRDM_FUNC_PWRST_OSWR:
+               *pwrdm_pwrst = PWRDM_POWER_RET;
+               *logic_retst = PWRDM_POWER_OFF;
+               break;
+       case PWRDM_FUNC_PWRST_OFF:
+               *pwrdm_pwrst = PWRDM_POWER_OFF;
                /*
-                * If the power domain did not hit the desired state,
-                * generate a trace event with both the desired and hit states
+                * logic_retst is set to PWRDM_POWER_RET in this case
+                * since the actual value does not matter, and because
+                * some powerdomains don't support a logic_retst of
+                * OFF.  XXX Maybe there's some way to indicate a
+                * 'don't care' value here?
                 */
-               if (state != prev) {
-                       trace_state = (PWRDM_TRACE_STATES_FLAG |
-                                      ((state & OMAP_POWERSTATE_MASK) << 8) |
-                                      ((prev & OMAP_POWERSTATE_MASK) << 0));
-                       trace_power_domain_target(pwrdm->name, trace_state,
-                                                 smp_processor_id());
-               }
+               *logic_retst = PWRDM_POWER_RET;
                break;
        default:
                return -EINVAL;
        }
 
-       if (state != prev)
-               pwrdm->state_counter[state]++;
+       pr_debug("powerdomain %s: convert fpwrst %0x to pwrst %0x\n",
+                pwrdm->name, fpwrst, *pwrdm_pwrst);
+
+       return 0;
+}
 
-       pm_dbg_update_time(pwrdm, prev);
+/**
+ * _pwrdm_pwrst_to_fpwrst - Convert internal (i.e. registers) to
+ * functional (i.e. logical) values for the power domains states.
+ * @pwrdm: struct powerdomain * to convert the values for
+ * @pwrst: internal powerdomain power state
+ * @logic: internal powerdomain logic power state
+ * @fpwrst: pointer to a u8 to store the corresponding functional power state to
+ *
+ * Returns the functional power state value for the power domain, or
+ * -EINVAL in case of invalid parameters passed in.  @pwrdm, @logic, and @pwrst
+ * are passed in, along with a pointer to the location to store the fpwrst to
+ * in @fpwrst.
+ */
+static int _pwrdm_pwrst_to_fpwrst(struct powerdomain *pwrdm, u8 pwrst, u8 logic,
+                                 u8 *fpwrst)
+{
+       switch (pwrst) {
+       case PWRDM_POWER_ON:
+               *fpwrst = PWRDM_FUNC_PWRST_ON;
+               break;
+       case PWRDM_POWER_INACTIVE:
+               *fpwrst = PWRDM_FUNC_PWRST_INACTIVE;
+               break;
+       case PWRDM_POWER_RET:
+               if (logic == PWRDM_POWER_OFF)
+                       *fpwrst = PWRDM_FUNC_PWRST_OSWR;
+               else if (logic == PWRDM_POWER_RET)
+                       *fpwrst = PWRDM_FUNC_PWRST_CSWR;
+               else
+                       return -EINVAL;
+               break;
+       case PWRDM_POWER_OFF:
+               *fpwrst = PWRDM_FUNC_PWRST_OFF;
+               break;
+       default:
+               return -EINVAL;
+       }
 
-       pwrdm->state = state;
+       pr_debug("powerdomain: convert pwrst (%0x,%0x) to fpwrst %0x\n",
+                pwrst, logic, *fpwrst);
 
        return 0;
 }
 
+/**
+ * _set_logic_retst_and_pwrdm_pwrst - program logic retst and pwrdm next pwrst
+ * @pwrdm: struct powerdomain * to program
+ * @logic: logic retention state to attempt to program
+ * @pwrst: powerdomain next-power-state to program
+ *
+ * Program the next-power-state and logic retention power state of the
+ * powerdomain represented by @pwrdm to @pwrst and @logic,
+ * respectively.  If the powerdomain next-power-state is not
+ * software-controllable, returns 0; otherwise, passes along the
+ * return value from pwrdm_set_logic_retst() if there is an error
+ * returned by that function, otherwise, passes along the return value
+ * from pwrdm_set_next_fpwrst()
+ */
+static int _set_logic_retst_and_pwrdm_pwrst(struct powerdomain *pwrdm,
+                                           u8 logic, u8 pwrst)
+{
+       int ret;
+
+       /*
+        * XXX Should return an error, but this means that our PM code
+        * will need to be much more careful about what it programs
+        */
+       if (!_pwrdm_pwrst_is_controllable(pwrdm))
+               return 0;
+
+       if (!(pwrdm->pwrsts & (1 << pwrst)))
+               return -EINVAL;
+
+       if (pwrdm->pwrsts_logic_ret && pwrst == PWRDM_POWER_RET) {
+               if (!(pwrdm->pwrsts_logic_ret & (1 << logic)))
+                       return -EINVAL;
+
+               ret = arch_pwrdm->pwrdm_set_logic_retst(pwrdm, logic);
+               if (ret) {
+                       pr_err("%s: unable to set logic state %0x of powerdomain: %s\n",
+                              __func__, logic, pwrdm->name);
+                       return ret;
+               }
+       }
+
+       ret = arch_pwrdm->pwrdm_set_next_pwrst(pwrdm, pwrst);
+       if (ret)
+               pr_err("%s: unable to set power state %0x of powerdomain: %s\n",
+                      __func__, pwrst, pwrdm->name);
+
+       return ret;
+}
+
+/**
+ * _pwrdm_read_next_fpwrst - get next powerdomain func power state (lockless)
+ * @pwrdm: struct powerdomain * to get power state
+ *
+ * Return the powerdomain @pwrdm's next functional power state.
+ * Caller must hold @pwrdm->_lock.  Returns -EINVAL if the powerdomain
+ * pointer is null or returns the next power state upon success.
+ */
+static int _pwrdm_read_next_fpwrst(struct powerdomain *pwrdm)
+{
+       int next_pwrst, next_logic, ret;
+       u8 fpwrst;
+
+       if (pwrdm->_flags & _PWRDM_NEXT_FPWRST_IS_VALID)
+               return pwrdm->next_fpwrst;
+
+       next_pwrst = arch_pwrdm->pwrdm_read_next_pwrst(pwrdm);
+       if (next_pwrst < 0)
+               return next_pwrst;
+
+       next_logic = next_pwrst;
+       if (_pwrdm_logic_retst_can_change(pwrdm) &&
+           arch_pwrdm->pwrdm_read_logic_pwrst) {
+               next_logic = arch_pwrdm->pwrdm_read_logic_pwrst(pwrdm);
+               if (next_logic < 0)
+                       return next_logic;
+       }
+       ret = _pwrdm_pwrst_to_fpwrst(pwrdm, next_pwrst, next_logic, &fpwrst);
+       if (!ret) {
+               pwrdm->next_fpwrst = fpwrst;
+               pwrdm->_flags |= _PWRDM_NEXT_FPWRST_IS_VALID;
+       }
+
+       return (ret) ? ret : fpwrst;
+}
+
+/**
+ * _pwrdm_read_fpwrst - get current func powerdomain power state (lockless)
+ * @pwrdm: struct powerdomain * to get current functional power state
+ *
+ * Return the powerdomain @pwrdm's current functional power state.
+ * Returns -EINVAL if the powerdomain pointer is null or returns the
+ * current power state upon success.
+ */
+static int _pwrdm_read_fpwrst(struct powerdomain *pwrdm)
+{
+       int pwrst, logic_pwrst, ret;
+       u8 fpwrst;
+
+       if (!_pwrdm_pwrst_can_change(pwrdm) ||
+           pwrdm->flags & PWRDM_ACTIVE_WITH_KERNEL)
+               return PWRDM_FUNC_PWRST_ON;
+
+       pwrst = arch_pwrdm->pwrdm_read_pwrst(pwrdm);
+       if (pwrst < 0)
+               return pwrst;
+
+       logic_pwrst = pwrst;
+       if (_pwrdm_logic_retst_can_change(pwrdm) &&
+           arch_pwrdm->pwrdm_read_logic_pwrst) {
+               logic_pwrst = arch_pwrdm->pwrdm_read_logic_pwrst(pwrdm);
+               if (logic_pwrst < 0)
+                       return logic_pwrst;
+       }
+
+       ret = _pwrdm_pwrst_to_fpwrst(pwrdm, pwrst, logic_pwrst, &fpwrst);
+
+       return (ret) ? ret : fpwrst;
+}
+
+/**
+ * _pwrdm_read_prev_fpwrst - get previous powerdomain func pwr state (lockless)
+ * @pwrdm: struct powerdomain * to get previous functional power state
+ *
+ * Return the powerdomain @pwrdm's previous functional power state.
+ * Returns -EINVAL if the powerdomain pointer is null or returns the
+ * previous functional power state upon success.
+ */
+static int _pwrdm_read_prev_fpwrst(struct powerdomain *pwrdm)
+{
+       int ret = -EINVAL;
+       int pwrst, logic_pwrst;
+       u8 fpwrst;
+
+       if (!_pwrdm_pwrst_can_change(pwrdm))
+               return PWRDM_FUNC_PWRST_ON;
+
+       if (pwrdm->_flags & _PWRDM_PREV_FPWRST_IS_VALID)
+               return pwrdm->prev_fpwrst;
+
+       pwrst = arch_pwrdm->pwrdm_read_prev_pwrst(pwrdm);
+       if (pwrst < 0)
+               return pwrst;
+
+       logic_pwrst = pwrst;
+       if (_pwrdm_logic_retst_can_change(pwrdm) &&
+           arch_pwrdm->pwrdm_read_prev_logic_pwrst) {
+               logic_pwrst = arch_pwrdm->pwrdm_read_prev_logic_pwrst(pwrdm);
+               if (logic_pwrst < 0)
+                       return logic_pwrst;
+       }
+
+       ret = _pwrdm_pwrst_to_fpwrst(pwrdm, pwrst, logic_pwrst, &fpwrst);
+       if (!ret) {
+               pwrdm->prev_fpwrst = fpwrst;
+               pwrdm->_flags |= _PWRDM_PREV_FPWRST_IS_VALID;
+       }
+
+       return (ret) ? ret : fpwrst;
+}
+
+/**
+ * _pwrdm_set_mem_onst - set memory power state while powerdomain ON
+ * @pwrdm: struct powerdomain * to set
+ * @bank: memory bank number to set (0-3)
+ * @pwrst: one of the PWRDM_POWER_* macros
+ *
+ * Set the next power state @pwrst that memory bank @bank of the
+ * powerdomain @pwrdm will enter when the powerdomain enters the ON
+ * state.  @bank will be a number from 0 to 3, and represents different
+ * types of memory, depending on the powerdomain.  Returns -EINVAL if
+ * the powerdomain pointer is null or the target power state is not
+ * not supported for this memory bank, -EEXIST if the target memory
+ * bank does not exist or is not controllable, or returns 0 upon
+ * success.
+ */
+static int _pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
+{
+       int ret = -EINVAL;
+
+       if (pwrdm->banks < (bank + 1))
+               return -EEXIST;
+
+       if (!(pwrdm->pwrsts_mem_on[bank] & (1 << pwrst)))
+               return -EINVAL;
+
+       pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-ON to %0x\n",
+                pwrdm->name, bank, pwrst);
+
+       if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_onst)
+               ret = arch_pwrdm->pwrdm_set_mem_onst(pwrdm, bank, pwrst);
+
+       return ret;
+}
+
+/**
+ * _pwrdm_set_mem_retst - set memory power state while powerdomain in RET
+ * @pwrdm: struct powerdomain * to set
+ * @bank: memory bank number to set (0-3)
+ * @pwrst: one of the PWRDM_POWER_* macros
+ *
+ * Set the next power state @pwrst that memory bank @bank of the
+ * powerdomain @pwrdm will enter when the powerdomain enters the
+ * RETENTION state.  Bank will be a number from 0 to 3, and represents
+ * different types of memory, depending on the powerdomain.  @pwrst
+ * will be either RETENTION or OFF, if supported.  Returns -EINVAL if
+ * the powerdomain pointer is null or the target power state is not
+ * not supported for this memory bank, -EEXIST if the target memory
+ * bank does not exist or is not controllable, or returns 0 upon
+ * success.
+ */
+static int _pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
+{
+       int ret = -EINVAL;
+
+       if (pwrdm->banks < (bank + 1))
+               return -EEXIST;
+
+       if (!(pwrdm->pwrsts_mem_ret[bank] & (1 << pwrst)))
+               return -EINVAL;
+
+       pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-RET to %0x\n",
+                pwrdm->name, bank, pwrst);
+
+       if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_retst)
+               ret = arch_pwrdm->pwrdm_set_mem_retst(pwrdm, bank, pwrst);
+
+       return ret;
+}
+
+/* XXX prev is wrong type */
+/* XXX is sched_clock() correct to use here? */
+/* Update timer for previous state */
+static void _pwrdm_update_pwrst_time(struct powerdomain *pwrdm, int prev)
+{
+#ifdef CONFIG_PM_DEBUG
+       s64 t;
+
+       t = sched_clock();
+
+       pwrdm->fpwrst_timer[prev - PWRDM_FPWRST_OFFSET] += t - pwrdm->timer;
+
+       pwrdm->timer = t;
+#endif
+}
+
+/**
+ * _pwrdm_state_switch - record powerdomain usage data; track power state
+ * (before powerdomain state transition)
+ * @pwrdm: struct powerdomain * to observe
+ *
+ * If the powerdomain @pwrdm's current power state is not what we last
+ * observed it to be, then increment the counter for that power state.
+ * This is used to track context loss events, and for debugging.  Also
+ * if CONFIG_PM_DEBUG=y, track the amount of time the powerdomain has
+ * spent in the current power state.  Caller must hold pwrdm->_lock.
+ * Intended to be called immediately before the powerdomain's power
+ * state is likely to change.  XXX Note that the counts and durations
+ * observed by this function may be inaccurate.  Powerdomains can
+ * transition power states automatically, without the kernel being
+ * involved -- for example, a device can DMA data from memory while
+ * the MPU is asleep.  This function does not attempt to account for
+ * that.  XXX It may be possible to skip this function completely if
+ * PM debugging is not needed and off-mode and OSWR is disabled (e.g.,
+ * no context loss events).  No return value.
+ */
+static void _pwrdm_state_switch(struct powerdomain *pwrdm)
+{
+       int fpwrst;
+
+       fpwrst = _pwrdm_read_fpwrst(pwrdm);
+       if (fpwrst != pwrdm->fpwrst)
+               pwrdm->fpwrst_counter[fpwrst - PWRDM_FPWRST_OFFSET]++;
+
+       _pwrdm_update_pwrst_time(pwrdm, pwrdm->fpwrst);
+
+       pwrdm->fpwrst = fpwrst;
+}
+
 static int _pwrdm_pre_transition_cb(struct powerdomain *pwrdm, void *unused)
 {
+       /*
+        * XXX It should be possible to avoid the clear_all_prev_pwrst
+        * call for powerdomains if we are programming them to stay on,
+        * for example.
+        */
        pwrdm_clear_all_prev_pwrst(pwrdm);
-       _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
+       _pwrdm_state_switch(pwrdm);
        return 0;
 }
 
+/**
+ * _pwrdm_post_transition_cb - record powerdomain usage data; track power state
+ * (after powerdomain power state transition)
+ * @pwrdm: struct powerdomain * to observe
+ *
+ * If the powerdomain @pwrdm's previous power state doesn't match our
+ * recollection of the powerdomain's current power state, then
+ * increment the counter for the previous power state.  And if the
+ * powerdomain's previous power state doesn't match the current power
+ * state, increment the counter for the current power state.  This
+ * function is used to track context loss events, and for debugging.
+ * Also if CONFIG_PM_DEBUG=y, track the approximate amount of time the
+ * powerdomain has spent in the previous power state.  Caller must
+ * hold pwrdm->_lock.  XXX Note that the counts and durations observed
+ * by this function may be inaccurate.  Powerdomains can transition
+ * power states automatically, without the kernel being involved --
+ * for example, a device can DMA data from memory while the MPU is
+ * asleep.  This function does not attempt to account for that.  XXX
+ * It may be possible to skip this function completely if PM debugging
+ * is not needed and off-mode and OSWR is disabled (e.g., no context
+ * loss events).  No return value.
+ */
 static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
 {
-       _pwrdm_state_switch(pwrdm, PWRDM_STATE_PREV);
+       int prev, fpwrst;
+       int trace_state = 0;
+
+       prev = (pwrdm->next_fpwrst == PWRDM_FUNC_PWRST_ON) ?
+               PWRDM_FUNC_PWRST_ON : _pwrdm_read_prev_fpwrst(pwrdm);
+
+       if (pwrdm->fpwrst != prev)
+               pwrdm->fpwrst_counter[prev - PWRDM_FPWRST_OFFSET]++;
+
+       _pwrdm_update_pwrst_time(pwrdm, prev);
+
+       /*
+        * If the power domain did not hit the desired state,
+        * generate a trace event with both the desired and hit states
+        */
+       if (pwrdm->next_fpwrst != prev) {
+               trace_state = (PWRDM_TRACE_STATES_FLAG |
+                              pwrdm->next_fpwrst << 8 | prev);
+               trace_power_domain_target(pwrdm->name, trace_state,
+                                         smp_processor_id());
+       }
+
+       fpwrst = _pwrdm_read_fpwrst(pwrdm);
+       if (fpwrst != prev)
+               pwrdm->fpwrst_counter[fpwrst - PWRDM_FPWRST_OFFSET]++;
+
+       pwrdm->fpwrst = fpwrst;
+
        return 0;
 }
 
@@ -207,9 +717,12 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
  *
  * Register the list of function pointers used to implement the
  * powerdomain functions on different OMAP SoCs.  Should be called
- * before any other pwrdm_register*() function.  Returns -EINVAL if
- * @po is null, -EEXIST if platform functions have already been
- * registered, or 0 upon success.
+ * before any other pwrdm_register*() function.  Several function
+ * pointers in @po are required to be non-null for the powerdomain
+ * code to function: pwrdm_wait_transition, pwrdm_read_next_pwrst,
+ * pwrdm_read_pwrst, pwrdm_set_next_pwrst, pwrdm_set_logic_retst, and
+ * pwrdm_read_prev_pwrst.  Returns -EINVAL if @po is null, -EEXIST if
+ * platform functions have already been registered, or 0 upon success.
  */
 int pwrdm_register_platform_funcs(struct pwrdm_ops *po)
 {
@@ -219,6 +732,11 @@ int pwrdm_register_platform_funcs(struct pwrdm_ops *po)
        if (arch_pwrdm)
                return -EEXIST;
 
+       if (!po->pwrdm_wait_transition || !po->pwrdm_read_next_pwrst ||
+           !po->pwrdm_read_pwrst || !po->pwrdm_set_next_pwrst ||
+           !po->pwrdm_set_logic_retst || !po->pwrdm_read_prev_pwrst)
+               return -ENOENT;
+
        arch_pwrdm = po;
 
        return 0;
@@ -255,25 +773,56 @@ int pwrdm_register_pwrdms(struct powerdomain **ps)
  *
  * Do whatever is necessary to initialize registered powerdomains and
  * powerdomain code.  Currently, this programs the next power state
- * for each powerdomain to ON.  This prevents powerdomains from
- * unexpectedly losing context or entering high wakeup latency modes
- * with non-power-management-enabled kernels.  Must be called after
- * pwrdm_register_pwrdms().  Returns -EACCES if called before
- * pwrdm_register_pwrdms(), or 0 upon success.
+ * for each powerdomain to ON, and programs the memory bank power
+ * states to follow the powerdomain power states.  This prevents
+ * powerdomains from unexpectedly losing context or entering high
+ * wakeup latency modes with non-power-management-enabled kernels.
+ * Must be called after pwrdm_register_pwrdms().  Returns -EACCES if
+ * called before pwrdm_register_pwrdms(), or 0 upon success.
  */
 int pwrdm_complete_init(void)
 {
        struct powerdomain *temp_p;
+       int i;
 
        if (list_empty(&pwrdm_list))
                return -EACCES;
 
-       list_for_each_entry(temp_p, &pwrdm_list, node)
-               pwrdm_set_next_pwrst(temp_p, PWRDM_POWER_ON);
+       list_for_each_entry(temp_p, &pwrdm_list, node) {
+               for (i = 0; i < temp_p->banks; i++) {
+                       _pwrdm_set_mem_onst(temp_p, i, PWRDM_POWER_ON);
+                       _pwrdm_set_mem_retst(temp_p, i, PWRDM_POWER_RET);
+               }
+               WARN_ON(pwrdm_set_next_fpwrst(temp_p, PWRDM_FUNC_PWRST_ON));
+       }
 
        return 0;
 }
 
+/**
+ * pwrdm_lock - acquire a Linux spinlock on a powerdomain
+ * @pwrdm: struct powerdomain * to lock
+ *
+ * Acquire the powerdomain spinlock on @pwrdm.  No return value.
+ */
+void pwrdm_lock(struct powerdomain *pwrdm)
+       __acquires(&pwrdm->_lock)
+{
+       spin_lock_irqsave(&pwrdm->_lock, pwrdm->_lock_flags);
+}
+
+/**
+ * pwrdm_unlock - release a Linux spinlock on a powerdomain
+ * @pwrdm: struct powerdomain * to unlock
+ *
+ * Release the powerdomain spinlock on @pwrdm.  No return value.
+ */
+void pwrdm_unlock(struct powerdomain *pwrdm)
+       __releases(&pwrdm->_lock)
+{
+       spin_unlock_irqrestore(&pwrdm->_lock, pwrdm->_lock_flags);
+}
+
 /**
  * pwrdm_lookup - look up a powerdomain by name, return a pointer
  * @name: name of powerdomain
@@ -450,631 +999,548 @@ struct voltagedomain *pwrdm_get_voltdm(struct powerdomain *pwrdm)
 }
 
 /**
- * pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain
- * @pwrdm: struct powerdomain *
- *
- * Return the number of controllable memory banks in powerdomain @pwrdm,
- * starting with 1.  Returns -EINVAL if the powerdomain pointer is null.
- */
-int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm)
-{
-       if (!pwrdm)
-               return -EINVAL;
-
-       return pwrdm->banks;
-}
-
-/**
- * pwrdm_set_next_pwrst - set next powerdomain power state
- * @pwrdm: struct powerdomain * to set
- * @pwrst: one of the PWRDM_POWER_* macros
+ * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm
+ * @pwrdm: struct powerdomain * to clear
  *
- * Set the powerdomain @pwrdm's next power state to @pwrst.  The powerdomain
- * may not enter this state immediately if the preconditions for this state
- * have not been satisfied.  Returns -EINVAL if the powerdomain pointer is
- * null or if the power state is invalid for the powerdomin, or returns 0
- * upon success.
+ * Clear the powerdomain's previous power state register @pwrdm.
+ * Clears the entire register, including logic and memory bank
+ * previous power states.  Returns -EINVAL if the powerdomain pointer
+ * is null, or returns 0 upon success.
  */
-int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst)
+int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm)
 {
        int ret = -EINVAL;
 
        if (!pwrdm)
-               return -EINVAL;
-
-       if (!(pwrdm->pwrsts & (1 << pwrst)))
-               return -EINVAL;
-
-       pr_debug("powerdomain: %s: setting next powerstate to %0x\n",
-                pwrdm->name, pwrst);
+               return ret;
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_set_next_pwrst) {
-               /* Trace the pwrdm desired target state */
-               trace_power_domain_target(pwrdm->name, pwrst,
-                                         smp_processor_id());
-               /* Program the pwrdm desired target state */
-               ret = arch_pwrdm->pwrdm_set_next_pwrst(pwrdm, pwrst);
-       }
+       /*
+        * XXX Is there some way for us to skip powerdomains that
+        * don't have a prev pwrst register?
+        */
 
-       return ret;
-}
+       /*
+        * XXX should get the powerdomain's current state here;
+        * warn & fail if it is not ON.
+        */
 
-/**
- * pwrdm_read_next_pwrst - get next powerdomain power state
- * @pwrdm: struct powerdomain * to get power state
- *
- * Return the powerdomain @pwrdm's next power state.  Returns -EINVAL
- * if the powerdomain pointer is null or returns the next power state
- * upon success.
- */
-int pwrdm_read_next_pwrst(struct powerdomain *pwrdm)
-{
-       int ret = -EINVAL;
+       pr_debug("powerdomain: %s: clearing previous power state reg\n",
+                pwrdm->name);
 
-       if (!pwrdm)
-               return -EINVAL;
+       pwrdm->_flags &= ~_PWRDM_PREV_FPWRST_IS_VALID;
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_read_next_pwrst)
-               ret = arch_pwrdm->pwrdm_read_next_pwrst(pwrdm);
+       if (arch_pwrdm && arch_pwrdm->pwrdm_clear_all_prev_pwrst)
+               ret = arch_pwrdm->pwrdm_clear_all_prev_pwrst(pwrdm);
 
        return ret;
 }
 
 /**
- * pwrdm_read_pwrst - get current powerdomain power state
- * @pwrdm: struct powerdomain * to get power state
+ * pwrdm_enable_hdwr_sar - enable automatic hardware SAR for a pwrdm
+ * @pwrdm: struct powerdomain *
  *
- * Return the powerdomain @pwrdm's current power state.        Returns -EINVAL
- * if the powerdomain pointer is null or returns the current power state
- * upon success. Note that if the power domain only supports the ON state
- * then just return ON as the current state.
+ * Enable automatic context save-and-restore upon power state change
+ * for some devices in the powerdomain @pwrdm.  Warning: this only
+ * affects a subset of devices in a powerdomain; check the TRM
+ * closely.  Returns -EINVAL if the powerdomain pointer is null or if
+ * the powerdomain does not support automatic save-and-restore, or
+ * returns 0 upon success.
  */
-int pwrdm_read_pwrst(struct powerdomain *pwrdm)
+int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm)
 {
        int ret = -EINVAL;
 
        if (!pwrdm)
-               return -EINVAL;
+               return ret;
+
+       if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
+               return ret;
 
-       if (pwrdm->pwrsts == PWRSTS_ON)
-               return PWRDM_POWER_ON;
+       pr_debug("powerdomain: %s: setting SAVEANDRESTORE bit\n", pwrdm->name);
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_read_pwrst)
-               ret = arch_pwrdm->pwrdm_read_pwrst(pwrdm);
+       if (arch_pwrdm && arch_pwrdm->pwrdm_enable_hdwr_sar)
+               ret = arch_pwrdm->pwrdm_enable_hdwr_sar(pwrdm);
 
        return ret;
 }
 
 /**
- * pwrdm_read_prev_pwrst - get previous powerdomain power state
- * @pwrdm: struct powerdomain * to get previous power state
+ * pwrdm_disable_hdwr_sar - disable automatic hardware SAR for a pwrdm
+ * @pwrdm: struct powerdomain *
  *
- * Return the powerdomain @pwrdm's previous power state.  Returns -EINVAL
- * if the powerdomain pointer is null or returns the previous power state
- * upon success.
+ * Disable automatic context save-and-restore upon power state change
+ * for some devices in the powerdomain @pwrdm.  Warning: this only
+ * affects a subset of devices in a powerdomain; check the TRM
+ * closely.  Returns -EINVAL if the powerdomain pointer is null or if
+ * the powerdomain does not support automatic save-and-restore, or
+ * returns 0 upon success.
  */
-int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm)
+int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm)
 {
        int ret = -EINVAL;
 
        if (!pwrdm)
-               return -EINVAL;
+               return ret;
+
+       if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
+               return ret;
+
+       pr_debug("powerdomain: %s: clearing SAVEANDRESTORE bit\n", pwrdm->name);
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_pwrst)
-               ret = arch_pwrdm->pwrdm_read_prev_pwrst(pwrdm);
+       if (arch_pwrdm && arch_pwrdm->pwrdm_disable_hdwr_sar)
+               ret = arch_pwrdm->pwrdm_disable_hdwr_sar(pwrdm);
 
        return ret;
 }
 
 /**
- * pwrdm_set_logic_retst - set powerdomain logic power state upon retention
- * @pwrdm: struct powerdomain * to set
- * @pwrst: one of the PWRDM_POWER_* macros
+ * pwrdm_has_hdwr_sar - test whether powerdomain supports hardware SAR
+ * @pwrdm: struct powerdomain *
  *
- * Set the next power state @pwrst that the logic portion of the
- * powerdomain @pwrdm will enter when the powerdomain enters retention.
- * This will be either RETENTION or OFF, if supported.  Returns
- * -EINVAL if the powerdomain pointer is null or the target power
- * state is not not supported, or returns 0 upon success.
+ * Returns 1 if powerdomain @pwrdm supports hardware save-and-restore
+ * for some devices, or 0 if it does not.
  */
-int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst)
+bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm)
 {
-       int ret = -EINVAL;
+       return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0;
+}
 
-       if (!pwrdm)
-               return -EINVAL;
+int pwrdm_state_switch_nolock(struct powerdomain *pwrdm)
+{
+       int ret = 0;
 
-       if (!(pwrdm->pwrsts_logic_ret & (1 << pwrst)))
+       if (!pwrdm || !arch_pwrdm)
                return -EINVAL;
 
-       pr_debug("powerdomain: %s: setting next logic powerstate to %0x\n",
-                pwrdm->name, pwrst);
+       if (!(pwrdm->flags & PWRDM_ACTIVE_WITH_KERNEL))
+               ret = arch_pwrdm->pwrdm_wait_transition(pwrdm);
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_set_logic_retst)
-               ret = arch_pwrdm->pwrdm_set_logic_retst(pwrdm, pwrst);
+       if (!ret)
+               _pwrdm_state_switch(pwrdm);
 
        return ret;
 }
 
-/**
- * pwrdm_set_mem_onst - set memory power state while powerdomain ON
- * @pwrdm: struct powerdomain * to set
- * @bank: memory bank number to set (0-3)
- * @pwrst: one of the PWRDM_POWER_* macros
- *
- * Set the next power state @pwrst that memory bank @bank of the
- * powerdomain @pwrdm will enter when the powerdomain enters the ON
- * state.  @bank will be a number from 0 to 3, and represents different
- * types of memory, depending on the powerdomain.  Returns -EINVAL if
- * the powerdomain pointer is null or the target power state is not
- * not supported for this memory bank, -EEXIST if the target memory
- * bank does not exist or is not controllable, or returns 0 upon
- * success.
- */
-int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
+int __deprecated pwrdm_state_switch(struct powerdomain *pwrdm)
 {
-       int ret = -EINVAL;
+       int ret;
 
-       if (!pwrdm)
-               return -EINVAL;
+       pwrdm_lock(pwrdm);
+       ret = pwrdm_state_switch_nolock(pwrdm);
+       pwrdm_unlock(pwrdm);
 
-       if (pwrdm->banks < (bank + 1))
-               return -EEXIST;
+       return ret;
+}
 
-       if (!(pwrdm->pwrsts_mem_on[bank] & (1 << pwrst)))
-               return -EINVAL;
+int pwrdm_pre_transition(struct powerdomain *pwrdm)
+{
+       if (pwrdm)
+               _pwrdm_pre_transition_cb(pwrdm, NULL);
+       else
+               pwrdm_for_each(_pwrdm_pre_transition_cb, NULL);
 
-       pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-ON to %0x\n",
-                pwrdm->name, bank, pwrst);
+       return 0;
+}
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_onst)
-               ret = arch_pwrdm->pwrdm_set_mem_onst(pwrdm, bank, pwrst);
+int pwrdm_post_transition(struct powerdomain *pwrdm)
+{
+       if (pwrdm)
+               _pwrdm_post_transition_cb(pwrdm, NULL);
+       else
+               pwrdm_for_each(_pwrdm_post_transition_cb, NULL);
 
-       return ret;
+       return 0;
 }
 
 /**
- * pwrdm_set_mem_retst - set memory power state while powerdomain in RET
- * @pwrdm: struct powerdomain * to set
- * @bank: memory bank number to set (0-3)
- * @pwrst: one of the PWRDM_POWER_* macros
+ * pwrdm_get_context_loss_count - get powerdomain's context loss count
+ * @pwrdm: struct powerdomain * to wait for
  *
- * Set the next power state @pwrst that memory bank @bank of the
- * powerdomain @pwrdm will enter when the powerdomain enters the
- * RETENTION state.  Bank will be a number from 0 to 3, and represents
- * different types of memory, depending on the powerdomain.  @pwrst
- * will be either RETENTION or OFF, if supported.  Returns -EINVAL if
- * the powerdomain pointer is null or the target power state is not
- * not supported for this memory bank, -EEXIST if the target memory
- * bank does not exist or is not controllable, or returns 0 upon
- * success.
+ * Context loss count is the sum of powerdomain off-mode counter, the
+ * logic off counter and the per-bank memory off counter.  Returns negative
+ * (and WARNs) upon error, otherwise, returns the context loss count.
  */
-int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
+int pwrdm_get_context_loss_count(struct powerdomain *pwrdm)
 {
-       int ret = -EINVAL;
-
-       if (!pwrdm)
-               return -EINVAL;
+       int count;
 
-       if (pwrdm->banks < (bank + 1))
-               return -EEXIST;
+       if (!pwrdm) {
+               WARN(1, "powerdomain: %s: pwrdm is null\n", __func__);
+               return -ENODEV;
+       }
 
-       if (!(pwrdm->pwrsts_mem_ret[bank] & (1 << pwrst)))
-               return -EINVAL;
+       count = pwrdm->fpwrst_counter[PWRDM_FUNC_PWRST_OFF -
+                                     PWRDM_FPWRST_OFFSET];
+       count += pwrdm->fpwrst_counter[PWRDM_FUNC_PWRST_OSWR -
+                                      PWRDM_FPWRST_OFFSET];
 
-       pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-RET to %0x\n",
-                pwrdm->name, bank, pwrst);
+       /*
+        * Context loss count has to be a non-negative value. Clear the sign
+        * bit to get a value range from 0 to INT_MAX.
+        */
+       count &= INT_MAX;
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_retst)
-               ret = arch_pwrdm->pwrdm_set_mem_retst(pwrdm, bank, pwrst);
+       pr_debug("powerdomain: %s: context loss count = %d\n",
+                pwrdm->name, count);
 
-       return ret;
+       return count;
 }
 
 /**
- * pwrdm_read_logic_pwrst - get current powerdomain logic retention power state
- * @pwrdm: struct powerdomain * to get current logic retention power state
+ * pwrdm_can_ever_lose_context - can this powerdomain ever lose context?
+ * @pwrdm: struct powerdomain *
  *
- * Return the power state that the logic portion of powerdomain @pwrdm
- * will enter when the powerdomain enters retention.  Returns -EINVAL
- * if the powerdomain pointer is null or returns the logic retention
- * power state upon success.
+ * Given a struct powerdomain * @pwrdm, returns 1 if the powerdomain
+ * can lose either memory or logic context or if @pwrdm is invalid, or
+ * returns 0 otherwise.  This function is not concerned with how the
+ * powerdomain registers are programmed (i.e., to go off or not); it's
+ * concerned with whether it's ever possible for this powerdomain to
+ * go off while some other part of the chip is active.  This function
+ * assumes that every powerdomain can go to either ON or INACTIVE.
  */
-int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm)
+bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm)
 {
-       int ret = -EINVAL;
-
-       if (!pwrdm)
-               return -EINVAL;
+       int i;
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_pwrst)
-               ret = arch_pwrdm->pwrdm_read_logic_pwrst(pwrdm);
+       if (IS_ERR_OR_NULL(pwrdm)) {
+               pr_debug("powerdomain: %s: invalid powerdomain pointer\n",
+                        __func__);
+               return 1;
+       }
 
-       return ret;
-}
+       if (pwrdm->pwrsts & PWRSTS_OFF)
+               return 1;
 
-/**
- * pwrdm_read_prev_logic_pwrst - get previous powerdomain logic power state
- * @pwrdm: struct powerdomain * to get previous logic power state
- *
- * Return the powerdomain @pwrdm's previous logic power state.  Returns
- * -EINVAL if the powerdomain pointer is null or returns the previous
- * logic power state upon success.
- */
-int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm)
-{
-       int ret = -EINVAL;
+       if (pwrdm->pwrsts & PWRSTS_RET) {
+               if (pwrdm->pwrsts_logic_ret & PWRSTS_OFF)
+                       return 1;
 
-       if (!pwrdm)
-               return -EINVAL;
+               for (i = 0; i < pwrdm->banks; i++)
+                       if (pwrdm->pwrsts_mem_ret[i] & PWRSTS_OFF)
+                               return 1;
+       }
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_logic_pwrst)
-               ret = arch_pwrdm->pwrdm_read_prev_logic_pwrst(pwrdm);
+       for (i = 0; i < pwrdm->banks; i++)
+               if (pwrdm->pwrsts_mem_on[i] & PWRSTS_OFF)
+                       return 1;
 
-       return ret;
+       return 0;
 }
 
+/* Public functions for functional power state handling */
+
 /**
- * pwrdm_read_logic_retst - get next powerdomain logic power state
- * @pwrdm: struct powerdomain * to get next logic power state
+ * pwrdm_convert_fpwrst_to_name - return the name of a functional power state
+ * @fpwrst: functional power state to return the name of
  *
- * Return the powerdomain pwrdm's logic power state.  Returns -EINVAL
- * if the powerdomain pointer is null or returns the next logic
- * power state upon success.
+ * Return a pointer to a string with the human-readable name of the
+ * functional power state (e.g., "ON", "CSWR", etc.)  Intended for use
+ * in debugging.  Returns NULL if @fpwrst is outside the range of the
+ * known functional power states.
  */
-int pwrdm_read_logic_retst(struct powerdomain *pwrdm)
+const char *pwrdm_convert_fpwrst_to_name(u8 fpwrst)
 {
-       int ret = -EINVAL;
-
-       if (!pwrdm)
-               return -EINVAL;
-
-       if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_retst)
-               ret = arch_pwrdm->pwrdm_read_logic_retst(pwrdm);
+       if (fpwrst < PWRDM_FPWRST_OFFSET || fpwrst >= PWRDM_MAX_FUNC_PWRSTS)
+               return NULL;
 
-       return ret;
+       return _fpwrst_names[fpwrst - PWRDM_FPWRST_OFFSET];
 }
 
 /**
- * pwrdm_read_mem_pwrst - get current memory bank power state
- * @pwrdm: struct powerdomain * to get current memory bank power state
- * @bank: memory bank number (0-3)
+ * pwrdm_set_next_fpwrst - set next powerdomain functional power state
+ * @pwrdm: struct powerdomain * to set
+ * @fpwrst: one of the PWRDM_POWER_* macros
  *
- * Return the powerdomain @pwrdm's current memory power state for bank
- * @bank.  Returns -EINVAL if the powerdomain pointer is null, -EEXIST if
- * the target memory bank does not exist or is not controllable, or
- * returns the current memory power state upon success.
+ * Set the powerdomain @pwrdm's next power state to @fpwrst.  The
+ * powerdomain may not enter this state immediately if the
+ * preconditions for this state have not been satisfied.  Returns
+ * -EINVAL if the powerdomain pointer is null or if the power state is
+ * invalid for the powerdomin, or returns 0 upon success.
  */
-int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
+int pwrdm_set_next_fpwrst(struct powerdomain *pwrdm, u8 fpwrst)
 {
-       int ret = -EINVAL;
-
-       if (!pwrdm)
-               return ret;
-
-       if (pwrdm->banks < (bank + 1))
-               return ret;
-
-       if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK)
-               bank = 1;
-
-       if (arch_pwrdm && arch_pwrdm->pwrdm_read_mem_pwrst)
-               ret = arch_pwrdm->pwrdm_read_mem_pwrst(pwrdm, bank);
+       u8 pwrst, logic;
+       int ret;
 
-       return ret;
-}
+       if (!pwrdm || IS_ERR(pwrdm))
+               return -EINVAL;
 
-/**
- * pwrdm_read_prev_mem_pwrst - get previous memory bank power state
- * @pwrdm: struct powerdomain * to get previous memory bank power state
- * @bank: memory bank number (0-3)
- *
- * Return the powerdomain @pwrdm's previous memory power state for
- * bank @bank.  Returns -EINVAL if the powerdomain pointer is null,
- * -EEXIST if the target memory bank does not exist or is not
- * controllable, or returns the previous memory power state upon
- * success.
- */
-int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
-{
-       int ret = -EINVAL;
+       /*
+        * XXX Should return an error, but this means that our PM code
+        * will need to be much more careful about what it programs
+        */
+       if (!_pwrdm_pwrst_is_controllable(pwrdm))
+               return 0;
 
-       if (!pwrdm)
+       ret = _pwrdm_fpwrst_to_pwrst(pwrdm, fpwrst, &pwrst, &logic);
+       if (ret)
                return ret;
 
-       if (pwrdm->banks < (bank + 1))
-               return ret;
+       if (pwrdm->_flags & _PWRDM_NEXT_FPWRST_IS_VALID &&
+           pwrdm->next_fpwrst == fpwrst)
+               return 0;
 
-       if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK)
-               bank = 1;
+       pr_debug("%s: set fpwrst %0x to pwrdm %s\n", __func__, fpwrst,
+                pwrdm->name);
+
+       /* Trace the pwrdm desired target state */
+       trace_power_domain_target(pwrdm->name, fpwrst, smp_processor_id());
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_mem_pwrst)
-               ret = arch_pwrdm->pwrdm_read_prev_mem_pwrst(pwrdm, bank);
+       pwrdm_lock(pwrdm);
+       ret = _set_logic_retst_and_pwrdm_pwrst(pwrdm, logic, pwrst);
+       if (!ret) {
+               pwrdm->next_fpwrst = fpwrst;
+               pwrdm->_flags |= _PWRDM_NEXT_FPWRST_IS_VALID;
+       }
+       pwrdm_unlock(pwrdm);
 
        return ret;
 }
 
 /**
- * pwrdm_read_mem_retst - get next memory bank power state
- * @pwrdm: struct powerdomain * to get mext memory bank power state
- * @bank: memory bank number (0-3)
+ * pwrdm_read_next_fpwrst - get next powerdomain functional power state
+ * @pwrdm: struct powerdomain * to get power state
  *
- * Return the powerdomain pwrdm's next memory power state for bank
- * x.  Returns -EINVAL if the powerdomain pointer is null, -EEXIST if
- * the target memory bank does not exist or is not controllable, or
- * returns the next memory power state upon success.
+ * Return the powerdomain @pwrdm's next functional power state.
+ * Returns -EINVAL if the powerdomain pointer is null or returns
+ * the next power state upon success.
  */
-int pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank)
+int pwrdm_read_next_fpwrst(struct powerdomain *pwrdm)
 {
-       int ret = -EINVAL;
+       int ret;
 
        if (!pwrdm)
-               return ret;
-
-       if (pwrdm->banks < (bank + 1))
-               return ret;
+               return -EINVAL;
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_read_mem_retst)
-               ret = arch_pwrdm->pwrdm_read_mem_retst(pwrdm, bank);
+       pwrdm_lock(pwrdm);
+       ret = _pwrdm_read_next_fpwrst(pwrdm);
+       pwrdm_unlock(pwrdm);
 
        return ret;
 }
 
 /**
- * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm
- * @pwrdm: struct powerdomain * to clear
+ * pwrdm_set_fpwrst - program next powerdomain functional power state
+ * @pwrdm: struct powerdomain * to set
+ * @fpwrst: power domain functional state, one of the PWRDM_FUNC_* macros
  *
- * Clear the powerdomain's previous power state register @pwrdm.
- * Clears the entire register, including logic and memory bank
- * previous power states.  Returns -EINVAL if the powerdomain pointer
- * is null, or returns 0 upon success.
+ * This programs the pwrdm next functional state, sets the dependencies
+ * and waits for the state to be applied.
  */
-int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm)
+int pwrdm_set_fpwrst(struct powerdomain *pwrdm, enum pwrdm_func_state fpwrst)
 {
-       int ret = -EINVAL;
+       u8 next_fpwrst, pwrst, logic, sleep_switch;
+       int ret = 0;
+       bool hwsup = false;
 
-       if (!pwrdm)
-               return ret;
+       if (!pwrdm || IS_ERR(pwrdm) || !arch_pwrdm)
+               return -EINVAL;
 
        /*
-        * XXX should get the powerdomain's current state here;
-        * warn & fail if it is not ON.
+        * XXX Should return an error, but this means that our PM code
+        * will need to be much more careful about what it programs
         */
+       if (!_pwrdm_pwrst_is_controllable(pwrdm))
+               return 0;
 
-       pr_debug("powerdomain: %s: clearing previous power state reg\n",
-                pwrdm->name);
+       ret = _pwrdm_fpwrst_to_pwrst(pwrdm, fpwrst, &pwrst, &logic);
+       if (ret)
+               return -EINVAL;
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_clear_all_prev_pwrst)
-               ret = arch_pwrdm->pwrdm_clear_all_prev_pwrst(pwrdm);
+       pr_debug("%s: pwrdm %s: set fpwrst %0x\n", __func__, pwrdm->name,
+                fpwrst);
 
-       return ret;
-}
+       pwrdm_lock(pwrdm);
 
-/**
- * pwrdm_enable_hdwr_sar - enable automatic hardware SAR for a pwrdm
- * @pwrdm: struct powerdomain *
- *
- * Enable automatic context save-and-restore upon power state change
- * for some devices in the powerdomain @pwrdm.  Warning: this only
- * affects a subset of devices in a powerdomain; check the TRM
- * closely.  Returns -EINVAL if the powerdomain pointer is null or if
- * the powerdomain does not support automatic save-and-restore, or
- * returns 0 upon success.
- */
-int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm)
-{
-       int ret = -EINVAL;
+       next_fpwrst = _pwrdm_read_next_fpwrst(pwrdm);
+       if (next_fpwrst == fpwrst)
+               goto psf_out;
 
-       if (!pwrdm)
-               return ret;
+       /* Trace the pwrdm desired target state */
+       trace_power_domain_target(pwrdm->name, next_fpwrst,
+                                 smp_processor_id());
 
-       if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
-               return ret;
+       sleep_switch = _pwrdm_save_clkdm_state_and_activate(pwrdm, fpwrst,
+                                                           &hwsup);
+       if (sleep_switch == ERROR_SWITCH) {
+               ret = -EINVAL;
+               goto psf_out;
+       }
 
-       pr_debug("powerdomain: %s: setting SAVEANDRESTORE bit\n", pwrdm->name);
+       ret = _set_logic_retst_and_pwrdm_pwrst(pwrdm, logic, pwrst);
+       if (ret) {
+               pr_err("%s: unable to set power state of powerdomain: %s\n",
+                      __func__, pwrdm->name);
+       } else {
+               pwrdm->next_fpwrst = fpwrst;
+               pwrdm->_flags |= _PWRDM_NEXT_FPWRST_IS_VALID;
+       }
 
-       if (arch_pwrdm && arch_pwrdm->pwrdm_enable_hdwr_sar)
-               ret = arch_pwrdm->pwrdm_enable_hdwr_sar(pwrdm);
+       _pwrdm_restore_clkdm_state(pwrdm, sleep_switch, hwsup);
+
+psf_out:
+       pwrdm_unlock(pwrdm);
 
        return ret;
 }
 
 /**
- * pwrdm_disable_hdwr_sar - disable automatic hardware SAR for a pwrdm
- * @pwrdm: struct powerdomain *
+ * pwrdm_read_fpwrst - get current functional powerdomain power state
+ * @pwrdm: struct powerdomain * to get current functional power state
  *
- * Disable automatic context save-and-restore upon power state change
- * for some devices in the powerdomain @pwrdm.  Warning: this only
- * affects a subset of devices in a powerdomain; check the TRM
- * closely.  Returns -EINVAL if the powerdomain pointer is null or if
- * the powerdomain does not support automatic save-and-restore, or
- * returns 0 upon success.
+ * Return the powerdomain @pwrdm's current functional power state.
+ * Returns -EINVAL if the powerdomain pointer is null or returns the
+ * current power state upon success.
  */
-int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm)
+int pwrdm_read_fpwrst(struct powerdomain *pwrdm)
 {
-       int ret = -EINVAL;
+       int ret;
 
-       if (!pwrdm)
-               return ret;
+       if (!pwrdm || !arch_pwrdm)
+               return -EINVAL;
 
-       if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
-               return ret;
+       if (!_pwrdm_pwrst_can_change(pwrdm))
+               return PWRDM_FUNC_PWRST_ON;
 
-       pr_debug("powerdomain: %s: clearing SAVEANDRESTORE bit\n", pwrdm->name);
-
-       if (arch_pwrdm && arch_pwrdm->pwrdm_disable_hdwr_sar)
-               ret = arch_pwrdm->pwrdm_disable_hdwr_sar(pwrdm);
+       pwrdm_lock(pwrdm);
+       ret = _pwrdm_read_fpwrst(pwrdm);
+       pwrdm_unlock(pwrdm);
 
        return ret;
 }
 
 /**
- * pwrdm_has_hdwr_sar - test whether powerdomain supports hardware SAR
- * @pwrdm: struct powerdomain *
+ * pwrdm_read_prev_fpwrst - get previous powerdomain functional power state
+ * @pwrdm: struct powerdomain * to get previous functional power state
  *
- * Returns 1 if powerdomain @pwrdm supports hardware save-and-restore
- * for some devices, or 0 if it does not.
+ * Return the powerdomain @pwrdm's previous functional power state.
+ * Returns -EINVAL if the powerdomain pointer is null or returns the
+ * previous functional power state upon success.
  */
-bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm)
-{
-       return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0;
-}
-
-/**
- * pwrdm_set_lowpwrstchange - Request a low power state change
- * @pwrdm: struct powerdomain *
- *
- * Allows a powerdomain to transtion to a lower power sleep state
- * from an existing sleep state without waking up the powerdomain.
- * Returns -EINVAL if the powerdomain pointer is null or if the
- * powerdomain does not support LOWPOWERSTATECHANGE, or returns 0
- * upon success.
- */
-int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm)
+int pwrdm_read_prev_fpwrst(struct powerdomain *pwrdm)
 {
        int ret = -EINVAL;
 
-       if (!pwrdm)
+       if (!pwrdm || !arch_pwrdm)
                return -EINVAL;
 
-       if (!(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE))
-               return -EINVAL;
+       if (!_pwrdm_pwrst_can_change(pwrdm))
+               return PWRDM_FUNC_PWRST_ON;
 
-       pr_debug("powerdomain: %s: setting LOWPOWERSTATECHANGE bit\n",
-                pwrdm->name);
-
-       if (arch_pwrdm && arch_pwrdm->pwrdm_set_lowpwrstchange)
-               ret = arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm);
+       pwrdm_lock(pwrdm);
+       ret = _pwrdm_read_prev_fpwrst(pwrdm);
+       pwrdm_unlock(pwrdm);
 
        return ret;
 }
 
 /**
- * pwrdm_wait_transition - wait for powerdomain power transition to finish
- * @pwrdm: struct powerdomain * to wait for
+ * pwrdm_supports_fpwrst - does the powerdomain @pwrdm support the @fpwrst power
+ * state?
+ * @pwrdm: struct powerdomain * pointing to a powerdomain to test
+ * @fpwrst: functional power state
  *
- * If the powerdomain @pwrdm is in the process of a state transition,
- * spin until it completes the power transition, or until an iteration
- * bailout value is reached. Returns -EINVAL if the powerdomain
- * pointer is null, -EAGAIN if the bailout value was reached, or
- * returns 0 upon success.
+ * Returns true if the powerdomain pointed to by @pwrdm can enter the
+ * functional power state @fpwrst, or false if not.
  */
-int pwrdm_wait_transition(struct powerdomain *pwrdm)
-{
-       int ret = -EINVAL;
-
-       if (!pwrdm)
-               return -EINVAL;
-
-       if (arch_pwrdm && arch_pwrdm->pwrdm_wait_transition)
-               ret = arch_pwrdm->pwrdm_wait_transition(pwrdm);
-
-       return ret;
-}
-
-int pwrdm_state_switch(struct powerdomain *pwrdm)
+bool pwrdm_supports_fpwrst(struct powerdomain *pwrdm, u8 fpwrst)
 {
+       u8 pwrst, logic;
        int ret;
 
-       ret = pwrdm_wait_transition(pwrdm);
-       if (!ret)
-               ret = _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
+       if (!pwrdm || IS_ERR(pwrdm))
+               return false;
 
-       return ret;
-}
-
-int pwrdm_pre_transition(struct powerdomain *pwrdm)
-{
-       if (pwrdm)
-               _pwrdm_pre_transition_cb(pwrdm, NULL);
-       else
-               pwrdm_for_each(_pwrdm_pre_transition_cb, NULL);
+       ret = _pwrdm_fpwrst_to_pwrst(pwrdm, fpwrst, &pwrst, &logic);
+       if (ret)
+               return false;
 
-       return 0;
-}
+       if (pwrdm->pwrsts_logic_ret && pwrst == PWRDM_POWER_RET &&
+           !(pwrdm->pwrsts_logic_ret & (1 << logic)))
+               return false;
 
-int pwrdm_post_transition(struct powerdomain *pwrdm)
-{
-       if (pwrdm)
-               _pwrdm_post_transition_cb(pwrdm, NULL);
-       else
-               pwrdm_for_each(_pwrdm_post_transition_cb, NULL);
+       if (!(pwrdm->pwrsts & (1 << pwrst)))
+               return false;
 
-       return 0;
+       return true;
 }
 
+/* Powerdomain debugfs-related functions */
+
 /**
- * pwrdm_get_context_loss_count - get powerdomain's context loss count
- * @pwrdm: struct powerdomain * to wait for
+ * pwrdm_dbg_show_counter - generate debugfs data for the pwrdm pwrst counters
+ * @pwrdm: struct powerdomain * to generate debugfs data for
+ * @seq_file: struct seq_file * to write data to
  *
- * Context loss count is the sum of powerdomain off-mode counter, the
- * logic off counter and the per-bank memory off counter.  Returns negative
- * (and WARNs) upon error, otherwise, returns the context loss count.
+ * Dump the powerdomain @pwrdm's power state counters (and current
+ * power state) to the seq_file @seq_file.  Currently called by the
+ * mach-omap2/pm-debug.c code.  Returns 0.
  */
-int pwrdm_get_context_loss_count(struct powerdomain *pwrdm)
+int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *seq_file)
 {
-       int i, count;
+       struct seq_file *s = (struct seq_file *)seq_file;
+       int i;
+       u8 curr_fpwrst;
 
-       if (!pwrdm) {
-               WARN(1, "powerdomain: %s: pwrdm is null\n", __func__);
-               return -ENODEV;
-       }
+       if (!_pwrdm_pwrst_can_change(pwrdm))
+               return 0;
 
-       count = pwrdm->state_counter[PWRDM_POWER_OFF];
-       count += pwrdm->ret_logic_off_counter;
+       pwrdm_lock(pwrdm);
 
-       for (i = 0; i < pwrdm->banks; i++)
-               count += pwrdm->ret_mem_off_counter[i];
+       curr_fpwrst = _pwrdm_read_fpwrst(pwrdm);
+       if (pwrdm->fpwrst != curr_fpwrst)
+               pr_err("pwrdm state mismatch(%s) %s != %s\n",
+                      pwrdm->name,
+                      pwrdm_convert_fpwrst_to_name(pwrdm->fpwrst),
+                      pwrdm_convert_fpwrst_to_name(curr_fpwrst));
 
-       /*
-        * Context loss count has to be a non-negative value. Clear the sign
-        * bit to get a value range from 0 to INT_MAX.
-        */
-       count &= INT_MAX;
+       seq_printf(s, "%s (%s)", pwrdm->name,
+                  pwrdm_convert_fpwrst_to_name(pwrdm->fpwrst));
+       for (i = PWRDM_FPWRST_OFFSET; i < PWRDM_MAX_FUNC_PWRSTS; i++)
+               seq_printf(s, ",%s:%d", pwrdm_convert_fpwrst_to_name(i),
+                          pwrdm->fpwrst_counter[i - PWRDM_FPWRST_OFFSET]);
 
-       pr_debug("powerdomain: %s: context loss count = %d\n",
-                pwrdm->name, count);
+       seq_printf(s, "\n");
 
-       return count;
+       pwrdm_unlock(pwrdm);
+
+       return 0;
 }
 
 /**
- * pwrdm_can_ever_lose_context - can this powerdomain ever lose context?
- * @pwrdm: struct powerdomain *
+ * pwrdm_dbg_show_timer - generate debugfs data for the pwrdm pwrst timers
+ * @pwrdm: struct powerdomain * to generate debugfs data for
+ * @seq_file: struct seq_file * to write data to
  *
- * Given a struct powerdomain * @pwrdm, returns 1 if the powerdomain
- * can lose either memory or logic context or if @pwrdm is invalid, or
- * returns 0 otherwise.  This function is not concerned with how the
- * powerdomain registers are programmed (i.e., to go off or not); it's
- * concerned with whether it's ever possible for this powerdomain to
- * go off while some other part of the chip is active.  This function
- * assumes that every powerdomain can go to either ON or INACTIVE.
+ * Dump the powerdomain @pwrdm's power state residency duration timings
+ * to the seq_file @seq_file.  Currently called by the mach-omap2/pm-debug.c
+ * code.  Returns 0.
  */
-bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm)
+int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *seq_file)
 {
+#ifdef CONFIG_PM_DEBUG
+       struct seq_file *s = (struct seq_file *)seq_file;
        int i;
 
-       if (IS_ERR_OR_NULL(pwrdm)) {
-               pr_debug("powerdomain: %s: invalid powerdomain pointer\n",
-                        __func__);
-               return 1;
-       }
+       if (!_pwrdm_pwrst_can_change(pwrdm))
+               return 0;
 
-       if (pwrdm->pwrsts & PWRSTS_OFF)
-               return 1;
+       pwrdm_lock(pwrdm);
 
-       if (pwrdm->pwrsts & PWRSTS_RET) {
-               if (pwrdm->pwrsts_logic_ret & PWRSTS_OFF)
-                       return 1;
+       pwrdm_state_switch_nolock(pwrdm);
 
-               for (i = 0; i < pwrdm->banks; i++)
-                       if (pwrdm->pwrsts_mem_ret[i] & PWRSTS_OFF)
-                               return 1;
-       }
+       seq_printf(s, "%s (%s)", pwrdm->name,
+                  pwrdm_convert_fpwrst_to_name(pwrdm->fpwrst));
 
-       for (i = 0; i < pwrdm->banks; i++)
-               if (pwrdm->pwrsts_mem_on[i] & PWRSTS_OFF)
-                       return 1;
+       for (i = 0; i < PWRDM_FPWRSTS_COUNT; i++)
+               seq_printf(s, ",%s:%lld",
+                          pwrdm_convert_fpwrst_to_name(i + PWRDM_FPWRST_OFFSET),
+                          pwrdm->fpwrst_timer[i]);
 
+       seq_printf(s, "\n");
+
+       pwrdm_unlock(pwrdm);
+#endif
        return 0;
 }
+
index 5277d56eb37f283fb9de3c33846639ee05ebba96..b8f3dbf564c13d0c00e3a1d689cb681702973875 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * OMAP2/3/4 powerdomain control
  *
- * Copyright (C) 2007-2008, 2010 Texas Instruments, Inc.
+ * Copyright (C) 2007-2008, 2010-2012 Texas Instruments, Inc.
  * Copyright (C) 2007-2011 Nokia Corporation
  *
  * Paul Walmsley
 
 #include <linux/types.h>
 #include <linux/list.h>
-
-#include <linux/atomic.h>
+#include <linux/spinlock.h>
 
 #include "voltage.h"
 
+/*
+ * PWRDM_FPWRST_OFFSET: offset of the first functional power state
+ * from 0.  This offset can be subtracted from the functional power
+ * state macros to produce offsets suitable for array indices, for
+ * example.  The intention behind the addition of this offset is to
+ * prevent functional power states from accidentally being confused
+ * with the low-level, hardware power states.
+ */
+#define PWRDM_FPWRST_OFFSET            0x80
+
+/*
+ * Powerdomain functional power states, used by the external API functions
+ * These must match the order and names in _fpwrst_names[]
+ */
+enum pwrdm_func_state {
+       PWRDM_FUNC_PWRST_OFF            = PWRDM_FPWRST_OFFSET,
+       PWRDM_FUNC_PWRST_OSWR,
+       PWRDM_FUNC_PWRST_CSWR,
+       PWRDM_FUNC_PWRST_INACTIVE,
+       PWRDM_FUNC_PWRST_ON,
+       PWRDM_MAX_FUNC_PWRSTS           /* Last value, used as the max value */
+};
+
+#define PWRDM_FPWRSTS_COUNT    (PWRDM_MAX_FUNC_PWRSTS - PWRDM_FPWRST_OFFSET)
+
 /* Powerdomain basic power states */
 #define PWRDM_POWER_OFF                0x0
 #define PWRDM_POWER_RET                0x1
 #define PWRSTS_OFF_RET_ON      (PWRSTS_OFF_RET | PWRSTS_ON)
 
 
-/* Powerdomain flags */
-#define PWRDM_HAS_HDWR_SAR     (1 << 0) /* hardware save-and-restore support */
-#define PWRDM_HAS_MPU_QUIRK    (1 << 1) /* MPU pwr domain has MEM bank 0 bits
-                                         * in MEM bank 1 position. This is
-                                         * true for OMAP3430
-                                         */
-#define PWRDM_HAS_LOWPOWERSTATECHANGE  (1 << 2) /*
-                                                 * support to transition from a
-                                                 * sleep state to a lower sleep
-                                                 * state without waking up the
-                                                 * powerdomain
-                                                 */
+/*
+ * Powerdomain flags (struct powerdomain.flags)
+ *
+ * PWRDM_HAS_HDWR_SAR - powerdomain has hardware save-and-restore support
+ *
+ * PWRDM_HAS_MPU_QUIRK - MPU pwr domain has MEM bank 0 bits in MEM
+ * bank 1 position. This is true for OMAP3430
+ *
+ * PWRDM_HAS_LOWPOWERSTATECHANGE - can transition from a sleep state
+ * to a lower sleep state without waking up the powerdomain
+ *
+ * PWRDM_ACTIVE_WITH_KERNEL - this powerdomain's current power state is
+ * guaranteed to be ON whenever the kernel is running
+ */
+#define PWRDM_HAS_HDWR_SAR             BIT(0)
+#define PWRDM_HAS_MPU_QUIRK            BIT(1)
+#define PWRDM_HAS_LOWPOWERSTATECHANGE  BIT(2)
+#define PWRDM_ACTIVE_WITH_KERNEL       BIT(3)
+
+/*
+ * Powerdomain internal flags (struct powerdomain._flags)
+ *
+ * _PWRDM_NEXT_FPWRST_IS_VALID: the locally-cached copy of the
+ *    powerdomain's next-functional-power-state -- struct
+ *    powerdomain.next_fpwrst -- is valid.  If this bit is not set,
+ *    the code needs to load the current value from the hardware.
+ *
+ * _PWRDM_PREV_FPWRST_IS_VALID: the locally-cached copy of the
+ *    powerdomain's previous-functional-power-state -- struct
+ *    powerdomain.prev_fpwrst -- is valid.  If this bit is not set,
+ *    the code needs to load the current value from the hardware.  The
+ *    previous-functional-power-state cache for the CORE and MPU needs
+ *    to be invalidated right before WFI, unless they were not programmed
+ *    to change power states.
+ */
+#define _PWRDM_NEXT_FPWRST_IS_VALID    BIT(0)
+#define _PWRDM_PREV_FPWRST_IS_VALID    BIT(1)
 
 /*
  * Number of memory banks that are power-controllable. On OMAP4430, the
@@ -99,12 +148,23 @@ struct powerdomain;
  * @mem_pwrst_mask: (AM33XX only) mask for mem state bitfield in @pwrstst_offs
  * @mem_retst_mask: (AM33XX only) mask for mem retention state bitfield
  *     in @pwrstctrl_offs
- * @state:
- * @state_counter:
- * @timer:
- * @state_timer:
+ * @fpwrst: current func power state (set in pwrdm_state_switch() or post_trans)
+ * @fpwrst_counter: estimated number of times the pwrdm entered the power states
+ * @next_fpwrst: cache of the powerdomain's next-power-state
+ * @prev_fpwrst: cache of the powerdomain's previous-power-state bitfield
+ * @timer: sched_clock() timestamp of last pwrdm_state_switch()
+ * @fpwrst_timer: estimated nanoseconds of residency in the various power states
+ * @_lock: spinlock used to serialize powerdomain and some clockdomain ops
+ * @_lock_flags: stored flags when @_lock is taken
+ * @_flags: flags (for internal use only)
  *
  * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
+ *
+ * @prev_fpwrst is updated during pwrdm_pre_transition(), but presumably
+ * should also be updated upon clock/IP block idle transitions.
+ *
+ * Possible values for @_flags are documented above in the
+ * "Powerdomain internal flags (struct powerdomain._flags)" comments.
  */
 struct powerdomain {
        const char *name;
@@ -120,14 +180,16 @@ struct powerdomain {
        const u8 pwrsts_mem_ret[PWRDM_MAX_MEM_BANKS];
        const u8 pwrsts_mem_on[PWRDM_MAX_MEM_BANKS];
        const u8 prcm_partition;
+       u8 fpwrst;
+       u8 next_fpwrst;
+       u8 prev_fpwrst;
+       u8 _flags;
        struct clockdomain *pwrdm_clkdms[PWRDM_MAX_CLKDMS];
        struct list_head node;
        struct list_head voltdm_node;
-       int state;
-       unsigned state_counter[PWRDM_MAX_PWRSTS];
-       unsigned ret_logic_off_counter;
-       unsigned ret_mem_off_counter[PWRDM_MAX_MEM_BANKS];
-
+       unsigned fpwrst_counter[PWRDM_FPWRSTS_COUNT];
+       spinlock_t _lock;
+       unsigned long _lock_flags;
        const u8 pwrstctrl_offs;
        const u8 pwrstst_offs;
        const u32 logicretstate_mask;
@@ -138,7 +200,7 @@ struct powerdomain {
 
 #ifdef CONFIG_PM_DEBUG
        s64 timer;
-       s64 state_timer[PWRDM_MAX_PWRSTS];
+       s64 fpwrst_timer[PWRDM_FPWRSTS_COUNT];
 #endif
 };
 
@@ -202,43 +264,37 @@ int pwrdm_for_each_clkdm(struct powerdomain *pwrdm,
                                   struct clockdomain *clkdm));
 struct voltagedomain *pwrdm_get_voltdm(struct powerdomain *pwrdm);
 
-int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm);
-
-int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst);
-int pwrdm_read_next_pwrst(struct powerdomain *pwrdm);
-int pwrdm_read_pwrst(struct powerdomain *pwrdm);
-int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm);
 int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm);
 
-int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst);
-int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst);
-int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst);
-
-int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm);
-int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm);
-int pwrdm_read_logic_retst(struct powerdomain *pwrdm);
-int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank);
-int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank);
-int pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank);
-
 int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm);
 int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm);
 bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm);
 
-int pwrdm_wait_transition(struct powerdomain *pwrdm);
-
+int pwrdm_state_switch_nolock(struct powerdomain *pwrdm);
 int pwrdm_state_switch(struct powerdomain *pwrdm);
+int pwrdm_state_switch_nolock(struct powerdomain *pwrdm);
 int pwrdm_pre_transition(struct powerdomain *pwrdm);
 int pwrdm_post_transition(struct powerdomain *pwrdm);
-int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
 int pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
 bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm);
 
+extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 state);
+
+extern const char *pwrdm_convert_fpwrst_to_name(u8 fpwrst);
+extern int pwrdm_set_next_fpwrst(struct powerdomain *pwrdm, u8 fpwrst);
+extern int pwrdm_read_next_fpwrst(struct powerdomain *pwrdm);
+extern int pwrdm_set_fpwrst(struct powerdomain *pwrdm,
+                           enum pwrdm_func_state fpwrst);
+extern int pwrdm_read_fpwrst(struct powerdomain *pwrdm);
+extern int pwrdm_read_prev_fpwrst(struct powerdomain *pwrdm);
+extern bool pwrdm_supports_fpwrst(struct powerdomain *pwrdm, u8 fpwrst);
+
 extern void omap242x_powerdomains_init(void);
 extern void omap243x_powerdomains_init(void);
 extern void omap3xxx_powerdomains_init(void);
 extern void am33xx_powerdomains_init(void);
 extern void omap44xx_powerdomains_init(void);
+extern void omap54xx_powerdomains_init(void);
 
 extern struct pwrdm_ops omap2_pwrdm_operations;
 extern struct pwrdm_ops omap3_pwrdm_operations;
@@ -253,5 +309,11 @@ extern u32 omap2_pwrdm_get_mem_bank_stst_mask(u8 bank);
 extern struct powerdomain wkup_omap2_pwrdm;
 extern struct powerdomain gfx_omap2_pwrdm;
 
+extern void pwrdm_lock(struct powerdomain *pwrdm);
+extern void pwrdm_unlock(struct powerdomain *pwrdm);
+
+/* Debugfs functions */
+extern int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *seq_file);
+extern int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *seq_file);
 
 #endif
index d3a5399091ad35817267c75ea008d060f0962e9a..7b946f1005b16ff7839f13fec742079ada6e230c 100644 (file)
@@ -54,12 +54,12 @@ struct powerdomain gfx_omap2_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,  /* MEMONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 struct powerdomain wkup_omap2_pwrdm = {
        .name           = "wkup_pwrdm",
        .prcm_offs      = WKUP_MOD,
        .pwrsts         = PWRSTS_ON,
-       .voltdm         = { .name = "wakeup" },
+       .voltdm         = { .name = "wakeup" },
 };
index ba520d4f7c7bf45d81503497a01ae9d9f555b032..112927f58c28d9b7e30c640cd408fc6d67e4490f 100644 (file)
@@ -38,7 +38,7 @@ static struct powerdomain dsp_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain mpu_24xx_pwrdm = {
@@ -53,13 +53,15 @@ static struct powerdomain mpu_24xx_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
+       .flags            = PWRDM_ACTIVE_WITH_KERNEL,
 };
 
 static struct powerdomain core_24xx_pwrdm = {
        .name             = "core_pwrdm",
        .prcm_offs        = CORE_MOD,
        .pwrsts           = PWRSTS_OFF_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_RET,
        .banks            = 3,
        .pwrsts_mem_ret   = {
                [0] = PWRSTS_OFF_RET,    /* MEM1RETSTATE */
@@ -71,7 +73,8 @@ static struct powerdomain core_24xx_pwrdm = {
                [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
                [2] = PWRSTS_OFF_RET_ON, /* MEM3ONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
+       .flags            = PWRDM_ACTIVE_WITH_KERNEL,
 };
 
 
@@ -93,7 +96,7 @@ static struct powerdomain mdm_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,  /* MEMONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 /*
index 869adb82569e8e6459b0433e6ca9f0127ba0a2e9..acb148a0bb7207cf0c47d4e6f88809d9ca228a5a 100644 (file)
@@ -123,7 +123,8 @@ static struct powerdomain mpu_33xx_pwrdm = {
        .pwrstst_offs           = AM33XX_PM_MPU_PWRSTST_OFFSET,
        .pwrsts                 = PWRSTS_OFF_RET_ON,
        .pwrsts_logic_ret       = PWRSTS_OFF_RET,
-       .flags                  = PWRDM_HAS_LOWPOWERSTATECHANGE,
+       .flags                  = (PWRDM_HAS_LOWPOWERSTATECHANGE |
+                                  PWRDM_ACTIVE_WITH_KERNEL),
        .banks                  = 3,
        .logicretstate_mask     = AM33XX_LOGICRETSTATE_MASK,
        .mem_on_mask            = {
index 8b23d234fb554cc4f34663f7f3741643cb45ba46..ade93d3288fece580204c27546e832f4b40c6e8c 100644 (file)
@@ -50,7 +50,7 @@ static struct powerdomain iva2_pwrdm = {
                [2] = PWRSTS_OFF_ON,
                [3] = PWRSTS_ON,
        },
-       .voltdm           = { .name = "mpu_iva" },
+       .voltdm           = { .name = "mpu_iva" },
 };
 
 static struct powerdomain mpu_3xxx_pwrdm = {
@@ -58,7 +58,7 @@ static struct powerdomain mpu_3xxx_pwrdm = {
        .prcm_offs        = MPU_MOD,
        .pwrsts           = PWRSTS_OFF_RET_ON,
        .pwrsts_logic_ret = PWRSTS_OFF_RET,
-       .flags            = PWRDM_HAS_MPU_QUIRK,
+       .flags            = (PWRDM_HAS_MPU_QUIRK | PWRDM_ACTIVE_WITH_KERNEL),
        .banks            = 1,
        .pwrsts_mem_ret   = {
                [0] = PWRSTS_OFF_RET,
@@ -66,7 +66,7 @@ static struct powerdomain mpu_3xxx_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_OFF_ON,
        },
-       .voltdm           = { .name = "mpu_iva" },
+       .voltdm           = { .name = "mpu_iva" },
 };
 
 static struct powerdomain mpu_am35x_pwrdm = {
@@ -74,7 +74,7 @@ static struct powerdomain mpu_am35x_pwrdm = {
        .prcm_offs        = MPU_MOD,
        .pwrsts           = PWRSTS_ON,
        .pwrsts_logic_ret = PWRSTS_ON,
-       .flags            = PWRDM_HAS_MPU_QUIRK,
+       .flags            = (PWRDM_HAS_MPU_QUIRK | PWRDM_ACTIVE_WITH_KERNEL),
        .banks            = 1,
        .pwrsts_mem_ret   = {
                [0] = PWRSTS_ON,
@@ -82,7 +82,7 @@ static struct powerdomain mpu_am35x_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,
        },
-       .voltdm           = { .name = "mpu_iva" },
+       .voltdm           = { .name = "mpu_iva" },
 };
 
 /*
@@ -109,7 +109,8 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
                [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
                [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
+       .flags            = PWRDM_ACTIVE_WITH_KERNEL,
 };
 
 static struct powerdomain core_3xxx_es3_1_pwrdm = {
@@ -121,7 +122,8 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
         * Setting the SAR flag for errata ID i478 which applies
         *  to 3430 <= ES3.1
         */
-       .flags            = PWRDM_HAS_HDWR_SAR, /* for USBTLL only */
+       .flags            = (PWRDM_HAS_HDWR_SAR |  /* for USBTLL only */
+                            PWRDM_ACTIVE_WITH_KERNEL),
        .banks            = 2,
        .pwrsts_mem_ret   = {
                [0] = PWRSTS_OFF_RET,    /* MEM1RETSTATE */
@@ -131,7 +133,7 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
                [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
                [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain core_am35x_pwrdm = {
@@ -148,7 +150,8 @@ static struct powerdomain core_am35x_pwrdm = {
                [0] = PWRSTS_ON, /* MEM1ONSTATE */
                [1] = PWRSTS_ON, /* MEM2ONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
+       .flags            = PWRDM_ACTIVE_WITH_KERNEL,
 };
 
 static struct powerdomain dss_pwrdm = {
@@ -163,7 +166,7 @@ static struct powerdomain dss_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,  /* MEMONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain dss_am35x_pwrdm = {
@@ -178,7 +181,7 @@ static struct powerdomain dss_am35x_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,  /* MEMONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 /*
@@ -199,7 +202,7 @@ static struct powerdomain sgx_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,  /* MEMONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain sgx_am35x_pwrdm = {
@@ -214,7 +217,7 @@ static struct powerdomain sgx_am35x_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,  /* MEMONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain cam_pwrdm = {
@@ -229,7 +232,7 @@ static struct powerdomain cam_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,  /* MEMONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain per_pwrdm = {
@@ -244,7 +247,7 @@ static struct powerdomain per_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,  /* MEMONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain per_am35x_pwrdm = {
@@ -259,13 +262,13 @@ static struct powerdomain per_am35x_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,  /* MEMONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain emu_pwrdm = {
        .name           = "emu_pwrdm",
        .prcm_offs      = OMAP3430_EMU_MOD,
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain neon_pwrdm = {
@@ -273,7 +276,7 @@ static struct powerdomain neon_pwrdm = {
        .prcm_offs        = OMAP3430_NEON_MOD,
        .pwrsts           = PWRSTS_OFF_RET_ON,
        .pwrsts_logic_ret = PWRSTS_RET,
-       .voltdm           = { .name = "mpu_iva" },
+       .voltdm           = { .name = "mpu_iva" },
 };
 
 static struct powerdomain neon_am35x_pwrdm = {
@@ -281,7 +284,7 @@ static struct powerdomain neon_am35x_pwrdm = {
        .prcm_offs        = OMAP3430_NEON_MOD,
        .pwrsts           = PWRSTS_ON,
        .pwrsts_logic_ret = PWRSTS_ON,
-       .voltdm           = { .name = "mpu_iva" },
+       .voltdm           = { .name = "mpu_iva" },
 };
 
 static struct powerdomain usbhost_pwrdm = {
@@ -303,37 +306,37 @@ static struct powerdomain usbhost_pwrdm = {
        .pwrsts_mem_on    = {
                [0] = PWRSTS_ON,  /* MEMONSTATE */
        },
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain dpll1_pwrdm = {
        .name           = "dpll1_pwrdm",
        .prcm_offs      = MPU_MOD,
-       .voltdm           = { .name = "mpu_iva" },
+       .voltdm           = { .name = "mpu_iva" },
 };
 
 static struct powerdomain dpll2_pwrdm = {
        .name           = "dpll2_pwrdm",
        .prcm_offs      = OMAP3430_IVA2_MOD,
-       .voltdm           = { .name = "mpu_iva" },
+       .voltdm           = { .name = "mpu_iva" },
 };
 
 static struct powerdomain dpll3_pwrdm = {
        .name           = "dpll3_pwrdm",
        .prcm_offs      = PLL_MOD,
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain dpll4_pwrdm = {
        .name           = "dpll4_pwrdm",
        .prcm_offs      = PLL_MOD,
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 static struct powerdomain dpll5_pwrdm = {
        .name           = "dpll5_pwrdm",
        .prcm_offs      = PLL_MOD,
-       .voltdm           = { .name = "core" },
+       .voltdm           = { .name = "core" },
 };
 
 /* As powerdomains are added or removed above, this list must also be changed */
index 704664c0e2594ddb82a02fdba2ae9375b58c7257..64e0cd35296c0ef610d4c9137a90d875406273a9 100644 (file)
@@ -164,6 +164,7 @@ static struct powerdomain cpu0_44xx_pwrdm = {
        .pwrsts_mem_on  = {
                [0] = PWRSTS_ON,        /* cpu0_l1 */
        },
+       .flags            = PWRDM_ACTIVE_WITH_KERNEL,
 };
 
 /* cpu1_44xx_pwrdm: MPU1 processor and Neon coprocessor power domain */
@@ -218,6 +219,7 @@ static struct powerdomain mpu_44xx_pwrdm = {
                [1] = PWRSTS_ON,        /* mpu_l2 */
                [2] = PWRSTS_ON,        /* mpu_ram */
        },
+       .flags            = PWRDM_ACTIVE_WITH_KERNEL,
 };
 
 /* ivahd_44xx_pwrdm: IVA-HD power domain */
diff --git a/arch/arm/mach-omap2/powerdomains54xx_data.c b/arch/arm/mach-omap2/powerdomains54xx_data.c
new file mode 100644 (file)
index 0000000..81f8a7c
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * OMAP54XX Power domains framework
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ *
+ * Abhijit Pagare (abhijitpagare@ti.com)
+ * Benoit Cousson (b-cousson@ti.com)
+ * Paul Walmsley (paul@pwsan.com)
+ *
+ * This file is automatically generated from the OMAP hardware databases.
+ * We respectfully ask that any modifications to this file be coordinated
+ * with the public linux-omap@vger.kernel.org mailing list and the
+ * authors above to ensure that the autogeneration scripts are kept
+ * up-to-date with the file contents.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include "powerdomain.h"
+
+#include "prcm-common.h"
+#include "prcm44xx.h"
+#include "prm-regbits-54xx.h"
+#include "prm54xx.h"
+#include "prcm_mpu54xx.h"
+
+/* core_54xx_pwrdm: CORE power domain */
+static struct powerdomain core_54xx_pwrdm = {
+       .name             = "core_pwrdm",
+       .voltdm           = { .name = "core" },
+       .prcm_offs        = OMAP54XX_PRM_CORE_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_OFF_RET,
+       .banks            = 5,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* core_nret_bank */
+               [1] = PWRSTS_OFF_RET,   /* core_ocmram */
+               [2] = PWRSTS_OFF_RET,   /* core_other_bank */
+               [3] = PWRSTS_OFF_RET,   /* ipu_l2ram */
+               [4] = PWRSTS_OFF_RET,   /* ipu_unicache */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_OFF_RET,   /* core_nret_bank */
+               [1] = PWRSTS_OFF_RET,   /* core_ocmram */
+               [2] = PWRSTS_OFF_RET,   /* core_other_bank */
+               [3] = PWRSTS_OFF_RET,   /* ipu_l2ram */
+               [4] = PWRSTS_OFF_RET,   /* ipu_unicache */
+       },
+       .flags            = PWRDM_HAS_LOWPOWERSTATECHANGE,
+};
+
+/* abe_54xx_pwrdm: Audio back end power domain */
+static struct powerdomain abe_54xx_pwrdm = {
+       .name             = "abe_pwrdm",
+       .voltdm           = { .name = "core" },
+       .prcm_offs        = OMAP54XX_PRM_ABE_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_OFF_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_OFF,
+       .banks            = 2,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* aessmem */
+               [1] = PWRSTS_OFF_RET,   /* periphmem */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_OFF_RET,   /* aessmem */
+               [1] = PWRSTS_OFF_RET,   /* periphmem */
+       },
+       .flags            = PWRDM_HAS_LOWPOWERSTATECHANGE,
+};
+
+/* coreaon_54xx_pwrdm: Always ON logic that sits in VDD_CORE voltage domain */
+static struct powerdomain coreaon_54xx_pwrdm = {
+       .name             = "coreaon_pwrdm",
+       .voltdm           = { .name = "core" },
+       .prcm_offs        = OMAP54XX_PRM_COREAON_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_ON,
+};
+
+/* dss_54xx_pwrdm: Display subsystem power domain */
+static struct powerdomain dss_54xx_pwrdm = {
+       .name             = "dss_pwrdm",
+       .voltdm           = { .name = "core" },
+       .prcm_offs        = OMAP54XX_PRM_DSS_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_OFF_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_OFF,
+       .banks            = 1,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* dss_mem */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_OFF_RET,   /* dss_mem */
+       },
+       .flags            = PWRDM_HAS_LOWPOWERSTATECHANGE,
+};
+
+/* cpu0_54xx_pwrdm: MPU0 processor and Neon coprocessor power domain */
+static struct powerdomain cpu0_54xx_pwrdm = {
+       .name             = "cpu0_pwrdm",
+       .voltdm           = { .name = "mpu" },
+       .prcm_offs        = OMAP54XX_PRCM_MPU_PRM_C0_INST,
+       .prcm_partition   = OMAP54XX_PRCM_MPU_PARTITION,
+       .pwrsts           = PWRSTS_OFF_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_OFF_RET,
+       .banks            = 1,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* cpu0_l1 */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_ON,        /* cpu0_l1 */
+       },
+};
+
+/* cpu1_54xx_pwrdm: MPU1 processor and Neon coprocessor power domain */
+static struct powerdomain cpu1_54xx_pwrdm = {
+       .name             = "cpu1_pwrdm",
+       .voltdm           = { .name = "mpu" },
+       .prcm_offs        = OMAP54XX_PRCM_MPU_PRM_C1_INST,
+       .prcm_partition   = OMAP54XX_PRCM_MPU_PARTITION,
+       .pwrsts           = PWRSTS_OFF_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_OFF_RET,
+       .banks            = 1,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* cpu1_l1 */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_ON,        /* cpu1_l1 */
+       },
+};
+
+/* emu_54xx_pwrdm: Emulation power domain */
+static struct powerdomain emu_54xx_pwrdm = {
+       .name             = "emu_pwrdm",
+       .voltdm           = { .name = "wkup" },
+       .prcm_offs        = OMAP54XX_PRM_EMU_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_OFF_ON,
+       .banks            = 1,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* emu_bank */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_OFF_RET,   /* emu_bank */
+       },
+};
+
+/* mpu_54xx_pwrdm: Modena processor and the Neon coprocessor power domain */
+static struct powerdomain mpu_54xx_pwrdm = {
+       .name             = "mpu_pwrdm",
+       .voltdm           = { .name = "mpu" },
+       .prcm_offs        = OMAP54XX_PRM_MPU_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_OFF_RET,
+       .banks            = 2,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* mpu_l2 */
+               [1] = PWRSTS_RET,       /* mpu_ram */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_OFF_RET,   /* mpu_l2 */
+               [1] = PWRSTS_OFF_RET,   /* mpu_ram */
+       },
+};
+
+/* custefuse_54xx_pwrdm: Customer efuse controller power domain */
+static struct powerdomain custefuse_54xx_pwrdm = {
+       .name             = "custefuse_pwrdm",
+       .voltdm           = { .name = "core" },
+       .prcm_offs        = OMAP54XX_PRM_CUSTEFUSE_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_OFF_ON,
+       .flags            = PWRDM_HAS_LOWPOWERSTATECHANGE,
+};
+
+/* dsp_54xx_pwrdm: Tesla processor power domain */
+static struct powerdomain dsp_54xx_pwrdm = {
+       .name             = "dsp_pwrdm",
+       .voltdm           = { .name = "mm" },
+       .prcm_offs        = OMAP54XX_PRM_DSP_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_OFF_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_OFF_RET,
+       .banks            = 3,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* dsp_edma */
+               [1] = PWRSTS_OFF_RET,   /* dsp_l1 */
+               [2] = PWRSTS_OFF_RET,   /* dsp_l2 */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_OFF_RET,   /* dsp_edma */
+               [1] = PWRSTS_OFF_RET,   /* dsp_l1 */
+               [2] = PWRSTS_OFF_RET,   /* dsp_l2 */
+       },
+       .flags            = PWRDM_HAS_LOWPOWERSTATECHANGE,
+};
+
+/* cam_54xx_pwrdm: Camera subsystem power domain */
+static struct powerdomain cam_54xx_pwrdm = {
+       .name             = "cam_pwrdm",
+       .voltdm           = { .name = "core" },
+       .prcm_offs        = OMAP54XX_PRM_CAM_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_OFF_ON,
+       .banks            = 1,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* cam_mem */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_OFF_RET,   /* cam_mem */
+       },
+       .flags            = PWRDM_HAS_LOWPOWERSTATECHANGE,
+};
+
+/* l3init_54xx_pwrdm: L3 initators pheripherals power domain  */
+static struct powerdomain l3init_54xx_pwrdm = {
+       .name             = "l3init_pwrdm",
+       .voltdm           = { .name = "core" },
+       .prcm_offs        = OMAP54XX_PRM_L3INIT_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_OFF_RET,
+       .banks            = 2,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* l3init_bank1 */
+               [1] = PWRSTS_OFF_RET,   /* l3init_bank2 */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_OFF_RET,   /* l3init_bank1 */
+               [1] = PWRSTS_OFF_RET,   /* l3init_bank2 */
+       },
+       .flags            = PWRDM_HAS_LOWPOWERSTATECHANGE,
+};
+
+/* gpu_54xx_pwrdm: 3D accelerator power domain */
+static struct powerdomain gpu_54xx_pwrdm = {
+       .name             = "gpu_pwrdm",
+       .voltdm           = { .name = "mm" },
+       .prcm_offs        = OMAP54XX_PRM_GPU_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_OFF_ON,
+       .banks            = 1,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* gpu_mem */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_OFF_RET,   /* gpu_mem */
+       },
+       .flags            = PWRDM_HAS_LOWPOWERSTATECHANGE,
+};
+
+/* wkupaon_54xx_pwrdm: Wake-up power domain */
+static struct powerdomain wkupaon_54xx_pwrdm = {
+       .name             = "wkupaon_pwrdm",
+       .voltdm           = { .name = "wkup" },
+       .prcm_offs        = OMAP54XX_PRM_WKUPAON_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_ON,
+       .banks            = 1,
+       .pwrsts_mem_ret = {
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_ON,        /* wkup_bank */
+       },
+};
+
+/* iva_54xx_pwrdm: IVA-HD power domain */
+static struct powerdomain iva_54xx_pwrdm = {
+       .name             = "iva_pwrdm",
+       .voltdm           = { .name = "mm" },
+       .prcm_offs        = OMAP54XX_PRM_IVA_INST,
+       .prcm_partition   = OMAP54XX_PRM_PARTITION,
+       .pwrsts           = PWRSTS_OFF_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_OFF,
+       .banks            = 4,
+       .pwrsts_mem_ret = {
+               [0] = PWRSTS_OFF_RET,   /* hwa_mem */
+               [1] = PWRSTS_OFF_RET,   /* sl2_mem */
+               [2] = PWRSTS_OFF_RET,   /* tcm1_mem */
+               [3] = PWRSTS_OFF_RET,   /* tcm2_mem */
+       },
+       .pwrsts_mem_on  = {
+               [0] = PWRSTS_OFF_RET,   /* hwa_mem */
+               [1] = PWRSTS_OFF_RET,   /* sl2_mem */
+               [2] = PWRSTS_OFF_RET,   /* tcm1_mem */
+               [3] = PWRSTS_OFF_RET,   /* tcm2_mem */
+       },
+       .flags            = PWRDM_HAS_LOWPOWERSTATECHANGE,
+};
+
+/*
+ * The following power domains are not under SW control
+ *
+ * mpuaon
+ * mmaon
+ */
+
+/* As powerdomains are added or removed above, this list must also be changed */
+static struct powerdomain *powerdomains_omap54xx[] __initdata = {
+       &core_54xx_pwrdm,
+       &abe_54xx_pwrdm,
+       &coreaon_54xx_pwrdm,
+       &dss_54xx_pwrdm,
+       &cpu0_54xx_pwrdm,
+       &cpu1_54xx_pwrdm,
+       &emu_54xx_pwrdm,
+       &mpu_54xx_pwrdm,
+       &custefuse_54xx_pwrdm,
+       &dsp_54xx_pwrdm,
+       &cam_54xx_pwrdm,
+       &l3init_54xx_pwrdm,
+       &gpu_54xx_pwrdm,
+       &wkupaon_54xx_pwrdm,
+       &iva_54xx_pwrdm,
+       NULL
+};
+
+void __init omap54xx_powerdomains_init(void)
+{
+       pwrdm_register_platform_funcs(&omap4_pwrdm_operations);
+       pwrdm_register_pwrdms(powerdomains_omap54xx);
+       pwrdm_complete_init();
+}
index 7334ffb9d2c1ea666a1f326e5f2a9076df0c16a9..f429cdd5a118aa5ca3ea647b1ee3307b18c3bf20 100644 (file)
 #define OMAP4430_SCRM_PARTITION                        4
 #define OMAP4430_PRCM_MPU_PARTITION            5
 
+#define OMAP54XX_PRM_PARTITION                 1
+#define OMAP54XX_CM_CORE_AON_PARTITION         2
+#define OMAP54XX_CM_CORE_PARTITION             3
+#define OMAP54XX_SCRM_PARTITION                        4
+#define OMAP54XX_PRCM_MPU_PARTITION            5
+
 /*
  * OMAP4_MAX_PRCM_PARTITIONS: set to the highest value of the PRCM partition
  * IDs, plus one
diff --git a/arch/arm/mach-omap2/prcm_mpu54xx.h b/arch/arm/mach-omap2/prcm_mpu54xx.h
new file mode 100644 (file)
index 0000000..d2eb945
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * OMAP54xx PRCM MPU instance offset macros
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Paul Walmsley (paul@pwsan.com)
+ * Rajendra Nayak (rnayak@ti.com)
+ * Benoit Cousson (b-cousson@ti.com)
+ *
+ * This file is automatically generated from the OMAP hardware databases.
+ * We respectfully ask that any modifications to this file be coordinated
+ * with the public linux-omap@vger.kernel.org mailing list and the
+ * authors above to ensure that the autogeneration scripts are kept
+ * up-to-date with the file contents.
+ *
+ * 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 __ARCH_ARM_MACH_OMAP2_PRCM_MPU54XX_H
+#define __ARCH_ARM_MACH_OMAP2_PRCM_MPU54XX_H
+
+#define OMAP54XX_PRCM_MPU_BASE                 0x48243000
+
+#define OMAP54XX_PRCM_MPU_REGADDR(inst, reg)                           \
+       OMAP2_L4_IO_ADDRESS(OMAP54XX_PRCM_MPU_BASE + (inst) + (reg))
+
+/* PRCM_MPU instances */
+#define OMAP54XX_PRCM_MPU_OCP_SOCKET_INST      0x0000
+#define OMAP54XX_PRCM_MPU_DEVICE_INST          0x0200
+#define OMAP54XX_PRCM_MPU_PRM_C0_INST          0x0400
+#define OMAP54XX_PRCM_MPU_CM_C0_INST           0x0600
+#define OMAP54XX_PRCM_MPU_PRM_C1_INST          0x0800
+#define OMAP54XX_PRCM_MPU_CM_C1_INST           0x0a00
+
+/* PRCM_MPU clockdomain register offsets (from instance start) */
+#define OMAP54XX_PRCM_MPU_CM_C0_CPU0_CDOFFS    0x0000
+#define OMAP54XX_PRCM_MPU_CM_C1_CPU1_CDOFFS    0x0000
+
+
+/*
+ * PRCM_MPU
+ *
+ * The PRCM_MPU is a local PRCM inside the MPU subsystem. For the PRCM (global)
+ * point of view the PRCM_MPU is a single entity. It shares the same
+ * programming model as the global PRCM and thus can be assimilate as two new
+ * MOD inside the PRCM
+ */
+
+/* PRCM_MPU.PRCM_MPU_OCP_SOCKET register offsets */
+#define OMAP54XX_REVISION_PRCM_MPU_OFFSET                      0x0000
+
+/* PRCM_MPU.PRCM_MPU_DEVICE register offsets */
+#define OMAP54XX_PRCM_MPU_PRM_RSTST_OFFSET                     0x0000
+#define OMAP54XX_PRCM_MPU_PRM_PSCON_COUNT_OFFSET               0x0004
+#define OMAP54XX_PRM_FRAC_INCREMENTER_NUMERATOR_OFFSET         0x0010
+#define OMAP54XX_PRM_FRAC_INCREMENTER_DENUMERATOR_RELOAD_OFFSET        0x0014
+
+/* PRCM_MPU.PRCM_MPU_PRM_C0 register offsets */
+#define OMAP54XX_PM_CPU0_PWRSTCTRL_OFFSET                      0x0000
+#define OMAP54XX_PM_CPU0_PWRSTST_OFFSET                                0x0004
+#define OMAP54XX_RM_CPU0_CPU0_RSTCTRL_OFFSET                   0x0010
+#define OMAP54XX_RM_CPU0_CPU0_RSTST_OFFSET                     0x0014
+#define OMAP54XX_RM_CPU0_CPU0_CONTEXT_OFFSET                   0x0024
+
+/* PRCM_MPU.PRCM_MPU_CM_C0 register offsets */
+#define OMAP54XX_CM_CPU0_CLKSTCTRL_OFFSET                      0x0000
+#define OMAP54XX_CM_CPU0_CPU0_CLKCTRL_OFFSET                   0x0020
+#define OMAP54XX_CM_CPU0_CPU0_CLKCTRL                          OMAP54XX_PRCM_MPU_REGADDR(OMAP54XX_PRCM_MPU_CM_C0_INST, 0x0020)
+
+/* PRCM_MPU.PRCM_MPU_PRM_C1 register offsets */
+#define OMAP54XX_PM_CPU1_PWRSTCTRL_OFFSET                      0x0000
+#define OMAP54XX_PM_CPU1_PWRSTST_OFFSET                                0x0004
+#define OMAP54XX_RM_CPU1_CPU1_RSTCTRL_OFFSET                   0x0010
+#define OMAP54XX_RM_CPU1_CPU1_RSTST_OFFSET                     0x0014
+#define OMAP54XX_RM_CPU1_CPU1_CONTEXT_OFFSET                   0x0024
+
+/* PRCM_MPU.PRCM_MPU_CM_C1 register offsets */
+#define OMAP54XX_CM_CPU1_CLKSTCTRL_OFFSET                      0x0000
+#define OMAP54XX_CM_CPU1_CPU1_CLKCTRL_OFFSET                   0x0020
+#define OMAP54XX_CM_CPU1_CPU1_CLKCTRL                          OMAP54XX_PRCM_MPU_REGADDR(OMAP54XX_PRCM_MPU_CM_C1_INST, 0x0020)
+
+/* Function prototypes */
+# ifndef __ASSEMBLER__
+extern u32 omap4_prcm_mpu_read_inst_reg(s16 inst, u16 idx);
+extern void omap4_prcm_mpu_write_inst_reg(u32 val, s16 inst, u16 idx);
+extern u32 omap4_prcm_mpu_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst,
+                                           s16 idx);
+# endif
+
+#endif
diff --git a/arch/arm/mach-omap2/prm-regbits-54xx.h b/arch/arm/mach-omap2/prm-regbits-54xx.h
new file mode 100644 (file)
index 0000000..be31b21
--- /dev/null
@@ -0,0 +1,2701 @@
+/*
+ * OMAP54xx Power Management register bits
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Paul Walmsley (paul@pwsan.com)
+ * Rajendra Nayak (rnayak@ti.com)
+ * Benoit Cousson (b-cousson@ti.com)
+ *
+ * This file is automatically generated from the OMAP hardware databases.
+ * We respectfully ask that any modifications to this file be coordinated
+ * with the public linux-omap@vger.kernel.org mailing list and the
+ * authors above to ensure that the autogeneration scripts are kept
+ * up-to-date with the file contents.
+ *
+ * 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 __ARCH_ARM_MACH_OMAP2_PRM_REGBITS_54XX_H
+#define __ARCH_ARM_MACH_OMAP2_PRM_REGBITS_54XX_H
+
+/* Used by PRM_SLDO_CORE_SETUP, PRM_SLDO_MM_SETUP, PRM_SLDO_MPU_SETUP */
+#define OMAP54XX_ABBOFF_ACT_SHIFT                                              1
+#define OMAP54XX_ABBOFF_ACT_WIDTH                                              0x1
+#define OMAP54XX_ABBOFF_ACT_MASK                                               (1 << 1)
+
+/* Used by PRM_SLDO_CORE_SETUP, PRM_SLDO_MM_SETUP, PRM_SLDO_MPU_SETUP */
+#define OMAP54XX_ABBOFF_SLEEP_SHIFT                                            2
+#define OMAP54XX_ABBOFF_SLEEP_WIDTH                                            0x1
+#define OMAP54XX_ABBOFF_SLEEP_MASK                                             (1 << 2)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_ABB_MM_DONE_EN_SHIFT                                          31
+#define OMAP54XX_ABB_MM_DONE_EN_WIDTH                                          0x1
+#define OMAP54XX_ABB_MM_DONE_EN_MASK                                           (1 << 31)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_ABB_MM_DONE_ST_SHIFT                                          31
+#define OMAP54XX_ABB_MM_DONE_ST_WIDTH                                          0x1
+#define OMAP54XX_ABB_MM_DONE_ST_MASK                                           (1 << 31)
+
+/* Used by PRM_IRQENABLE_MPU_2 */
+#define OMAP54XX_ABB_MPU_DONE_EN_SHIFT                                         7
+#define OMAP54XX_ABB_MPU_DONE_EN_WIDTH                                         0x1
+#define OMAP54XX_ABB_MPU_DONE_EN_MASK                                          (1 << 7)
+
+/* Used by PRM_IRQSTATUS_MPU_2 */
+#define OMAP54XX_ABB_MPU_DONE_ST_SHIFT                                         7
+#define OMAP54XX_ABB_MPU_DONE_ST_WIDTH                                         0x1
+#define OMAP54XX_ABB_MPU_DONE_ST_MASK                                          (1 << 7)
+
+/* Used by PRM_ABBLDO_MM_SETUP, PRM_ABBLDO_MPU_SETUP */
+#define OMAP54XX_ACTIVE_FBB_SEL_SHIFT                                          2
+#define OMAP54XX_ACTIVE_FBB_SEL_WIDTH                                          0x1
+#define OMAP54XX_ACTIVE_FBB_SEL_MASK                                           (1 << 2)
+
+/* Used by PM_ABE_PWRSTCTRL */
+#define OMAP54XX_AESSMEM_ONSTATE_SHIFT                                         16
+#define OMAP54XX_AESSMEM_ONSTATE_WIDTH                                         0x2
+#define OMAP54XX_AESSMEM_ONSTATE_MASK                                          (0x3 << 16)
+
+/* Used by PM_ABE_PWRSTCTRL */
+#define OMAP54XX_AESSMEM_RETSTATE_SHIFT                                                8
+#define OMAP54XX_AESSMEM_RETSTATE_WIDTH                                                0x1
+#define OMAP54XX_AESSMEM_RETSTATE_MASK                                         (1 << 8)
+
+/* Used by PM_ABE_PWRSTST */
+#define OMAP54XX_AESSMEM_STATEST_SHIFT                                         4
+#define OMAP54XX_AESSMEM_STATEST_WIDTH                                         0x2
+#define OMAP54XX_AESSMEM_STATEST_MASK                                          (0x3 << 4)
+
+/* Used by PRM_SLDO_CORE_SETUP, PRM_SLDO_MM_SETUP, PRM_SLDO_MPU_SETUP */
+#define OMAP54XX_AIPOFF_SHIFT                                                  8
+#define OMAP54XX_AIPOFF_WIDTH                                                  0x1
+#define OMAP54XX_AIPOFF_MASK                                                   (1 << 8)
+
+/* Used by PRM_VOLTCTRL */
+#define OMAP54XX_AUTO_CTRL_VDD_CORE_L_SHIFT                                    0
+#define OMAP54XX_AUTO_CTRL_VDD_CORE_L_WIDTH                                    0x2
+#define OMAP54XX_AUTO_CTRL_VDD_CORE_L_MASK                                     (0x3 << 0)
+
+/* Used by PRM_VOLTCTRL */
+#define OMAP54XX_AUTO_CTRL_VDD_MM_L_SHIFT                                      4
+#define OMAP54XX_AUTO_CTRL_VDD_MM_L_WIDTH                                      0x2
+#define OMAP54XX_AUTO_CTRL_VDD_MM_L_MASK                                       (0x3 << 4)
+
+/* Used by PRM_VOLTCTRL */
+#define OMAP54XX_AUTO_CTRL_VDD_MPU_L_SHIFT                                     2
+#define OMAP54XX_AUTO_CTRL_VDD_MPU_L_WIDTH                                     0x2
+#define OMAP54XX_AUTO_CTRL_VDD_MPU_L_MASK                                      (0x3 << 2)
+
+/* Used by PRM_VC_BYPASS_ERRST */
+#define OMAP54XX_BYPS_RA_ERR_SHIFT                                             1
+#define OMAP54XX_BYPS_RA_ERR_WIDTH                                             0x1
+#define OMAP54XX_BYPS_RA_ERR_MASK                                              (1 << 1)
+
+/* Used by PRM_VC_BYPASS_ERRST */
+#define OMAP54XX_BYPS_SA_ERR_SHIFT                                             0
+#define OMAP54XX_BYPS_SA_ERR_WIDTH                                             0x1
+#define OMAP54XX_BYPS_SA_ERR_MASK                                              (1 << 0)
+
+/* Used by PRM_VC_BYPASS_ERRST */
+#define OMAP54XX_BYPS_TIMEOUT_ERR_SHIFT                                                2
+#define OMAP54XX_BYPS_TIMEOUT_ERR_WIDTH                                                0x1
+#define OMAP54XX_BYPS_TIMEOUT_ERR_MASK                                         (1 << 2)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_C2C_RST_SHIFT                                                 10
+#define OMAP54XX_C2C_RST_WIDTH                                                 0x1
+#define OMAP54XX_C2C_RST_MASK                                                  (1 << 10)
+
+/* Used by PM_CAM_PWRSTCTRL */
+#define OMAP54XX_CAM_MEM_ONSTATE_SHIFT                                         16
+#define OMAP54XX_CAM_MEM_ONSTATE_WIDTH                                         0x2
+#define OMAP54XX_CAM_MEM_ONSTATE_MASK                                          (0x3 << 16)
+
+/* Used by PM_CAM_PWRSTST */
+#define OMAP54XX_CAM_MEM_STATEST_SHIFT                                         4
+#define OMAP54XX_CAM_MEM_STATEST_WIDTH                                         0x2
+#define OMAP54XX_CAM_MEM_STATEST_MASK                                          (0x3 << 4)
+
+/* Used by PRM_CLKREQCTRL */
+#define OMAP54XX_CLKREQ_COND_SHIFT                                             0
+#define OMAP54XX_CLKREQ_COND_WIDTH                                             0x3
+#define OMAP54XX_CLKREQ_COND_MASK                                              (0x7 << 0)
+
+/* Used by PRM_VC_SMPS_CORE_CONFIG */
+#define OMAP54XX_CMDRA_VDD_CORE_L_SHIFT                                                16
+#define OMAP54XX_CMDRA_VDD_CORE_L_WIDTH                                                0x8
+#define OMAP54XX_CMDRA_VDD_CORE_L_MASK                                         (0xff << 16)
+
+/* Used by PRM_VC_SMPS_MM_CONFIG */
+#define OMAP54XX_CMDRA_VDD_MM_L_SHIFT                                          16
+#define OMAP54XX_CMDRA_VDD_MM_L_WIDTH                                          0x8
+#define OMAP54XX_CMDRA_VDD_MM_L_MASK                                           (0xff << 16)
+
+/* Used by PRM_VC_SMPS_MPU_CONFIG */
+#define OMAP54XX_CMDRA_VDD_MPU_L_SHIFT                                         16
+#define OMAP54XX_CMDRA_VDD_MPU_L_WIDTH                                         0x8
+#define OMAP54XX_CMDRA_VDD_MPU_L_MASK                                          (0xff << 16)
+
+/* Used by PRM_VC_SMPS_CORE_CONFIG */
+#define OMAP54XX_CMD_VDD_CORE_L_SHIFT                                          28
+#define OMAP54XX_CMD_VDD_CORE_L_WIDTH                                          0x1
+#define OMAP54XX_CMD_VDD_CORE_L_MASK                                           (1 << 28)
+
+/* Used by PRM_VC_SMPS_MM_CONFIG */
+#define OMAP54XX_CMD_VDD_MM_L_SHIFT                                            28
+#define OMAP54XX_CMD_VDD_MM_L_WIDTH                                            0x1
+#define OMAP54XX_CMD_VDD_MM_L_MASK                                             (1 << 28)
+
+/* Used by PRM_VC_SMPS_MPU_CONFIG */
+#define OMAP54XX_CMD_VDD_MPU_L_SHIFT                                           28
+#define OMAP54XX_CMD_VDD_MPU_L_WIDTH                                           0x1
+#define OMAP54XX_CMD_VDD_MPU_L_MASK                                            (1 << 28)
+
+/* Used by PM_CORE_PWRSTCTRL */
+#define OMAP54XX_CORE_OCMRAM_ONSTATE_SHIFT                                     18
+#define OMAP54XX_CORE_OCMRAM_ONSTATE_WIDTH                                     0x2
+#define OMAP54XX_CORE_OCMRAM_ONSTATE_MASK                                      (0x3 << 18)
+
+/* Used by PM_CORE_PWRSTCTRL */
+#define OMAP54XX_CORE_OCMRAM_RETSTATE_SHIFT                                    9
+#define OMAP54XX_CORE_OCMRAM_RETSTATE_WIDTH                                    0x1
+#define OMAP54XX_CORE_OCMRAM_RETSTATE_MASK                                     (1 << 9)
+
+/* Used by PM_CORE_PWRSTST */
+#define OMAP54XX_CORE_OCMRAM_STATEST_SHIFT                                     6
+#define OMAP54XX_CORE_OCMRAM_STATEST_WIDTH                                     0x2
+#define OMAP54XX_CORE_OCMRAM_STATEST_MASK                                      (0x3 << 6)
+
+/* Used by PM_CORE_PWRSTCTRL */
+#define OMAP54XX_CORE_OTHER_BANK_ONSTATE_SHIFT                                 16
+#define OMAP54XX_CORE_OTHER_BANK_ONSTATE_WIDTH                                 0x2
+#define OMAP54XX_CORE_OTHER_BANK_ONSTATE_MASK                                  (0x3 << 16)
+
+/* Used by PM_CORE_PWRSTCTRL */
+#define OMAP54XX_CORE_OTHER_BANK_RETSTATE_SHIFT                                        8
+#define OMAP54XX_CORE_OTHER_BANK_RETSTATE_WIDTH                                        0x1
+#define OMAP54XX_CORE_OTHER_BANK_RETSTATE_MASK                                 (1 << 8)
+
+/* Used by PM_CORE_PWRSTST */
+#define OMAP54XX_CORE_OTHER_BANK_STATEST_SHIFT                                 4
+#define OMAP54XX_CORE_OTHER_BANK_STATEST_WIDTH                                 0x2
+#define OMAP54XX_CORE_OTHER_BANK_STATEST_MASK                                  (0x3 << 4)
+
+/* Used by REVISION_PRM */
+#define OMAP54XX_CUSTOM_SHIFT                                                  6
+#define OMAP54XX_CUSTOM_WIDTH                                                  0x2
+#define OMAP54XX_CUSTOM_MASK                                                   (0x3 << 6)
+
+/* Used by PRM_VC_VAL_BYPASS */
+#define OMAP54XX_DATA_SHIFT                                                    16
+#define OMAP54XX_DATA_WIDTH                                                    0x8
+#define OMAP54XX_DATA_MASK                                                     (0xff << 16)
+
+/* Used by PRM_DEBUG_CORE_RET_TRANS */
+#define OMAP54XX_PRM_DEBUG_OUT_SHIFT                                           0
+#define OMAP54XX_PRM_DEBUG_OUT_WIDTH                                           0x1c
+#define OMAP54XX_PRM_DEBUG_OUT_MASK                                            (0xfffffff << 0)
+
+/* Renamed from DEBUG_OUT Used by PRM_DEBUG_MM_RET_TRANS */
+#define OMAP54XX_DEBUG_OUT_0_9_SHIFT                                           0
+#define OMAP54XX_DEBUG_OUT_0_9_WIDTH                                           0xa
+#define OMAP54XX_DEBUG_OUT_0_9_MASK                                            (0x3ff << 0)
+
+/* Renamed from DEBUG_OUT Used by PRM_DEBUG_MPU_RET_TRANS */
+#define OMAP54XX_DEBUG_OUT_0_6_SHIFT                                           0
+#define OMAP54XX_DEBUG_OUT_0_6_WIDTH                                           0x7
+#define OMAP54XX_DEBUG_OUT_0_6_MASK                                            (0x7f << 0)
+
+/* Renamed from DEBUG_OUT Used by PRM_DEBUG_OFF_TRANS */
+#define OMAP54XX_DEBUG_OUT_0_31_SHIFT                                          0
+#define OMAP54XX_DEBUG_OUT_0_31_WIDTH                                          0x20
+#define OMAP54XX_DEBUG_OUT_0_31_MASK                                           (0xffffffff << 0)
+
+/* Renamed from DEBUG_OUT Used by PRM_DEBUG_WKUPAON_FD_TRANS */
+#define OMAP54XX_DEBUG_OUT_0_11_SHIFT                                          0
+#define OMAP54XX_DEBUG_OUT_0_11_WIDTH                                          0xc
+#define OMAP54XX_DEBUG_OUT_0_11_MASK                                           (0xfff << 0)
+
+/* Used by PRM_DEVICE_OFF_CTRL */
+#define OMAP54XX_DEVICE_OFF_ENABLE_SHIFT                                       0
+#define OMAP54XX_DEVICE_OFF_ENABLE_WIDTH                                       0x1
+#define OMAP54XX_DEVICE_OFF_ENABLE_MASK                                                (1 << 0)
+
+/* Used by PRM_VC_CFG_I2C_MODE */
+#define OMAP54XX_DFILTEREN_SHIFT                                               6
+#define OMAP54XX_DFILTEREN_WIDTH                                               0x1
+#define OMAP54XX_DFILTEREN_MASK                                                        (1 << 6)
+
+/* Used by PRM_IRQENABLE_DSP, PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_DPLL_ABE_RECAL_EN_SHIFT                                       4
+#define OMAP54XX_DPLL_ABE_RECAL_EN_WIDTH                                       0x1
+#define OMAP54XX_DPLL_ABE_RECAL_EN_MASK                                                (1 << 4)
+
+/* Used by PRM_IRQSTATUS_DSP, PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_DPLL_ABE_RECAL_ST_SHIFT                                       4
+#define OMAP54XX_DPLL_ABE_RECAL_ST_WIDTH                                       0x1
+#define OMAP54XX_DPLL_ABE_RECAL_ST_MASK                                                (1 << 4)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_DPLL_CORE_RECAL_EN_SHIFT                                      0
+#define OMAP54XX_DPLL_CORE_RECAL_EN_WIDTH                                      0x1
+#define OMAP54XX_DPLL_CORE_RECAL_EN_MASK                                       (1 << 0)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_DPLL_CORE_RECAL_ST_SHIFT                                      0
+#define OMAP54XX_DPLL_CORE_RECAL_ST_WIDTH                                      0x1
+#define OMAP54XX_DPLL_CORE_RECAL_ST_MASK                                       (1 << 0)
+
+/* Used by PRM_IRQENABLE_DSP, PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_DPLL_IVA_RECAL_EN_SHIFT                                       2
+#define OMAP54XX_DPLL_IVA_RECAL_EN_WIDTH                                       0x1
+#define OMAP54XX_DPLL_IVA_RECAL_EN_MASK                                                (1 << 2)
+
+/* Used by PRM_IRQSTATUS_DSP, PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_DPLL_IVA_RECAL_ST_SHIFT                                       2
+#define OMAP54XX_DPLL_IVA_RECAL_ST_WIDTH                                       0x1
+#define OMAP54XX_DPLL_IVA_RECAL_ST_MASK                                                (1 << 2)
+
+/* Used by PRM_IRQENABLE_MPU */
+#define OMAP54XX_DPLL_MPU_RECAL_EN_SHIFT                                       1
+#define OMAP54XX_DPLL_MPU_RECAL_EN_WIDTH                                       0x1
+#define OMAP54XX_DPLL_MPU_RECAL_EN_MASK                                                (1 << 1)
+
+/* Used by PRM_IRQSTATUS_MPU */
+#define OMAP54XX_DPLL_MPU_RECAL_ST_SHIFT                                       1
+#define OMAP54XX_DPLL_MPU_RECAL_ST_WIDTH                                       0x1
+#define OMAP54XX_DPLL_MPU_RECAL_ST_MASK                                                (1 << 1)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_DPLL_PER_RECAL_EN_SHIFT                                       3
+#define OMAP54XX_DPLL_PER_RECAL_EN_WIDTH                                       0x1
+#define OMAP54XX_DPLL_PER_RECAL_EN_MASK                                                (1 << 3)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_DPLL_PER_RECAL_ST_SHIFT                                       3
+#define OMAP54XX_DPLL_PER_RECAL_ST_WIDTH                                       0x1
+#define OMAP54XX_DPLL_PER_RECAL_ST_MASK                                                (1 << 3)
+
+/* Used by PM_DSP_PWRSTCTRL */
+#define OMAP54XX_DSP_EDMA_ONSTATE_SHIFT                                                20
+#define OMAP54XX_DSP_EDMA_ONSTATE_WIDTH                                                0x2
+#define OMAP54XX_DSP_EDMA_ONSTATE_MASK                                         (0x3 << 20)
+
+/* Used by PM_DSP_PWRSTCTRL */
+#define OMAP54XX_DSP_EDMA_RETSTATE_SHIFT                                       10
+#define OMAP54XX_DSP_EDMA_RETSTATE_WIDTH                                       0x1
+#define OMAP54XX_DSP_EDMA_RETSTATE_MASK                                                (1 << 10)
+
+/* Used by PM_DSP_PWRSTST */
+#define OMAP54XX_DSP_EDMA_STATEST_SHIFT                                                8
+#define OMAP54XX_DSP_EDMA_STATEST_WIDTH                                                0x2
+#define OMAP54XX_DSP_EDMA_STATEST_MASK                                         (0x3 << 8)
+
+/* Used by PM_DSP_PWRSTCTRL */
+#define OMAP54XX_DSP_L1_ONSTATE_SHIFT                                          16
+#define OMAP54XX_DSP_L1_ONSTATE_WIDTH                                          0x2
+#define OMAP54XX_DSP_L1_ONSTATE_MASK                                           (0x3 << 16)
+
+/* Used by PM_DSP_PWRSTCTRL */
+#define OMAP54XX_DSP_L1_RETSTATE_SHIFT                                         8
+#define OMAP54XX_DSP_L1_RETSTATE_WIDTH                                         0x1
+#define OMAP54XX_DSP_L1_RETSTATE_MASK                                          (1 << 8)
+
+/* Used by PM_DSP_PWRSTST */
+#define OMAP54XX_DSP_L1_STATEST_SHIFT                                          4
+#define OMAP54XX_DSP_L1_STATEST_WIDTH                                          0x2
+#define OMAP54XX_DSP_L1_STATEST_MASK                                           (0x3 << 4)
+
+/* Used by PM_DSP_PWRSTCTRL */
+#define OMAP54XX_DSP_L2_ONSTATE_SHIFT                                          18
+#define OMAP54XX_DSP_L2_ONSTATE_WIDTH                                          0x2
+#define OMAP54XX_DSP_L2_ONSTATE_MASK                                           (0x3 << 18)
+
+/* Used by PM_DSP_PWRSTCTRL */
+#define OMAP54XX_DSP_L2_RETSTATE_SHIFT                                         9
+#define OMAP54XX_DSP_L2_RETSTATE_WIDTH                                         0x1
+#define OMAP54XX_DSP_L2_RETSTATE_MASK                                          (1 << 9)
+
+/* Used by PM_DSP_PWRSTST */
+#define OMAP54XX_DSP_L2_STATEST_SHIFT                                          6
+#define OMAP54XX_DSP_L2_STATEST_WIDTH                                          0x2
+#define OMAP54XX_DSP_L2_STATEST_MASK                                           (0x3 << 6)
+
+/* Used by PM_DSS_PWRSTCTRL */
+#define OMAP54XX_DSS_MEM_ONSTATE_SHIFT                                         16
+#define OMAP54XX_DSS_MEM_ONSTATE_WIDTH                                         0x2
+#define OMAP54XX_DSS_MEM_ONSTATE_MASK                                          (0x3 << 16)
+
+/* Used by PM_DSS_PWRSTCTRL */
+#define OMAP54XX_DSS_MEM_RETSTATE_SHIFT                                                8
+#define OMAP54XX_DSS_MEM_RETSTATE_WIDTH                                                0x1
+#define OMAP54XX_DSS_MEM_RETSTATE_MASK                                         (1 << 8)
+
+/* Used by PM_DSS_PWRSTST */
+#define OMAP54XX_DSS_MEM_STATEST_SHIFT                                         4
+#define OMAP54XX_DSS_MEM_STATEST_WIDTH                                         0x2
+#define OMAP54XX_DSS_MEM_STATEST_MASK                                          (0x3 << 4)
+
+/* Used by PRM_DEVICE_OFF_CTRL */
+#define OMAP54XX_EMIF1_OFFWKUP_DISABLE_SHIFT                                   8
+#define OMAP54XX_EMIF1_OFFWKUP_DISABLE_WIDTH                                   0x1
+#define OMAP54XX_EMIF1_OFFWKUP_DISABLE_MASK                                    (1 << 8)
+
+/* Used by PRM_DEVICE_OFF_CTRL */
+#define OMAP54XX_EMIF2_OFFWKUP_DISABLE_SHIFT                                   9
+#define OMAP54XX_EMIF2_OFFWKUP_DISABLE_WIDTH                                   0x1
+#define OMAP54XX_EMIF2_OFFWKUP_DISABLE_MASK                                    (1 << 9)
+
+/* Used by PM_EMU_PWRSTCTRL */
+#define OMAP54XX_EMU_BANK_ONSTATE_SHIFT                                                16
+#define OMAP54XX_EMU_BANK_ONSTATE_WIDTH                                                0x2
+#define OMAP54XX_EMU_BANK_ONSTATE_MASK                                         (0x3 << 16)
+
+/* Used by PM_EMU_PWRSTST */
+#define OMAP54XX_EMU_BANK_STATEST_SHIFT                                                4
+#define OMAP54XX_EMU_BANK_STATEST_WIDTH                                                0x2
+#define OMAP54XX_EMU_BANK_STATEST_MASK                                         (0x3 << 4)
+
+/*
+ * Used by PRM_SLDO_CORE_SETUP, PRM_SLDO_MM_SETUP, PRM_SLDO_MPU_SETUP,
+ * PRM_SRAM_WKUP_SETUP
+ */
+#define OMAP54XX_ENABLE_RTA_SHIFT                                              0
+#define OMAP54XX_ENABLE_RTA_WIDTH                                              0x1
+#define OMAP54XX_ENABLE_RTA_MASK                                               (1 << 0)
+
+/* Used by PRM_SLDO_CORE_SETUP, PRM_SLDO_MM_SETUP, PRM_SLDO_MPU_SETUP */
+#define OMAP54XX_ENFUNC1_SHIFT                                                 3
+#define OMAP54XX_ENFUNC1_WIDTH                                                 0x1
+#define OMAP54XX_ENFUNC1_MASK                                                  (1 << 3)
+
+/* Used by PRM_SLDO_CORE_SETUP, PRM_SLDO_MM_SETUP, PRM_SLDO_MPU_SETUP */
+#define OMAP54XX_ENFUNC2_SHIFT                                                 4
+#define OMAP54XX_ENFUNC2_WIDTH                                                 0x1
+#define OMAP54XX_ENFUNC2_MASK                                                  (1 << 4)
+
+/* Used by PRM_SLDO_CORE_SETUP, PRM_SLDO_MM_SETUP, PRM_SLDO_MPU_SETUP */
+#define OMAP54XX_ENFUNC3_SHIFT                                                 5
+#define OMAP54XX_ENFUNC3_WIDTH                                                 0x1
+#define OMAP54XX_ENFUNC3_MASK                                                  (1 << 5)
+
+/* Used by PRM_SLDO_CORE_SETUP, PRM_SLDO_MM_SETUP, PRM_SLDO_MPU_SETUP */
+#define OMAP54XX_ENFUNC4_SHIFT                                                 6
+#define OMAP54XX_ENFUNC4_WIDTH                                                 0x1
+#define OMAP54XX_ENFUNC4_MASK                                                  (1 << 6)
+
+/* Used by PRM_SLDO_CORE_SETUP, PRM_SLDO_MM_SETUP, PRM_SLDO_MPU_SETUP */
+#define OMAP54XX_ENFUNC5_SHIFT                                                 7
+#define OMAP54XX_ENFUNC5_WIDTH                                                 0x1
+#define OMAP54XX_ENFUNC5_MASK                                                  (1 << 7)
+
+/* Used by PRM_VP_CORE_CONFIG, PRM_VP_MM_CONFIG, PRM_VP_MPU_CONFIG */
+#define OMAP54XX_ERRORGAIN_SHIFT                                               16
+#define OMAP54XX_ERRORGAIN_WIDTH                                               0x8
+#define OMAP54XX_ERRORGAIN_MASK                                                        (0xff << 16)
+
+/* Used by PRM_VP_CORE_CONFIG, PRM_VP_MM_CONFIG, PRM_VP_MPU_CONFIG */
+#define OMAP54XX_ERROROFFSET_SHIFT                                             24
+#define OMAP54XX_ERROROFFSET_WIDTH                                             0x8
+#define OMAP54XX_ERROROFFSET_MASK                                              (0xff << 24)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_EXTERNAL_WARM_RST_SHIFT                                       5
+#define OMAP54XX_EXTERNAL_WARM_RST_WIDTH                                       0x1
+#define OMAP54XX_EXTERNAL_WARM_RST_MASK                                                (1 << 5)
+
+/* Used by PRM_VP_CORE_CONFIG, PRM_VP_MM_CONFIG, PRM_VP_MPU_CONFIG */
+#define OMAP54XX_FORCEUPDATE_SHIFT                                             1
+#define OMAP54XX_FORCEUPDATE_WIDTH                                             0x1
+#define OMAP54XX_FORCEUPDATE_MASK                                              (1 << 1)
+
+/* Used by PRM_VP_CORE_VOLTAGE, PRM_VP_MM_VOLTAGE, PRM_VP_MPU_VOLTAGE */
+#define OMAP54XX_FORCEUPDATEWAIT_SHIFT                                         8
+#define OMAP54XX_FORCEUPDATEWAIT_WIDTH                                         0x18
+#define OMAP54XX_FORCEUPDATEWAIT_MASK                                          (0xffffff << 8)
+
+/* Used by PRM_IRQENABLE_DSP, PRM_IRQENABLE_IPU */
+#define OMAP54XX_FORCEWKUP_EN_SHIFT                                            10
+#define OMAP54XX_FORCEWKUP_EN_WIDTH                                            0x1
+#define OMAP54XX_FORCEWKUP_EN_MASK                                             (1 << 10)
+
+/* Used by PRM_IRQSTATUS_DSP, PRM_IRQSTATUS_IPU */
+#define OMAP54XX_FORCEWKUP_ST_SHIFT                                            10
+#define OMAP54XX_FORCEWKUP_ST_WIDTH                                            0x1
+#define OMAP54XX_FORCEWKUP_ST_MASK                                             (1 << 10)
+
+/* Used by REVISION_PRM */
+#define OMAP54XX_FUNC_SHIFT                                                    16
+#define OMAP54XX_FUNC_WIDTH                                                    0xc
+#define OMAP54XX_FUNC_MASK                                                     (0xfff << 16)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_GLOBAL_COLD_RST_SHIFT                                         0
+#define OMAP54XX_GLOBAL_COLD_RST_WIDTH                                         0x1
+#define OMAP54XX_GLOBAL_COLD_RST_MASK                                          (1 << 0)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_GLOBAL_WARM_SW_RST_SHIFT                                      1
+#define OMAP54XX_GLOBAL_WARM_SW_RST_WIDTH                                      0x1
+#define OMAP54XX_GLOBAL_WARM_SW_RST_MASK                                       (1 << 1)
+
+/* Used by PRM_IO_PMCTRL */
+#define OMAP54XX_GLOBAL_WUEN_SHIFT                                             16
+#define OMAP54XX_GLOBAL_WUEN_WIDTH                                             0x1
+#define OMAP54XX_GLOBAL_WUEN_MASK                                              (1 << 16)
+
+/* Used by PM_GPU_PWRSTCTRL */
+#define OMAP54XX_GPU_MEM_ONSTATE_SHIFT                                         16
+#define OMAP54XX_GPU_MEM_ONSTATE_WIDTH                                         0x2
+#define OMAP54XX_GPU_MEM_ONSTATE_MASK                                          (0x3 << 16)
+
+/* Used by PM_GPU_PWRSTST */
+#define OMAP54XX_GPU_MEM_STATEST_SHIFT                                         4
+#define OMAP54XX_GPU_MEM_STATEST_WIDTH                                         0x2
+#define OMAP54XX_GPU_MEM_STATEST_MASK                                          (0x3 << 4)
+
+/* Used by PRM_VC_CFG_I2C_MODE */
+#define OMAP54XX_HSMCODE_SHIFT                                                 0
+#define OMAP54XX_HSMCODE_WIDTH                                                 0x3
+#define OMAP54XX_HSMCODE_MASK                                                  (0x7 << 0)
+
+/* Used by PRM_VC_CFG_I2C_MODE */
+#define OMAP54XX_HSMODEEN_SHIFT                                                        3
+#define OMAP54XX_HSMODEEN_WIDTH                                                        0x1
+#define OMAP54XX_HSMODEEN_MASK                                                 (1 << 3)
+
+/* Used by PRM_VC_CFG_I2C_CLK */
+#define OMAP54XX_HSSCLH_SHIFT                                                  16
+#define OMAP54XX_HSSCLH_WIDTH                                                  0x8
+#define OMAP54XX_HSSCLH_MASK                                                   (0xff << 16)
+
+/* Used by PRM_VC_CFG_I2C_CLK */
+#define OMAP54XX_HSSCLL_SHIFT                                                  24
+#define OMAP54XX_HSSCLL_WIDTH                                                  0x8
+#define OMAP54XX_HSSCLL_MASK                                                   (0xff << 24)
+
+/* Used by PM_IVA_PWRSTCTRL */
+#define OMAP54XX_HWA_MEM_ONSTATE_SHIFT                                         16
+#define OMAP54XX_HWA_MEM_ONSTATE_WIDTH                                         0x2
+#define OMAP54XX_HWA_MEM_ONSTATE_MASK                                          (0x3 << 16)
+
+/* Used by PM_IVA_PWRSTCTRL */
+#define OMAP54XX_HWA_MEM_RETSTATE_SHIFT                                                8
+#define OMAP54XX_HWA_MEM_RETSTATE_WIDTH                                                0x1
+#define OMAP54XX_HWA_MEM_RETSTATE_MASK                                         (1 << 8)
+
+/* Used by PM_IVA_PWRSTST */
+#define OMAP54XX_HWA_MEM_STATEST_SHIFT                                         4
+#define OMAP54XX_HWA_MEM_STATEST_WIDTH                                         0x2
+#define OMAP54XX_HWA_MEM_STATEST_MASK                                          (0x3 << 4)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_ICEPICK_RST_SHIFT                                             9
+#define OMAP54XX_ICEPICK_RST_WIDTH                                             0x1
+#define OMAP54XX_ICEPICK_RST_MASK                                              (1 << 9)
+
+/* Used by PRM_VP_CORE_CONFIG, PRM_VP_MM_CONFIG, PRM_VP_MPU_CONFIG */
+#define OMAP54XX_INITVDD_SHIFT                                                 2
+#define OMAP54XX_INITVDD_WIDTH                                                 0x1
+#define OMAP54XX_INITVDD_MASK                                                  (1 << 2)
+
+/* Used by PRM_VP_CORE_CONFIG, PRM_VP_MM_CONFIG, PRM_VP_MPU_CONFIG */
+#define OMAP54XX_INITVOLTAGE_SHIFT                                             8
+#define OMAP54XX_INITVOLTAGE_WIDTH                                             0x8
+#define OMAP54XX_INITVOLTAGE_MASK                                              (0xff << 8)
+
+/*
+ * Used by PM_ABE_PWRSTST, PM_CAM_PWRSTST, PM_CORE_PWRSTST,
+ * PM_CUSTEFUSE_PWRSTST, PM_DSP_PWRSTST, PM_DSS_PWRSTST, PM_EMU_PWRSTST,
+ * PM_GPU_PWRSTST, PM_IVA_PWRSTST, PM_L3INIT_PWRSTST, PM_MPU_PWRSTST,
+ * PRM_VOLTST_MM, PRM_VOLTST_MPU
+ */
+#define OMAP54XX_INTRANSITION_SHIFT                                            20
+#define OMAP54XX_INTRANSITION_WIDTH                                            0x1
+#define OMAP54XX_INTRANSITION_MASK                                             (1 << 20)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_IO_EN_SHIFT                                                   9
+#define OMAP54XX_IO_EN_WIDTH                                                   0x1
+#define OMAP54XX_IO_EN_MASK                                                    (1 << 9)
+
+/* Used by PRM_IO_PMCTRL */
+#define OMAP54XX_IO_ON_STATUS_SHIFT                                            5
+#define OMAP54XX_IO_ON_STATUS_WIDTH                                            0x1
+#define OMAP54XX_IO_ON_STATUS_MASK                                             (1 << 5)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_IO_ST_SHIFT                                                   9
+#define OMAP54XX_IO_ST_WIDTH                                                   0x1
+#define OMAP54XX_IO_ST_MASK                                                    (1 << 9)
+
+/* Used by PM_CORE_PWRSTCTRL */
+#define OMAP54XX_IPU_L2RAM_ONSTATE_SHIFT                                       20
+#define OMAP54XX_IPU_L2RAM_ONSTATE_WIDTH                                       0x2
+#define OMAP54XX_IPU_L2RAM_ONSTATE_MASK                                                (0x3 << 20)
+
+/* Used by PM_CORE_PWRSTCTRL */
+#define OMAP54XX_IPU_L2RAM_RETSTATE_SHIFT                                      10
+#define OMAP54XX_IPU_L2RAM_RETSTATE_WIDTH                                      0x1
+#define OMAP54XX_IPU_L2RAM_RETSTATE_MASK                                       (1 << 10)
+
+/* Used by PM_CORE_PWRSTST */
+#define OMAP54XX_IPU_L2RAM_STATEST_SHIFT                                       8
+#define OMAP54XX_IPU_L2RAM_STATEST_WIDTH                                       0x2
+#define OMAP54XX_IPU_L2RAM_STATEST_MASK                                                (0x3 << 8)
+
+/* Used by PM_CORE_PWRSTCTRL */
+#define OMAP54XX_IPU_UNICACHE_ONSTATE_SHIFT                                    22
+#define OMAP54XX_IPU_UNICACHE_ONSTATE_WIDTH                                    0x2
+#define OMAP54XX_IPU_UNICACHE_ONSTATE_MASK                                     (0x3 << 22)
+
+/* Used by PM_CORE_PWRSTCTRL */
+#define OMAP54XX_IPU_UNICACHE_RETSTATE_SHIFT                                   11
+#define OMAP54XX_IPU_UNICACHE_RETSTATE_WIDTH                                   0x1
+#define OMAP54XX_IPU_UNICACHE_RETSTATE_MASK                                    (1 << 11)
+
+/* Used by PM_CORE_PWRSTST */
+#define OMAP54XX_IPU_UNICACHE_STATEST_SHIFT                                    10
+#define OMAP54XX_IPU_UNICACHE_STATEST_WIDTH                                    0x2
+#define OMAP54XX_IPU_UNICACHE_STATEST_MASK                                     (0x3 << 10)
+
+/* Used by PRM_IO_PMCTRL */
+#define OMAP54XX_ISOCLK_OVERRIDE_SHIFT                                         0
+#define OMAP54XX_ISOCLK_OVERRIDE_WIDTH                                         0x1
+#define OMAP54XX_ISOCLK_OVERRIDE_MASK                                          (1 << 0)
+
+/* Used by PRM_IO_PMCTRL */
+#define OMAP54XX_ISOCLK_STATUS_SHIFT                                           1
+#define OMAP54XX_ISOCLK_STATUS_WIDTH                                           0x1
+#define OMAP54XX_ISOCLK_STATUS_MASK                                            (1 << 1)
+
+/* Used by PRM_IO_PMCTRL */
+#define OMAP54XX_ISOOVR_EXTEND_SHIFT                                           4
+#define OMAP54XX_ISOOVR_EXTEND_WIDTH                                           0x1
+#define OMAP54XX_ISOOVR_EXTEND_MASK                                            (1 << 4)
+
+/* Used by PRM_IO_COUNT */
+#define OMAP54XX_ISO_2_ON_TIME_SHIFT                                           0
+#define OMAP54XX_ISO_2_ON_TIME_WIDTH                                           0x8
+#define OMAP54XX_ISO_2_ON_TIME_MASK                                            (0xff << 0)
+
+/* Used by PM_L3INIT_PWRSTCTRL */
+#define OMAP54XX_L3INIT_BANK1_ONSTATE_SHIFT                                    16
+#define OMAP54XX_L3INIT_BANK1_ONSTATE_WIDTH                                    0x2
+#define OMAP54XX_L3INIT_BANK1_ONSTATE_MASK                                     (0x3 << 16)
+
+/* Used by PM_L3INIT_PWRSTCTRL */
+#define OMAP54XX_L3INIT_BANK1_RETSTATE_SHIFT                                   8
+#define OMAP54XX_L3INIT_BANK1_RETSTATE_WIDTH                                   0x1
+#define OMAP54XX_L3INIT_BANK1_RETSTATE_MASK                                    (1 << 8)
+
+/* Used by PM_L3INIT_PWRSTST */
+#define OMAP54XX_L3INIT_BANK1_STATEST_SHIFT                                    4
+#define OMAP54XX_L3INIT_BANK1_STATEST_WIDTH                                    0x2
+#define OMAP54XX_L3INIT_BANK1_STATEST_MASK                                     (0x3 << 4)
+
+/* Used by PM_L3INIT_PWRSTCTRL */
+#define OMAP54XX_L3INIT_BANK2_ONSTATE_SHIFT                                    18
+#define OMAP54XX_L3INIT_BANK2_ONSTATE_WIDTH                                    0x2
+#define OMAP54XX_L3INIT_BANK2_ONSTATE_MASK                                     (0x3 << 18)
+
+/* Used by PM_L3INIT_PWRSTCTRL */
+#define OMAP54XX_L3INIT_BANK2_RETSTATE_SHIFT                                   9
+#define OMAP54XX_L3INIT_BANK2_RETSTATE_WIDTH                                   0x1
+#define OMAP54XX_L3INIT_BANK2_RETSTATE_MASK                                    (1 << 9)
+
+/* Used by PM_L3INIT_PWRSTST */
+#define OMAP54XX_L3INIT_BANK2_STATEST_SHIFT                                    6
+#define OMAP54XX_L3INIT_BANK2_STATEST_WIDTH                                    0x2
+#define OMAP54XX_L3INIT_BANK2_STATEST_MASK                                     (0x3 << 6)
+
+/*
+ * Used by PM_ABE_PWRSTST, PM_CAM_PWRSTST, PM_CORE_PWRSTST,
+ * PM_CUSTEFUSE_PWRSTST, PM_DSP_PWRSTST, PM_DSS_PWRSTST, PM_EMU_PWRSTST,
+ * PM_GPU_PWRSTST, PM_IVA_PWRSTST, PM_L3INIT_PWRSTST, PM_MPU_PWRSTST
+ */
+#define OMAP54XX_LASTPOWERSTATEENTERED_SHIFT                                   24
+#define OMAP54XX_LASTPOWERSTATEENTERED_WIDTH                                   0x2
+#define OMAP54XX_LASTPOWERSTATEENTERED_MASK                                    (0x3 << 24)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_LLI_RST_SHIFT                                                 14
+#define OMAP54XX_LLI_RST_WIDTH                                                 0x1
+#define OMAP54XX_LLI_RST_MASK                                                  (1 << 14)
+
+/*
+ * Used by PM_ABE_PWRSTCTRL, PM_CORE_PWRSTCTRL, PM_DSP_PWRSTCTRL,
+ * PM_DSS_PWRSTCTRL, PM_IVA_PWRSTCTRL, PM_L3INIT_PWRSTCTRL, PM_MPU_PWRSTCTRL
+ */
+#define OMAP54XX_LOGICRETSTATE_SHIFT                                           2
+#define OMAP54XX_LOGICRETSTATE_WIDTH                                           0x1
+#define OMAP54XX_LOGICRETSTATE_MASK                                            (1 << 2)
+
+/*
+ * Used by PM_ABE_PWRSTST, PM_CAM_PWRSTST, PM_CORE_PWRSTST,
+ * PM_CUSTEFUSE_PWRSTST, PM_DSP_PWRSTST, PM_DSS_PWRSTST, PM_EMU_PWRSTST,
+ * PM_GPU_PWRSTST, PM_IVA_PWRSTST, PM_L3INIT_PWRSTST, PM_MPU_PWRSTST
+ */
+#define OMAP54XX_LOGICSTATEST_SHIFT                                            2
+#define OMAP54XX_LOGICSTATEST_WIDTH                                            0x1
+#define OMAP54XX_LOGICSTATEST_MASK                                             (1 << 2)
+
+/*
+ * Used by RM_ABE_AESS_CONTEXT, RM_ABE_DMIC_CONTEXT, RM_ABE_MCASP_CONTEXT,
+ * RM_ABE_MCBSP1_CONTEXT, RM_ABE_MCBSP2_CONTEXT, RM_ABE_MCBSP3_CONTEXT,
+ * RM_ABE_MCPDM_CONTEXT, RM_ABE_SLIMBUS1_CONTEXT, RM_ABE_TIMER5_CONTEXT,
+ * RM_ABE_TIMER6_CONTEXT, RM_ABE_TIMER7_CONTEXT, RM_ABE_TIMER8_CONTEXT,
+ * RM_ABE_WD_TIMER3_CONTEXT, RM_C2C_C2C_CONTEXT, RM_C2C_C2C_OCP_FW_CONTEXT,
+ * RM_CAM_CAL_CONTEXT, RM_CAM_FDIF_CONTEXT, RM_CAM_ISS_CONTEXT,
+ * RM_COREAON_SMARTREFLEX_CORE_CONTEXT, RM_COREAON_SMARTREFLEX_MM_CONTEXT,
+ * RM_COREAON_SMARTREFLEX_MPU_CONTEXT, RM_CUSTEFUSE_EFUSE_CTRL_CUST_CONTEXT,
+ * RM_DSP_DSP_CONTEXT, RM_DSS_BB2D_CONTEXT, RM_DSS_DSS_CONTEXT,
+ * RM_EMIF_DMM_CONTEXT, RM_EMIF_EMIF1_CONTEXT, RM_EMIF_EMIF2_CONTEXT,
+ * RM_EMIF_EMIF_DLL_CONTEXT, RM_EMIF_EMIF_OCP_FW_CONTEXT,
+ * RM_EMU_DEBUGSS_CONTEXT, RM_GPU_GPU_CONTEXT, RM_IPU_IPU_CONTEXT,
+ * RM_IVA_IVA_CONTEXT, RM_IVA_SL2_CONTEXT, RM_L3INIT_IEEE1500_2_OCP_CONTEXT,
+ * RM_L3INIT_OCP2SCP1_CONTEXT, RM_L3INIT_OCP2SCP3_CONTEXT,
+ * RM_L3INIT_SATA_CONTEXT, RM_L3INIT_UNIPRO2_CONTEXT,
+ * RM_L3INSTR_L3_INSTR_CONTEXT, RM_L3INSTR_L3_MAIN_3_CONTEXT,
+ * RM_L3INSTR_OCP_WP_NOC_CONTEXT, RM_L3MAIN1_L3_MAIN_1_CONTEXT,
+ * RM_L3MAIN2_L3_MAIN_2_CONTEXT, RM_L3MAIN2_OCMC_RAM_CONTEXT,
+ * RM_L4CFG_L4_CFG_CONTEXT, RM_L4CFG_OCP2SCP2_CONTEXT,
+ * RM_L4CFG_SAR_ROM_CONTEXT, RM_L4PER_ELM_CONTEXT, RM_L4PER_HDQ1W_CONTEXT,
+ * RM_L4PER_I2C2_CONTEXT, RM_L4PER_I2C3_CONTEXT, RM_L4PER_I2C4_CONTEXT,
+ * RM_L4PER_I2C5_CONTEXT, RM_L4PER_L4_PER_CONTEXT, RM_L4PER_MCSPI1_CONTEXT,
+ * RM_L4PER_MCSPI2_CONTEXT, RM_L4PER_MCSPI3_CONTEXT, RM_L4PER_MCSPI4_CONTEXT,
+ * RM_L4PER_MMC3_CONTEXT, RM_L4PER_MMC4_CONTEXT, RM_L4PER_MMC5_CONTEXT,
+ * RM_L4PER_TIMER10_CONTEXT, RM_L4PER_TIMER11_CONTEXT, RM_L4PER_TIMER2_CONTEXT,
+ * RM_L4PER_TIMER3_CONTEXT, RM_L4PER_TIMER4_CONTEXT, RM_L4PER_TIMER9_CONTEXT,
+ * RM_L4SEC_FPKA_CONTEXT, RM_MIPIEXT_LLI_CONTEXT,
+ * RM_MIPIEXT_LLI_OCP_FW_CONTEXT, RM_MIPIEXT_MPHY_CONTEXT, RM_MPU_MPU_CONTEXT,
+ * RM_WKUPAON_COUNTER_32K_CONTEXT, RM_WKUPAON_GPIO1_CONTEXT,
+ * RM_WKUPAON_KBD_CONTEXT, RM_WKUPAON_L4_WKUP_CONTEXT,
+ * RM_WKUPAON_SAR_RAM_CONTEXT, RM_WKUPAON_TIMER12_CONTEXT,
+ * RM_WKUPAON_TIMER1_CONTEXT, RM_WKUPAON_WD_TIMER1_CONTEXT,
+ * RM_WKUPAON_WD_TIMER2_CONTEXT
+ */
+#define OMAP54XX_LOSTCONTEXT_DFF_SHIFT                                         0
+#define OMAP54XX_LOSTCONTEXT_DFF_WIDTH                                         0x1
+#define OMAP54XX_LOSTCONTEXT_DFF_MASK                                          (1 << 0)
+
+/*
+ * Used by RM_C2C_C2C_CONTEXT, RM_C2C_C2C_OCP_FW_CONTEXT,
+ * RM_C2C_MODEM_ICR_CONTEXT, RM_DMA_DMA_SYSTEM_CONTEXT, RM_DSP_DSP_CONTEXT,
+ * RM_DSS_DSS_CONTEXT, RM_EMIF_DMM_CONTEXT, RM_EMIF_EMIF1_CONTEXT,
+ * RM_EMIF_EMIF2_CONTEXT, RM_EMIF_EMIF_OCP_FW_CONTEXT, RM_IPU_IPU_CONTEXT,
+ * RM_L3INIT_HSI_CONTEXT, RM_L3INIT_MMC1_CONTEXT, RM_L3INIT_MMC2_CONTEXT,
+ * RM_L3INIT_USB_HOST_HS_CONTEXT, RM_L3INIT_USB_OTG_SS_CONTEXT,
+ * RM_L3INIT_USB_TLL_HS_CONTEXT, RM_L3INSTR_L3_MAIN_3_CONTEXT,
+ * RM_L3INSTR_OCP_WP_NOC_CONTEXT, RM_L3MAIN1_L3_MAIN_1_CONTEXT,
+ * RM_L3MAIN2_GPMC_CONTEXT, RM_L3MAIN2_L3_MAIN_2_CONTEXT,
+ * RM_L4CFG_L4_CFG_CONTEXT, RM_L4CFG_MAILBOX_CONTEXT,
+ * RM_L4CFG_SPINLOCK_CONTEXT, RM_L4PER_GPIO2_CONTEXT, RM_L4PER_GPIO3_CONTEXT,
+ * RM_L4PER_GPIO4_CONTEXT, RM_L4PER_GPIO5_CONTEXT, RM_L4PER_GPIO6_CONTEXT,
+ * RM_L4PER_GPIO7_CONTEXT, RM_L4PER_GPIO8_CONTEXT, RM_L4PER_I2C1_CONTEXT,
+ * RM_L4PER_L4_PER_CONTEXT, RM_L4PER_UART1_CONTEXT, RM_L4PER_UART2_CONTEXT,
+ * RM_L4PER_UART3_CONTEXT, RM_L4PER_UART4_CONTEXT, RM_L4PER_UART5_CONTEXT,
+ * RM_L4PER_UART6_CONTEXT, RM_L4SEC_AES1_CONTEXT, RM_L4SEC_AES2_CONTEXT,
+ * RM_L4SEC_DES3DES_CONTEXT, RM_L4SEC_DMA_CRYPTO_CONTEXT, RM_L4SEC_RNG_CONTEXT,
+ * RM_L4SEC_SHA2MD5_CONTEXT, RM_MIPIEXT_LLI_CONTEXT,
+ * RM_MIPIEXT_LLI_OCP_FW_CONTEXT, RM_MIPIEXT_MPHY_CONTEXT, RM_MPU_MPU_CONTEXT
+ */
+#define OMAP54XX_LOSTCONTEXT_RFF_SHIFT                                         1
+#define OMAP54XX_LOSTCONTEXT_RFF_WIDTH                                         0x1
+#define OMAP54XX_LOSTCONTEXT_RFF_MASK                                          (1 << 1)
+
+/* Used by RM_ABE_AESS_CONTEXT */
+#define OMAP54XX_LOSTMEM_AESSMEM_SHIFT                                         8
+#define OMAP54XX_LOSTMEM_AESSMEM_WIDTH                                         0x1
+#define OMAP54XX_LOSTMEM_AESSMEM_MASK                                          (1 << 8)
+
+/* Used by RM_CAM_CAL_CONTEXT */
+#define OMAP54XX_LOSTMEM_CAL_MEM_SHIFT                                         8
+#define OMAP54XX_LOSTMEM_CAL_MEM_WIDTH                                         0x1
+#define OMAP54XX_LOSTMEM_CAL_MEM_MASK                                          (1 << 8)
+
+/* Used by RM_CAM_FDIF_CONTEXT, RM_CAM_ISS_CONTEXT */
+#define OMAP54XX_LOSTMEM_CAM_MEM_SHIFT                                         8
+#define OMAP54XX_LOSTMEM_CAM_MEM_WIDTH                                         0x1
+#define OMAP54XX_LOSTMEM_CAM_MEM_MASK                                          (1 << 8)
+
+/* Used by RM_EMIF_DMM_CONTEXT */
+#define OMAP54XX_LOSTMEM_CORE_NRET_BANK_SHIFT                                  9
+#define OMAP54XX_LOSTMEM_CORE_NRET_BANK_WIDTH                                  0x1
+#define OMAP54XX_LOSTMEM_CORE_NRET_BANK_MASK                                   (1 << 9)
+
+/* Renamed from LOSTMEM_CORE_NRET_BANK Used by RM_L3INSTR_OCP_WP_NOC_CONTEXT */
+#define OMAP54XX_LOSTMEM_CORE_NRET_BANK_8_8_SHIFT                              8
+#define OMAP54XX_LOSTMEM_CORE_NRET_BANK_8_8_WIDTH                              0x1
+#define OMAP54XX_LOSTMEM_CORE_NRET_BANK_8_8_MASK                               (1 << 8)
+
+/* Used by RM_L3MAIN2_OCMC_RAM_CONTEXT */
+#define OMAP54XX_LOSTMEM_CORE_OCMRAM_SHIFT                                     8
+#define OMAP54XX_LOSTMEM_CORE_OCMRAM_WIDTH                                     0x1
+#define OMAP54XX_LOSTMEM_CORE_OCMRAM_MASK                                      (1 << 8)
+
+/* Used by RM_DMA_DMA_SYSTEM_CONTEXT, RM_EMIF_DMM_CONTEXT */
+#define OMAP54XX_LOSTMEM_CORE_OTHER_BANK_SHIFT                                 8
+#define OMAP54XX_LOSTMEM_CORE_OTHER_BANK_WIDTH                                 0x1
+#define OMAP54XX_LOSTMEM_CORE_OTHER_BANK_MASK                                  (1 << 8)
+
+/* Used by RM_DSP_DSP_CONTEXT */
+#define OMAP54XX_LOSTMEM_DSP_EDMA_SHIFT                                                10
+#define OMAP54XX_LOSTMEM_DSP_EDMA_WIDTH                                                0x1
+#define OMAP54XX_LOSTMEM_DSP_EDMA_MASK                                         (1 << 10)
+
+/* Used by RM_DSP_DSP_CONTEXT */
+#define OMAP54XX_LOSTMEM_DSP_L1_SHIFT                                          8
+#define OMAP54XX_LOSTMEM_DSP_L1_WIDTH                                          0x1
+#define OMAP54XX_LOSTMEM_DSP_L1_MASK                                           (1 << 8)
+
+/* Used by RM_DSP_DSP_CONTEXT */
+#define OMAP54XX_LOSTMEM_DSP_L2_SHIFT                                          9
+#define OMAP54XX_LOSTMEM_DSP_L2_WIDTH                                          0x1
+#define OMAP54XX_LOSTMEM_DSP_L2_MASK                                           (1 << 9)
+
+/* Used by RM_DSS_BB2D_CONTEXT, RM_DSS_DSS_CONTEXT */
+#define OMAP54XX_LOSTMEM_DSS_MEM_SHIFT                                         8
+#define OMAP54XX_LOSTMEM_DSS_MEM_WIDTH                                         0x1
+#define OMAP54XX_LOSTMEM_DSS_MEM_MASK                                          (1 << 8)
+
+/* Used by RM_EMU_DEBUGSS_CONTEXT */
+#define OMAP54XX_LOSTMEM_EMU_BANK_SHIFT                                                8
+#define OMAP54XX_LOSTMEM_EMU_BANK_WIDTH                                                0x1
+#define OMAP54XX_LOSTMEM_EMU_BANK_MASK                                         (1 << 8)
+
+/* Used by RM_GPU_GPU_CONTEXT */
+#define OMAP54XX_LOSTMEM_GPU_MEM_SHIFT                                         8
+#define OMAP54XX_LOSTMEM_GPU_MEM_WIDTH                                         0x1
+#define OMAP54XX_LOSTMEM_GPU_MEM_MASK                                          (1 << 8)
+
+/* Used by RM_IVA_IVA_CONTEXT */
+#define OMAP54XX_LOSTMEM_HWA_MEM_SHIFT                                         10
+#define OMAP54XX_LOSTMEM_HWA_MEM_WIDTH                                         0x1
+#define OMAP54XX_LOSTMEM_HWA_MEM_MASK                                          (1 << 10)
+
+/* Used by RM_IPU_IPU_CONTEXT */
+#define OMAP54XX_LOSTMEM_IPU_L2RAM_SHIFT                                       9
+#define OMAP54XX_LOSTMEM_IPU_L2RAM_WIDTH                                       0x1
+#define OMAP54XX_LOSTMEM_IPU_L2RAM_MASK                                                (1 << 9)
+
+/* Used by RM_IPU_IPU_CONTEXT */
+#define OMAP54XX_LOSTMEM_IPU_UNICACHE_SHIFT                                    8
+#define OMAP54XX_LOSTMEM_IPU_UNICACHE_WIDTH                                    0x1
+#define OMAP54XX_LOSTMEM_IPU_UNICACHE_MASK                                     (1 << 8)
+
+/*
+ * Used by RM_L3INIT_HSI_CONTEXT, RM_L3INIT_MMC1_CONTEXT,
+ * RM_L3INIT_MMC2_CONTEXT, RM_L3INIT_SATA_CONTEXT, RM_L3INIT_UNIPRO2_CONTEXT,
+ * RM_L3INIT_USB_OTG_SS_CONTEXT
+ */
+#define OMAP54XX_LOSTMEM_L3INIT_BANK1_SHIFT                                    8
+#define OMAP54XX_LOSTMEM_L3INIT_BANK1_WIDTH                                    0x1
+#define OMAP54XX_LOSTMEM_L3INIT_BANK1_MASK                                     (1 << 8)
+
+/* Used by RM_MPU_MPU_CONTEXT */
+#define OMAP54XX_LOSTMEM_MPU_L2_SHIFT                                          9
+#define OMAP54XX_LOSTMEM_MPU_L2_WIDTH                                          0x1
+#define OMAP54XX_LOSTMEM_MPU_L2_MASK                                           (1 << 9)
+
+/* Used by RM_MPU_MPU_CONTEXT */
+#define OMAP54XX_LOSTMEM_MPU_RAM_SHIFT                                         10
+#define OMAP54XX_LOSTMEM_MPU_RAM_WIDTH                                         0x1
+#define OMAP54XX_LOSTMEM_MPU_RAM_MASK                                          (1 << 10)
+
+/*
+ * Used by RM_L4PER_MMC3_CONTEXT, RM_L4PER_MMC4_CONTEXT, RM_L4PER_MMC5_CONTEXT,
+ * RM_L4SEC_FPKA_CONTEXT
+ */
+#define OMAP54XX_LOSTMEM_NONRETAINED_BANK_SHIFT                                        8
+#define OMAP54XX_LOSTMEM_NONRETAINED_BANK_WIDTH                                        0x1
+#define OMAP54XX_LOSTMEM_NONRETAINED_BANK_MASK                                 (1 << 8)
+
+/*
+ * Used by RM_ABE_DMIC_CONTEXT, RM_ABE_MCBSP1_CONTEXT, RM_ABE_MCBSP2_CONTEXT,
+ * RM_ABE_MCBSP3_CONTEXT, RM_ABE_MCPDM_CONTEXT, RM_ABE_SLIMBUS1_CONTEXT
+ */
+#define OMAP54XX_LOSTMEM_PERIHPMEM_SHIFT                                       8
+#define OMAP54XX_LOSTMEM_PERIHPMEM_WIDTH                                       0x1
+#define OMAP54XX_LOSTMEM_PERIHPMEM_MASK                                                (1 << 8)
+
+/*
+ * Used by RM_L4PER_UART1_CONTEXT, RM_L4PER_UART2_CONTEXT,
+ * RM_L4PER_UART3_CONTEXT, RM_L4PER_UART4_CONTEXT, RM_L4PER_UART5_CONTEXT,
+ * RM_L4PER_UART6_CONTEXT, RM_L4SEC_DMA_CRYPTO_CONTEXT
+ */
+#define OMAP54XX_LOSTMEM_RETAINED_BANK_SHIFT                                   8
+#define OMAP54XX_LOSTMEM_RETAINED_BANK_WIDTH                                   0x1
+#define OMAP54XX_LOSTMEM_RETAINED_BANK_MASK                                    (1 << 8)
+
+/* Used by RM_IVA_SL2_CONTEXT */
+#define OMAP54XX_LOSTMEM_SL2_MEM_SHIFT                                         8
+#define OMAP54XX_LOSTMEM_SL2_MEM_WIDTH                                         0x1
+#define OMAP54XX_LOSTMEM_SL2_MEM_MASK                                          (1 << 8)
+
+/* Used by RM_IVA_IVA_CONTEXT */
+#define OMAP54XX_LOSTMEM_TCM1_MEM_SHIFT                                                8
+#define OMAP54XX_LOSTMEM_TCM1_MEM_WIDTH                                                0x1
+#define OMAP54XX_LOSTMEM_TCM1_MEM_MASK                                         (1 << 8)
+
+/* Used by RM_IVA_IVA_CONTEXT */
+#define OMAP54XX_LOSTMEM_TCM2_MEM_SHIFT                                                9
+#define OMAP54XX_LOSTMEM_TCM2_MEM_WIDTH                                                0x1
+#define OMAP54XX_LOSTMEM_TCM2_MEM_MASK                                         (1 << 9)
+
+/* Used by RM_WKUPAON_SAR_RAM_CONTEXT */
+#define OMAP54XX_LOSTMEM_WKUP_BANK_SHIFT                                       8
+#define OMAP54XX_LOSTMEM_WKUP_BANK_WIDTH                                       0x1
+#define OMAP54XX_LOSTMEM_WKUP_BANK_MASK                                                (1 << 8)
+
+/*
+ * Used by PM_ABE_PWRSTCTRL, PM_CAM_PWRSTCTRL, PM_CORE_PWRSTCTRL,
+ * PM_CUSTEFUSE_PWRSTCTRL, PM_DSP_PWRSTCTRL, PM_DSS_PWRSTCTRL,
+ * PM_GPU_PWRSTCTRL, PM_IVA_PWRSTCTRL, PM_L3INIT_PWRSTCTRL, PM_MPU_PWRSTCTRL
+ */
+#define OMAP54XX_LOWPOWERSTATECHANGE_SHIFT                                     4
+#define OMAP54XX_LOWPOWERSTATECHANGE_WIDTH                                     0x1
+#define OMAP54XX_LOWPOWERSTATECHANGE_MASK                                      (1 << 4)
+
+/* Used by PRM_DEBUG_TRANS_CFG */
+#define OMAP54XX_MODE_SHIFT                                                    0
+#define OMAP54XX_MODE_WIDTH                                                    0x2
+#define OMAP54XX_MODE_MASK                                                     (0x3 << 0)
+
+/* Used by PRM_MODEM_IF_CTRL */
+#define OMAP54XX_MODEM_SHUTDOWN_IRQ_SHIFT                                      9
+#define OMAP54XX_MODEM_SHUTDOWN_IRQ_WIDTH                                      0x1
+#define OMAP54XX_MODEM_SHUTDOWN_IRQ_MASK                                       (1 << 9)
+
+/* Used by PRM_MODEM_IF_CTRL */
+#define OMAP54XX_MODEM_WAKE_IRQ_SHIFT                                          8
+#define OMAP54XX_MODEM_WAKE_IRQ_WIDTH                                          0x1
+#define OMAP54XX_MODEM_WAKE_IRQ_MASK                                           (1 << 8)
+
+/* Used by PM_MPU_PWRSTCTRL */
+#define OMAP54XX_MPU_L2_ONSTATE_SHIFT                                          18
+#define OMAP54XX_MPU_L2_ONSTATE_WIDTH                                          0x2
+#define OMAP54XX_MPU_L2_ONSTATE_MASK                                           (0x3 << 18)
+
+/* Used by PM_MPU_PWRSTCTRL */
+#define OMAP54XX_MPU_L2_RETSTATE_SHIFT                                         9
+#define OMAP54XX_MPU_L2_RETSTATE_WIDTH                                         0x1
+#define OMAP54XX_MPU_L2_RETSTATE_MASK                                          (1 << 9)
+
+/* Used by PM_MPU_PWRSTST */
+#define OMAP54XX_MPU_L2_STATEST_SHIFT                                          6
+#define OMAP54XX_MPU_L2_STATEST_WIDTH                                          0x2
+#define OMAP54XX_MPU_L2_STATEST_MASK                                           (0x3 << 6)
+
+/* Used by PM_MPU_PWRSTCTRL */
+#define OMAP54XX_MPU_RAM_ONSTATE_SHIFT                                         20
+#define OMAP54XX_MPU_RAM_ONSTATE_WIDTH                                         0x2
+#define OMAP54XX_MPU_RAM_ONSTATE_MASK                                          (0x3 << 20)
+
+/* Used by PM_MPU_PWRSTCTRL */
+#define OMAP54XX_MPU_RAM_RETSTATE_SHIFT                                                10
+#define OMAP54XX_MPU_RAM_RETSTATE_WIDTH                                                0x1
+#define OMAP54XX_MPU_RAM_RETSTATE_MASK                                         (1 << 10)
+
+/* Used by PM_MPU_PWRSTST */
+#define OMAP54XX_MPU_RAM_STATEST_SHIFT                                         8
+#define OMAP54XX_MPU_RAM_STATEST_WIDTH                                         0x2
+#define OMAP54XX_MPU_RAM_STATEST_MASK                                          (0x3 << 8)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_MPU_SECURITY_VIOL_RST_SHIFT                                   2
+#define OMAP54XX_MPU_SECURITY_VIOL_RST_WIDTH                                   0x1
+#define OMAP54XX_MPU_SECURITY_VIOL_RST_MASK                                    (1 << 2)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_MPU_WDT_RST_SHIFT                                             3
+#define OMAP54XX_MPU_WDT_RST_WIDTH                                             0x1
+#define OMAP54XX_MPU_WDT_RST_MASK                                              (1 << 3)
+
+/* Used by PRM_ABBLDO_MM_SETUP, PRM_ABBLDO_MPU_SETUP */
+#define OMAP54XX_NOCAP_SHIFT                                                   4
+#define OMAP54XX_NOCAP_WIDTH                                                   0x1
+#define OMAP54XX_NOCAP_MASK                                                    (1 << 4)
+
+/* Used by PM_CORE_PWRSTCTRL */
+#define OMAP54XX_OCP_NRET_BANK_ONSTATE_SHIFT                                   24
+#define OMAP54XX_OCP_NRET_BANK_ONSTATE_WIDTH                                   0x2
+#define OMAP54XX_OCP_NRET_BANK_ONSTATE_MASK                                    (0x3 << 24)
+
+/* Used by PM_CORE_PWRSTCTRL */
+#define OMAP54XX_OCP_NRET_BANK_RETSTATE_SHIFT                                  12
+#define OMAP54XX_OCP_NRET_BANK_RETSTATE_WIDTH                                  0x1
+#define OMAP54XX_OCP_NRET_BANK_RETSTATE_MASK                                   (1 << 12)
+
+/* Used by PM_CORE_PWRSTST */
+#define OMAP54XX_OCP_NRET_BANK_STATEST_SHIFT                                   12
+#define OMAP54XX_OCP_NRET_BANK_STATEST_WIDTH                                   0x2
+#define OMAP54XX_OCP_NRET_BANK_STATEST_MASK                                    (0x3 << 12)
+
+/*
+ * Used by PRM_VC_VAL_CMD_VDD_CORE_L, PRM_VC_VAL_CMD_VDD_MM_L,
+ * PRM_VC_VAL_CMD_VDD_MPU_L
+ */
+#define OMAP54XX_OFF_SHIFT                                                     0
+#define OMAP54XX_OFF_WIDTH                                                     0x8
+#define OMAP54XX_OFF_MASK                                                      (0xff << 0)
+
+/*
+ * Used by PRM_VC_VAL_CMD_VDD_CORE_L, PRM_VC_VAL_CMD_VDD_MM_L,
+ * PRM_VC_VAL_CMD_VDD_MPU_L
+ */
+#define OMAP54XX_ON_SHIFT                                                      24
+#define OMAP54XX_ON_WIDTH                                                      0x8
+#define OMAP54XX_ON_MASK                                                       (0xff << 24)
+
+/*
+ * Used by PRM_VC_VAL_CMD_VDD_CORE_L, PRM_VC_VAL_CMD_VDD_MM_L,
+ * PRM_VC_VAL_CMD_VDD_MPU_L
+ */
+#define OMAP54XX_ONLP_SHIFT                                                    16
+#define OMAP54XX_ONLP_WIDTH                                                    0x8
+#define OMAP54XX_ONLP_MASK                                                     (0xff << 16)
+
+/* Used by PRM_ABBLDO_MM_CTRL, PRM_ABBLDO_MPU_CTRL */
+#define OMAP54XX_OPP_CHANGE_SHIFT                                              2
+#define OMAP54XX_OPP_CHANGE_WIDTH                                              0x1
+#define OMAP54XX_OPP_CHANGE_MASK                                               (1 << 2)
+
+/* Used by PRM_VC_VAL_BYPASS */
+#define OMAP54XX_OPP_CHANGE_EMIF_LVL_SHIFT                                     25
+#define OMAP54XX_OPP_CHANGE_EMIF_LVL_WIDTH                                     0x1
+#define OMAP54XX_OPP_CHANGE_EMIF_LVL_MASK                                      (1 << 25)
+
+/* Used by PRM_ABBLDO_MM_CTRL, PRM_ABBLDO_MPU_CTRL */
+#define OMAP54XX_OPP_SEL_SHIFT                                                 0
+#define OMAP54XX_OPP_SEL_WIDTH                                                 0x2
+#define OMAP54XX_OPP_SEL_MASK                                                  (0x3 << 0)
+
+/* Used by PRM_DEBUG_OUT */
+#define OMAP54XX_OUTPUT_SHIFT                                                  0
+#define OMAP54XX_OUTPUT_WIDTH                                                  0x20
+#define OMAP54XX_OUTPUT_MASK                                                   (0xffffffff << 0)
+
+/* Used by PRM_SRAM_COUNT */
+#define OMAP54XX_PCHARGECNT_VALUE_SHIFT                                                0
+#define OMAP54XX_PCHARGECNT_VALUE_WIDTH                                                0x6
+#define OMAP54XX_PCHARGECNT_VALUE_MASK                                         (0x3f << 0)
+
+/* Used by PRM_PSCON_COUNT */
+#define OMAP54XX_PCHARGE_TIME_SHIFT                                            0
+#define OMAP54XX_PCHARGE_TIME_WIDTH                                            0x8
+#define OMAP54XX_PCHARGE_TIME_MASK                                             (0xff << 0)
+
+/* Used by PM_ABE_PWRSTCTRL */
+#define OMAP54XX_PERIPHMEM_ONSTATE_SHIFT                                       20
+#define OMAP54XX_PERIPHMEM_ONSTATE_WIDTH                                       0x2
+#define OMAP54XX_PERIPHMEM_ONSTATE_MASK                                                (0x3 << 20)
+
+/* Used by PM_ABE_PWRSTCTRL */
+#define OMAP54XX_PERIPHMEM_RETSTATE_SHIFT                                      10
+#define OMAP54XX_PERIPHMEM_RETSTATE_WIDTH                                      0x1
+#define OMAP54XX_PERIPHMEM_RETSTATE_MASK                                       (1 << 10)
+
+/* Used by PM_ABE_PWRSTST */
+#define OMAP54XX_PERIPHMEM_STATEST_SHIFT                                       8
+#define OMAP54XX_PERIPHMEM_STATEST_WIDTH                                       0x2
+#define OMAP54XX_PERIPHMEM_STATEST_MASK                                                (0x3 << 8)
+
+/* Used by PRM_PHASE1_CNDP */
+#define OMAP54XX_PHASE1_CNDP_SHIFT                                             0
+#define OMAP54XX_PHASE1_CNDP_WIDTH                                             0x20
+#define OMAP54XX_PHASE1_CNDP_MASK                                              (0xffffffff << 0)
+
+/* Used by PRM_PHASE2A_CNDP */
+#define OMAP54XX_PHASE2A_CNDP_SHIFT                                            0
+#define OMAP54XX_PHASE2A_CNDP_WIDTH                                            0x20
+#define OMAP54XX_PHASE2A_CNDP_MASK                                             (0xffffffff << 0)
+
+/* Used by PRM_PHASE2B_CNDP */
+#define OMAP54XX_PHASE2B_CNDP_SHIFT                                            0
+#define OMAP54XX_PHASE2B_CNDP_WIDTH                                            0x20
+#define OMAP54XX_PHASE2B_CNDP_MASK                                             (0xffffffff << 0)
+
+/* Used by PRM_PSCON_COUNT */
+#define OMAP54XX_PONOUT_2_PGOODIN_TIME_SHIFT                                   8
+#define OMAP54XX_PONOUT_2_PGOODIN_TIME_WIDTH                                   0x8
+#define OMAP54XX_PONOUT_2_PGOODIN_TIME_MASK                                    (0xff << 8)
+
+/*
+ * Used by PM_ABE_PWRSTCTRL, PM_CAM_PWRSTCTRL, PM_CORE_PWRSTCTRL,
+ * PM_CUSTEFUSE_PWRSTCTRL, PM_DSP_PWRSTCTRL, PM_DSS_PWRSTCTRL,
+ * PM_EMU_PWRSTCTRL, PM_GPU_PWRSTCTRL, PM_IVA_PWRSTCTRL, PM_L3INIT_PWRSTCTRL,
+ * PM_MPU_PWRSTCTRL
+ */
+#define OMAP54XX_POWERSTATE_SHIFT                                              0
+#define OMAP54XX_POWERSTATE_WIDTH                                              0x2
+#define OMAP54XX_POWERSTATE_MASK                                               (0x3 << 0)
+
+/*
+ * Used by PM_ABE_PWRSTST, PM_CAM_PWRSTST, PM_CORE_PWRSTST,
+ * PM_CUSTEFUSE_PWRSTST, PM_DSP_PWRSTST, PM_DSS_PWRSTST, PM_EMU_PWRSTST,
+ * PM_GPU_PWRSTST, PM_IVA_PWRSTST, PM_L3INIT_PWRSTST, PM_MPU_PWRSTST
+ */
+#define OMAP54XX_POWERSTATEST_SHIFT                                            0
+#define OMAP54XX_POWERSTATEST_WIDTH                                            0x2
+#define OMAP54XX_POWERSTATEST_MASK                                             (0x3 << 0)
+
+/* Used by PRM_PWRREQCTRL */
+#define OMAP54XX_PWRREQ_COND_SHIFT                                             0
+#define OMAP54XX_PWRREQ_COND_WIDTH                                             0x2
+#define OMAP54XX_PWRREQ_COND_MASK                                              (0x3 << 0)
+
+/* Used by PRM_VC_SMPS_CORE_CONFIG */
+#define OMAP54XX_RACEN_VDD_CORE_L_SHIFT                                                27
+#define OMAP54XX_RACEN_VDD_CORE_L_WIDTH                                                0x1
+#define OMAP54XX_RACEN_VDD_CORE_L_MASK                                         (1 << 27)
+
+/* Used by PRM_VC_SMPS_MM_CONFIG */
+#define OMAP54XX_RACEN_VDD_MM_L_SHIFT                                          27
+#define OMAP54XX_RACEN_VDD_MM_L_WIDTH                                          0x1
+#define OMAP54XX_RACEN_VDD_MM_L_MASK                                           (1 << 27)
+
+/* Used by PRM_VC_SMPS_MPU_CONFIG */
+#define OMAP54XX_RACEN_VDD_MPU_L_SHIFT                                         27
+#define OMAP54XX_RACEN_VDD_MPU_L_WIDTH                                         0x1
+#define OMAP54XX_RACEN_VDD_MPU_L_MASK                                          (1 << 27)
+
+/* Used by PRM_VC_SMPS_CORE_CONFIG */
+#define OMAP54XX_RAC_VDD_CORE_L_SHIFT                                          26
+#define OMAP54XX_RAC_VDD_CORE_L_WIDTH                                          0x1
+#define OMAP54XX_RAC_VDD_CORE_L_MASK                                           (1 << 26)
+
+/* Used by PRM_VC_SMPS_MM_CONFIG */
+#define OMAP54XX_RAC_VDD_MM_L_SHIFT                                            26
+#define OMAP54XX_RAC_VDD_MM_L_WIDTH                                            0x1
+#define OMAP54XX_RAC_VDD_MM_L_MASK                                             (1 << 26)
+
+/* Used by PRM_VC_SMPS_MPU_CONFIG */
+#define OMAP54XX_RAC_VDD_MPU_L_SHIFT                                           26
+#define OMAP54XX_RAC_VDD_MPU_L_WIDTH                                           0x1
+#define OMAP54XX_RAC_VDD_MPU_L_MASK                                            (1 << 26)
+
+/*
+ * Used by PRM_VOLTSETUP_CORE_OFF, PRM_VOLTSETUP_CORE_RET_SLEEP,
+ * PRM_VOLTSETUP_MM_OFF, PRM_VOLTSETUP_MM_RET_SLEEP, PRM_VOLTSETUP_MPU_OFF,
+ * PRM_VOLTSETUP_MPU_RET_SLEEP
+ */
+#define OMAP54XX_RAMP_DOWN_COUNT_SHIFT                                         16
+#define OMAP54XX_RAMP_DOWN_COUNT_WIDTH                                         0x6
+#define OMAP54XX_RAMP_DOWN_COUNT_MASK                                          (0x3f << 16)
+
+/*
+ * Used by PRM_VOLTSETUP_CORE_OFF, PRM_VOLTSETUP_CORE_RET_SLEEP,
+ * PRM_VOLTSETUP_MM_OFF, PRM_VOLTSETUP_MM_RET_SLEEP, PRM_VOLTSETUP_MPU_OFF,
+ * PRM_VOLTSETUP_MPU_RET_SLEEP
+ */
+#define OMAP54XX_RAMP_DOWN_PRESCAL_SHIFT                                       24
+#define OMAP54XX_RAMP_DOWN_PRESCAL_WIDTH                                       0x2
+#define OMAP54XX_RAMP_DOWN_PRESCAL_MASK                                                (0x3 << 24)
+
+/*
+ * Used by PRM_VOLTSETUP_CORE_OFF, PRM_VOLTSETUP_CORE_RET_SLEEP,
+ * PRM_VOLTSETUP_MM_OFF, PRM_VOLTSETUP_MM_RET_SLEEP, PRM_VOLTSETUP_MPU_OFF,
+ * PRM_VOLTSETUP_MPU_RET_SLEEP
+ */
+#define OMAP54XX_RAMP_UP_COUNT_SHIFT                                           0
+#define OMAP54XX_RAMP_UP_COUNT_WIDTH                                           0x6
+#define OMAP54XX_RAMP_UP_COUNT_MASK                                            (0x3f << 0)
+
+/*
+ * Used by PRM_VOLTSETUP_CORE_OFF, PRM_VOLTSETUP_CORE_RET_SLEEP,
+ * PRM_VOLTSETUP_MM_OFF, PRM_VOLTSETUP_MM_RET_SLEEP, PRM_VOLTSETUP_MPU_OFF,
+ * PRM_VOLTSETUP_MPU_RET_SLEEP
+ */
+#define OMAP54XX_RAMP_UP_PRESCAL_SHIFT                                         8
+#define OMAP54XX_RAMP_UP_PRESCAL_WIDTH                                         0x2
+#define OMAP54XX_RAMP_UP_PRESCAL_MASK                                          (0x3 << 8)
+
+/* Used by PRM_VC_SMPS_CORE_CONFIG */
+#define OMAP54XX_RAV_VDD_CORE_L_SHIFT                                          25
+#define OMAP54XX_RAV_VDD_CORE_L_WIDTH                                          0x1
+#define OMAP54XX_RAV_VDD_CORE_L_MASK                                           (1 << 25)
+
+/* Used by PRM_VC_SMPS_MM_CONFIG */
+#define OMAP54XX_RAV_VDD_MM_L_SHIFT                                            25
+#define OMAP54XX_RAV_VDD_MM_L_WIDTH                                            0x1
+#define OMAP54XX_RAV_VDD_MM_L_MASK                                             (1 << 25)
+
+/* Used by PRM_VC_SMPS_MPU_CONFIG */
+#define OMAP54XX_RAV_VDD_MPU_L_SHIFT                                           25
+#define OMAP54XX_RAV_VDD_MPU_L_WIDTH                                           0x1
+#define OMAP54XX_RAV_VDD_MPU_L_MASK                                            (1 << 25)
+
+/* Used by PRM_VC_VAL_BYPASS */
+#define OMAP54XX_REGADDR_SHIFT                                                 8
+#define OMAP54XX_REGADDR_WIDTH                                                 0x8
+#define OMAP54XX_REGADDR_MASK                                                  (0xff << 8)
+
+/*
+ * Used by PRM_VC_VAL_CMD_VDD_CORE_L, PRM_VC_VAL_CMD_VDD_MM_L,
+ * PRM_VC_VAL_CMD_VDD_MPU_L
+ */
+#define OMAP54XX_RET_SHIFT                                                     8
+#define OMAP54XX_RET_WIDTH                                                     0x8
+#define OMAP54XX_RET_MASK                                                      (0xff << 8)
+
+/* Used by PRM_SLDO_CORE_CTRL, PRM_SLDO_MM_CTRL, PRM_SLDO_MPU_CTRL */
+#define OMAP54XX_RETMODE_ENABLE_SHIFT                                          0
+#define OMAP54XX_RETMODE_ENABLE_WIDTH                                          0x1
+#define OMAP54XX_RETMODE_ENABLE_MASK                                           (1 << 0)
+
+/* Used by PRM_RSTTIME */
+#define OMAP54XX_RSTTIME1_SHIFT                                                        0
+#define OMAP54XX_RSTTIME1_WIDTH                                                        0xa
+#define OMAP54XX_RSTTIME1_MASK                                                 (0x3ff << 0)
+
+/* Used by PRM_RSTTIME */
+#define OMAP54XX_RSTTIME2_SHIFT                                                        10
+#define OMAP54XX_RSTTIME2_WIDTH                                                        0x5
+#define OMAP54XX_RSTTIME2_MASK                                                 (0x1f << 10)
+
+/* Used by RM_IPU_RSTCTRL, RM_IPU_RSTST */
+#define OMAP54XX_RST_CPU0_SHIFT                                                        0
+#define OMAP54XX_RST_CPU0_WIDTH                                                        0x1
+#define OMAP54XX_RST_CPU0_MASK                                                 (1 << 0)
+
+/* Used by RM_IPU_RSTCTRL, RM_IPU_RSTST */
+#define OMAP54XX_RST_CPU1_SHIFT                                                        1
+#define OMAP54XX_RST_CPU1_WIDTH                                                        0x1
+#define OMAP54XX_RST_CPU1_MASK                                                 (1 << 1)
+
+/* Used by RM_DSP_RSTCTRL, RM_DSP_RSTST */
+#define OMAP54XX_RST_DSP_SHIFT                                                 0
+#define OMAP54XX_RST_DSP_WIDTH                                                 0x1
+#define OMAP54XX_RST_DSP_MASK                                                  (1 << 0)
+
+/* Used by RM_DSP_RSTST */
+#define OMAP54XX_RST_DSP_EMU_SHIFT                                             2
+#define OMAP54XX_RST_DSP_EMU_WIDTH                                             0x1
+#define OMAP54XX_RST_DSP_EMU_MASK                                              (1 << 2)
+
+/* Used by RM_DSP_RSTST */
+#define OMAP54XX_RST_DSP_EMU_REQ_SHIFT                                         3
+#define OMAP54XX_RST_DSP_EMU_REQ_WIDTH                                         0x1
+#define OMAP54XX_RST_DSP_EMU_REQ_MASK                                          (1 << 3)
+
+/* Used by RM_DSP_RSTCTRL, RM_DSP_RSTST */
+#define OMAP54XX_RST_DSP_MMU_CACHE_SHIFT                                       1
+#define OMAP54XX_RST_DSP_MMU_CACHE_WIDTH                                       0x1
+#define OMAP54XX_RST_DSP_MMU_CACHE_MASK                                                (1 << 1)
+
+/* Used by RM_IPU_RSTST */
+#define OMAP54XX_RST_EMULATION_CPU0_SHIFT                                      3
+#define OMAP54XX_RST_EMULATION_CPU0_WIDTH                                      0x1
+#define OMAP54XX_RST_EMULATION_CPU0_MASK                                       (1 << 3)
+
+/* Used by RM_IPU_RSTST */
+#define OMAP54XX_RST_EMULATION_CPU1_SHIFT                                      4
+#define OMAP54XX_RST_EMULATION_CPU1_WIDTH                                      0x1
+#define OMAP54XX_RST_EMULATION_CPU1_MASK                                       (1 << 4)
+
+/* Used by RM_IVA_RSTST */
+#define OMAP54XX_RST_EMULATION_SEQ1_SHIFT                                      3
+#define OMAP54XX_RST_EMULATION_SEQ1_WIDTH                                      0x1
+#define OMAP54XX_RST_EMULATION_SEQ1_MASK                                       (1 << 3)
+
+/* Used by RM_IVA_RSTST */
+#define OMAP54XX_RST_EMULATION_SEQ2_SHIFT                                      4
+#define OMAP54XX_RST_EMULATION_SEQ2_WIDTH                                      0x1
+#define OMAP54XX_RST_EMULATION_SEQ2_MASK                                       (1 << 4)
+
+/* Used by PRM_RSTCTRL */
+#define OMAP54XX_RST_GLOBAL_COLD_SW_SHIFT                                      1
+#define OMAP54XX_RST_GLOBAL_COLD_SW_WIDTH                                      0x1
+#define OMAP54XX_RST_GLOBAL_COLD_SW_MASK                                       (1 << 1)
+
+/* Used by PRM_RSTCTRL */
+#define OMAP54XX_RST_GLOBAL_WARM_SW_SHIFT                                      0
+#define OMAP54XX_RST_GLOBAL_WARM_SW_WIDTH                                      0x1
+#define OMAP54XX_RST_GLOBAL_WARM_SW_MASK                                       (1 << 0)
+
+/* Used by RM_IPU_RSTST */
+#define OMAP54XX_RST_ICECRUSHER_CPU0_SHIFT                                     5
+#define OMAP54XX_RST_ICECRUSHER_CPU0_WIDTH                                     0x1
+#define OMAP54XX_RST_ICECRUSHER_CPU0_MASK                                      (1 << 5)
+
+/* Used by RM_IPU_RSTST */
+#define OMAP54XX_RST_ICECRUSHER_CPU1_SHIFT                                     6
+#define OMAP54XX_RST_ICECRUSHER_CPU1_WIDTH                                     0x1
+#define OMAP54XX_RST_ICECRUSHER_CPU1_MASK                                      (1 << 6)
+
+/* Used by RM_IVA_RSTST */
+#define OMAP54XX_RST_ICECRUSHER_SEQ1_SHIFT                                     5
+#define OMAP54XX_RST_ICECRUSHER_SEQ1_WIDTH                                     0x1
+#define OMAP54XX_RST_ICECRUSHER_SEQ1_MASK                                      (1 << 5)
+
+/* Used by RM_IVA_RSTST */
+#define OMAP54XX_RST_ICECRUSHER_SEQ2_SHIFT                                     6
+#define OMAP54XX_RST_ICECRUSHER_SEQ2_WIDTH                                     0x1
+#define OMAP54XX_RST_ICECRUSHER_SEQ2_MASK                                      (1 << 6)
+
+/* Used by RM_IPU_RSTCTRL, RM_IPU_RSTST */
+#define OMAP54XX_RST_IPU_MMU_CACHE_SHIFT                                       2
+#define OMAP54XX_RST_IPU_MMU_CACHE_WIDTH                                       0x1
+#define OMAP54XX_RST_IPU_MMU_CACHE_MASK                                                (1 << 2)
+
+/* Used by RM_IVA_RSTCTRL, RM_IVA_RSTST */
+#define OMAP54XX_RST_LOGIC_SHIFT                                               2
+#define OMAP54XX_RST_LOGIC_WIDTH                                               0x1
+#define OMAP54XX_RST_LOGIC_MASK                                                        (1 << 2)
+
+/* Used by RM_IVA_RSTCTRL, RM_IVA_RSTST */
+#define OMAP54XX_RST_SEQ1_SHIFT                                                        0
+#define OMAP54XX_RST_SEQ1_WIDTH                                                        0x1
+#define OMAP54XX_RST_SEQ1_MASK                                                 (1 << 0)
+
+/* Used by RM_IVA_RSTCTRL, RM_IVA_RSTST */
+#define OMAP54XX_RST_SEQ2_SHIFT                                                        1
+#define OMAP54XX_RST_SEQ2_WIDTH                                                        0x1
+#define OMAP54XX_RST_SEQ2_MASK                                                 (1 << 1)
+
+/* Used by REVISION_PRM */
+#define OMAP54XX_R_RTL_SHIFT                                                   11
+#define OMAP54XX_R_RTL_WIDTH                                                   0x5
+#define OMAP54XX_R_RTL_MASK                                                    (0x1f << 11)
+
+/* Used by PRM_VC_SMPS_CORE_CONFIG */
+#define OMAP54XX_SA_VDD_CORE_L_SHIFT                                           0
+#define OMAP54XX_SA_VDD_CORE_L_WIDTH                                           0x7
+#define OMAP54XX_SA_VDD_CORE_L_MASK                                            (0x7f << 0)
+
+/* Used by PRM_VC_SMPS_MM_CONFIG */
+#define OMAP54XX_SA_VDD_MM_L_SHIFT                                             0
+#define OMAP54XX_SA_VDD_MM_L_WIDTH                                             0x7
+#define OMAP54XX_SA_VDD_MM_L_MASK                                              (0x7f << 0)
+
+/* Used by PRM_VC_SMPS_MPU_CONFIG */
+#define OMAP54XX_SA_VDD_MPU_L_SHIFT                                            0
+#define OMAP54XX_SA_VDD_MPU_L_WIDTH                                            0x7
+#define OMAP54XX_SA_VDD_MPU_L_MASK                                             (0x7f << 0)
+
+/* Used by REVISION_PRM */
+#define OMAP54XX_SCHEME_SHIFT                                                  30
+#define OMAP54XX_SCHEME_WIDTH                                                  0x2
+#define OMAP54XX_SCHEME_MASK                                                   (0x3 << 30)
+
+/* Used by PRM_VC_CFG_I2C_CLK */
+#define OMAP54XX_SCLH_SHIFT                                                    0
+#define OMAP54XX_SCLH_WIDTH                                                    0x8
+#define OMAP54XX_SCLH_MASK                                                     (0xff << 0)
+
+/* Used by PRM_VC_CFG_I2C_CLK */
+#define OMAP54XX_SCLL_SHIFT                                                    8
+#define OMAP54XX_SCLL_WIDTH                                                    0x8
+#define OMAP54XX_SCLL_MASK                                                     (0xff << 8)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_SECURE_WDT_RST_SHIFT                                          4
+#define OMAP54XX_SECURE_WDT_RST_WIDTH                                          0x1
+#define OMAP54XX_SECURE_WDT_RST_MASK                                           (1 << 4)
+
+/* Used by PRM_VC_SMPS_CORE_CONFIG */
+#define OMAP54XX_SEL_SA_VDD_CORE_L_SHIFT                                       24
+#define OMAP54XX_SEL_SA_VDD_CORE_L_WIDTH                                       0x1
+#define OMAP54XX_SEL_SA_VDD_CORE_L_MASK                                                (1 << 24)
+
+/* Used by PRM_VC_SMPS_MM_CONFIG */
+#define OMAP54XX_SEL_SA_VDD_MM_L_SHIFT                                         24
+#define OMAP54XX_SEL_SA_VDD_MM_L_WIDTH                                         0x1
+#define OMAP54XX_SEL_SA_VDD_MM_L_MASK                                          (1 << 24)
+
+/* Used by PRM_VC_SMPS_MPU_CONFIG */
+#define OMAP54XX_SEL_SA_VDD_MPU_L_SHIFT                                                24
+#define OMAP54XX_SEL_SA_VDD_MPU_L_WIDTH                                                0x1
+#define OMAP54XX_SEL_SA_VDD_MPU_L_MASK                                         (1 << 24)
+
+/* Used by PM_IVA_PWRSTCTRL */
+#define OMAP54XX_SL2_MEM_ONSTATE_SHIFT                                         18
+#define OMAP54XX_SL2_MEM_ONSTATE_WIDTH                                         0x2
+#define OMAP54XX_SL2_MEM_ONSTATE_MASK                                          (0x3 << 18)
+
+/* Used by PM_IVA_PWRSTCTRL */
+#define OMAP54XX_SL2_MEM_RETSTATE_SHIFT                                                9
+#define OMAP54XX_SL2_MEM_RETSTATE_WIDTH                                                0x1
+#define OMAP54XX_SL2_MEM_RETSTATE_MASK                                         (1 << 9)
+
+/* Used by PM_IVA_PWRSTST */
+#define OMAP54XX_SL2_MEM_STATEST_SHIFT                                         6
+#define OMAP54XX_SL2_MEM_STATEST_WIDTH                                         0x2
+#define OMAP54XX_SL2_MEM_STATEST_MASK                                          (0x3 << 6)
+
+/* Used by PRM_VC_VAL_BYPASS */
+#define OMAP54XX_SLAVEADDR_SHIFT                                               0
+#define OMAP54XX_SLAVEADDR_WIDTH                                               0x7
+#define OMAP54XX_SLAVEADDR_MASK                                                        (0x7f << 0)
+
+/* Used by PRM_SRAM_COUNT */
+#define OMAP54XX_SLPCNT_VALUE_SHIFT                                            16
+#define OMAP54XX_SLPCNT_VALUE_WIDTH                                            0x8
+#define OMAP54XX_SLPCNT_VALUE_MASK                                             (0xff << 16)
+
+/* Used by PRM_VP_CORE_VSTEPMAX, PRM_VP_MM_VSTEPMAX, PRM_VP_MPU_VSTEPMAX */
+#define OMAP54XX_SMPSWAITTIMEMAX_SHIFT                                         8
+#define OMAP54XX_SMPSWAITTIMEMAX_WIDTH                                         0x10
+#define OMAP54XX_SMPSWAITTIMEMAX_MASK                                          (0xffff << 8)
+
+/* Used by PRM_VP_CORE_VSTEPMIN, PRM_VP_MM_VSTEPMIN, PRM_VP_MPU_VSTEPMIN */
+#define OMAP54XX_SMPSWAITTIMEMIN_SHIFT                                         8
+#define OMAP54XX_SMPSWAITTIMEMIN_WIDTH                                         0x10
+#define OMAP54XX_SMPSWAITTIMEMIN_MASK                                          (0xffff << 8)
+
+/* Used by PRM_VC_CORE_ERRST */
+#define OMAP54XX_SMPS_RA_ERR_CORE_SHIFT                                                1
+#define OMAP54XX_SMPS_RA_ERR_CORE_WIDTH                                                0x1
+#define OMAP54XX_SMPS_RA_ERR_CORE_MASK                                         (1 << 1)
+
+/* Used by PRM_VC_MM_ERRST */
+#define OMAP54XX_SMPS_RA_ERR_MM_SHIFT                                          1
+#define OMAP54XX_SMPS_RA_ERR_MM_WIDTH                                          0x1
+#define OMAP54XX_SMPS_RA_ERR_MM_MASK                                           (1 << 1)
+
+/* Used by PRM_VC_MPU_ERRST */
+#define OMAP54XX_SMPS_RA_ERR_MPU_SHIFT                                         1
+#define OMAP54XX_SMPS_RA_ERR_MPU_WIDTH                                         0x1
+#define OMAP54XX_SMPS_RA_ERR_MPU_MASK                                          (1 << 1)
+
+/* Used by PRM_VC_CORE_ERRST */
+#define OMAP54XX_SMPS_SA_ERR_CORE_SHIFT                                                0
+#define OMAP54XX_SMPS_SA_ERR_CORE_WIDTH                                                0x1
+#define OMAP54XX_SMPS_SA_ERR_CORE_MASK                                         (1 << 0)
+
+/* Used by PRM_VC_MM_ERRST */
+#define OMAP54XX_SMPS_SA_ERR_MM_SHIFT                                          0
+#define OMAP54XX_SMPS_SA_ERR_MM_WIDTH                                          0x1
+#define OMAP54XX_SMPS_SA_ERR_MM_MASK                                           (1 << 0)
+
+/* Used by PRM_VC_MPU_ERRST */
+#define OMAP54XX_SMPS_SA_ERR_MPU_SHIFT                                         0
+#define OMAP54XX_SMPS_SA_ERR_MPU_WIDTH                                         0x1
+#define OMAP54XX_SMPS_SA_ERR_MPU_MASK                                          (1 << 0)
+
+/* Used by PRM_VC_CORE_ERRST */
+#define OMAP54XX_SMPS_TIMEOUT_ERR_CORE_SHIFT                                   2
+#define OMAP54XX_SMPS_TIMEOUT_ERR_CORE_WIDTH                                   0x1
+#define OMAP54XX_SMPS_TIMEOUT_ERR_CORE_MASK                                    (1 << 2)
+
+/* Used by PRM_VC_MM_ERRST */
+#define OMAP54XX_SMPS_TIMEOUT_ERR_MM_SHIFT                                     2
+#define OMAP54XX_SMPS_TIMEOUT_ERR_MM_WIDTH                                     0x1
+#define OMAP54XX_SMPS_TIMEOUT_ERR_MM_MASK                                      (1 << 2)
+
+/* Used by PRM_VC_MPU_ERRST */
+#define OMAP54XX_SMPS_TIMEOUT_ERR_MPU_SHIFT                                    2
+#define OMAP54XX_SMPS_TIMEOUT_ERR_MPU_WIDTH                                    0x1
+#define OMAP54XX_SMPS_TIMEOUT_ERR_MPU_MASK                                     (1 << 2)
+
+/* Used by PRM_ABBLDO_MM_SETUP, PRM_ABBLDO_MPU_SETUP */
+#define OMAP54XX_SR2EN_SHIFT                                                   0
+#define OMAP54XX_SR2EN_WIDTH                                                   0x1
+#define OMAP54XX_SR2EN_MASK                                                    (1 << 0)
+
+/* Used by PRM_ABBLDO_MM_CTRL, PRM_ABBLDO_MPU_CTRL */
+#define OMAP54XX_SR2_IN_TRANSITION_SHIFT                                       6
+#define OMAP54XX_SR2_IN_TRANSITION_WIDTH                                       0x1
+#define OMAP54XX_SR2_IN_TRANSITION_MASK                                                (1 << 6)
+
+/* Used by PRM_ABBLDO_MM_CTRL, PRM_ABBLDO_MPU_CTRL */
+#define OMAP54XX_SR2_STATUS_SHIFT                                              3
+#define OMAP54XX_SR2_STATUS_WIDTH                                              0x2
+#define OMAP54XX_SR2_STATUS_MASK                                               (0x3 << 3)
+
+/* Used by PRM_ABBLDO_MM_SETUP, PRM_ABBLDO_MPU_SETUP */
+#define OMAP54XX_SR2_WTCNT_VALUE_SHIFT                                         8
+#define OMAP54XX_SR2_WTCNT_VALUE_WIDTH                                         0x8
+#define OMAP54XX_SR2_WTCNT_VALUE_MASK                                          (0xff << 8)
+
+/* Used by PRM_SLDO_CORE_CTRL, PRM_SLDO_MM_CTRL, PRM_SLDO_MPU_CTRL */
+#define OMAP54XX_SRAMLDO_STATUS_SHIFT                                          8
+#define OMAP54XX_SRAMLDO_STATUS_WIDTH                                          0x1
+#define OMAP54XX_SRAMLDO_STATUS_MASK                                           (1 << 8)
+
+/* Used by PRM_SLDO_CORE_CTRL, PRM_SLDO_MM_CTRL, PRM_SLDO_MPU_CTRL */
+#define OMAP54XX_SRAM_IN_TRANSITION_SHIFT                                      9
+#define OMAP54XX_SRAM_IN_TRANSITION_WIDTH                                      0x1
+#define OMAP54XX_SRAM_IN_TRANSITION_MASK                                       (1 << 9)
+
+/* Used by PRM_VC_CFG_I2C_MODE */
+#define OMAP54XX_SRMODEEN_SHIFT                                                        4
+#define OMAP54XX_SRMODEEN_WIDTH                                                        0x1
+#define OMAP54XX_SRMODEEN_MASK                                                 (1 << 4)
+
+/* Used by PRM_VOLTSETUP_WARMRESET */
+#define OMAP54XX_STABLE_COUNT_SHIFT                                            0
+#define OMAP54XX_STABLE_COUNT_WIDTH                                            0x6
+#define OMAP54XX_STABLE_COUNT_MASK                                             (0x3f << 0)
+
+/* Used by PRM_VOLTSETUP_WARMRESET */
+#define OMAP54XX_STABLE_PRESCAL_SHIFT                                          8
+#define OMAP54XX_STABLE_PRESCAL_WIDTH                                          0x2
+#define OMAP54XX_STABLE_PRESCAL_MASK                                           (0x3 << 8)
+
+/* Used by PRM_BANDGAP_SETUP */
+#define OMAP54XX_STARTUP_COUNT_SHIFT                                           0
+#define OMAP54XX_STARTUP_COUNT_WIDTH                                           0x8
+#define OMAP54XX_STARTUP_COUNT_MASK                                            (0xff << 0)
+
+/* Renamed from STARTUP_COUNT Used by PRM_SRAM_COUNT */
+#define OMAP54XX_STARTUP_COUNT_24_31_SHIFT                                     24
+#define OMAP54XX_STARTUP_COUNT_24_31_WIDTH                                     0x8
+#define OMAP54XX_STARTUP_COUNT_24_31_MASK                                      (0xff << 24)
+
+/* Used by PM_IVA_PWRSTCTRL */
+#define OMAP54XX_TCM1_MEM_ONSTATE_SHIFT                                                20
+#define OMAP54XX_TCM1_MEM_ONSTATE_WIDTH                                                0x2
+#define OMAP54XX_TCM1_MEM_ONSTATE_MASK                                         (0x3 << 20)
+
+/* Used by PM_IVA_PWRSTCTRL */
+#define OMAP54XX_TCM1_MEM_RETSTATE_SHIFT                                       10
+#define OMAP54XX_TCM1_MEM_RETSTATE_WIDTH                                       0x1
+#define OMAP54XX_TCM1_MEM_RETSTATE_MASK                                                (1 << 10)
+
+/* Used by PM_IVA_PWRSTST */
+#define OMAP54XX_TCM1_MEM_STATEST_SHIFT                                                8
+#define OMAP54XX_TCM1_MEM_STATEST_WIDTH                                                0x2
+#define OMAP54XX_TCM1_MEM_STATEST_MASK                                         (0x3 << 8)
+
+/* Used by PM_IVA_PWRSTCTRL */
+#define OMAP54XX_TCM2_MEM_ONSTATE_SHIFT                                                22
+#define OMAP54XX_TCM2_MEM_ONSTATE_WIDTH                                                0x2
+#define OMAP54XX_TCM2_MEM_ONSTATE_MASK                                         (0x3 << 22)
+
+/* Used by PM_IVA_PWRSTCTRL */
+#define OMAP54XX_TCM2_MEM_RETSTATE_SHIFT                                       11
+#define OMAP54XX_TCM2_MEM_RETSTATE_WIDTH                                       0x1
+#define OMAP54XX_TCM2_MEM_RETSTATE_MASK                                                (1 << 11)
+
+/* Used by PM_IVA_PWRSTST */
+#define OMAP54XX_TCM2_MEM_STATEST_SHIFT                                                10
+#define OMAP54XX_TCM2_MEM_STATEST_WIDTH                                                0x2
+#define OMAP54XX_TCM2_MEM_STATEST_MASK                                         (0x3 << 10)
+
+/* Used by PRM_VP_CORE_VLIMITTO, PRM_VP_MM_VLIMITTO, PRM_VP_MPU_VLIMITTO */
+#define OMAP54XX_TIMEOUT_SHIFT                                                 0
+#define OMAP54XX_TIMEOUT_WIDTH                                                 0x10
+#define OMAP54XX_TIMEOUT_MASK                                                  (0xffff << 0)
+
+/* Used by PRM_VP_CORE_CONFIG, PRM_VP_MM_CONFIG, PRM_VP_MPU_CONFIG */
+#define OMAP54XX_TIMEOUTEN_SHIFT                                               3
+#define OMAP54XX_TIMEOUTEN_WIDTH                                               0x1
+#define OMAP54XX_TIMEOUTEN_MASK                                                        (1 << 3)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_TRANSITION_EN_SHIFT                                           8
+#define OMAP54XX_TRANSITION_EN_WIDTH                                           0x1
+#define OMAP54XX_TRANSITION_EN_MASK                                            (1 << 8)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_TRANSITION_ST_SHIFT                                           8
+#define OMAP54XX_TRANSITION_ST_WIDTH                                           0x1
+#define OMAP54XX_TRANSITION_ST_MASK                                            (1 << 8)
+
+/* Used by PRM_DEBUG_TRANS_CFG */
+#define OMAP54XX_TRIGGER_CLEAR_SHIFT                                           2
+#define OMAP54XX_TRIGGER_CLEAR_WIDTH                                           0x1
+#define OMAP54XX_TRIGGER_CLEAR_MASK                                            (1 << 2)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_TSHUT_CORE_RST_SHIFT                                          13
+#define OMAP54XX_TSHUT_CORE_RST_WIDTH                                          0x1
+#define OMAP54XX_TSHUT_CORE_RST_MASK                                           (1 << 13)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_TSHUT_MM_RST_SHIFT                                            12
+#define OMAP54XX_TSHUT_MM_RST_WIDTH                                            0x1
+#define OMAP54XX_TSHUT_MM_RST_MASK                                             (1 << 12)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_TSHUT_MPU_RST_SHIFT                                           11
+#define OMAP54XX_TSHUT_MPU_RST_WIDTH                                           0x1
+#define OMAP54XX_TSHUT_MPU_RST_MASK                                            (1 << 11)
+
+/* Used by PRM_VC_VAL_BYPASS */
+#define OMAP54XX_VALID_SHIFT                                                   24
+#define OMAP54XX_VALID_WIDTH                                                   0x1
+#define OMAP54XX_VALID_MASK                                                    (1 << 24)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VC_BYPASSACK_EN_SHIFT                                         14
+#define OMAP54XX_VC_BYPASSACK_EN_WIDTH                                         0x1
+#define OMAP54XX_VC_BYPASSACK_EN_MASK                                          (1 << 14)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VC_BYPASSACK_ST_SHIFT                                         14
+#define OMAP54XX_VC_BYPASSACK_ST_WIDTH                                         0x1
+#define OMAP54XX_VC_BYPASSACK_ST_MASK                                          (1 << 14)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VC_CORE_VPACK_EN_SHIFT                                                22
+#define OMAP54XX_VC_CORE_VPACK_EN_WIDTH                                                0x1
+#define OMAP54XX_VC_CORE_VPACK_EN_MASK                                         (1 << 22)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VC_CORE_VPACK_ST_SHIFT                                                22
+#define OMAP54XX_VC_CORE_VPACK_ST_WIDTH                                                0x1
+#define OMAP54XX_VC_CORE_VPACK_ST_MASK                                         (1 << 22)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VC_MM_VPACK_EN_SHIFT                                          30
+#define OMAP54XX_VC_MM_VPACK_EN_WIDTH                                          0x1
+#define OMAP54XX_VC_MM_VPACK_EN_MASK                                           (1 << 30)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VC_MM_VPACK_ST_SHIFT                                          30
+#define OMAP54XX_VC_MM_VPACK_ST_WIDTH                                          0x1
+#define OMAP54XX_VC_MM_VPACK_ST_MASK                                           (1 << 30)
+
+/* Used by PRM_IRQENABLE_MPU_2 */
+#define OMAP54XX_VC_MPU_VPACK_EN_SHIFT                                         6
+#define OMAP54XX_VC_MPU_VPACK_EN_WIDTH                                         0x1
+#define OMAP54XX_VC_MPU_VPACK_EN_MASK                                          (1 << 6)
+
+/* Used by PRM_IRQSTATUS_MPU_2 */
+#define OMAP54XX_VC_MPU_VPACK_ST_SHIFT                                         6
+#define OMAP54XX_VC_MPU_VPACK_ST_WIDTH                                         0x1
+#define OMAP54XX_VC_MPU_VPACK_ST_MASK                                          (1 << 6)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VC_RAERR_EN_SHIFT                                             12
+#define OMAP54XX_VC_RAERR_EN_WIDTH                                             0x1
+#define OMAP54XX_VC_RAERR_EN_MASK                                              (1 << 12)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VC_RAERR_ST_SHIFT                                             12
+#define OMAP54XX_VC_RAERR_ST_WIDTH                                             0x1
+#define OMAP54XX_VC_RAERR_ST_MASK                                              (1 << 12)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VC_SAERR_EN_SHIFT                                             11
+#define OMAP54XX_VC_SAERR_EN_WIDTH                                             0x1
+#define OMAP54XX_VC_SAERR_EN_MASK                                              (1 << 11)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VC_SAERR_ST_SHIFT                                             11
+#define OMAP54XX_VC_SAERR_ST_WIDTH                                             0x1
+#define OMAP54XX_VC_SAERR_ST_MASK                                              (1 << 11)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VC_TOERR_EN_SHIFT                                             13
+#define OMAP54XX_VC_TOERR_EN_WIDTH                                             0x1
+#define OMAP54XX_VC_TOERR_EN_MASK                                              (1 << 13)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VC_TOERR_ST_SHIFT                                             13
+#define OMAP54XX_VC_TOERR_ST_WIDTH                                             0x1
+#define OMAP54XX_VC_TOERR_ST_MASK                                              (1 << 13)
+
+/* Used by PRM_VP_CORE_VLIMITTO, PRM_VP_MM_VLIMITTO, PRM_VP_MPU_VLIMITTO */
+#define OMAP54XX_VDDMAX_SHIFT                                                  24
+#define OMAP54XX_VDDMAX_WIDTH                                                  0x8
+#define OMAP54XX_VDDMAX_MASK                                                   (0xff << 24)
+
+/* Used by PRM_VP_CORE_VLIMITTO, PRM_VP_MM_VLIMITTO, PRM_VP_MPU_VLIMITTO */
+#define OMAP54XX_VDDMIN_SHIFT                                                  16
+#define OMAP54XX_VDDMIN_WIDTH                                                  0x8
+#define OMAP54XX_VDDMIN_MASK                                                   (0xff << 16)
+
+/* Used by PRM_VOLTCTRL */
+#define OMAP54XX_VDD_CORE_I2C_DISABLE_SHIFT                                    12
+#define OMAP54XX_VDD_CORE_I2C_DISABLE_WIDTH                                    0x1
+#define OMAP54XX_VDD_CORE_I2C_DISABLE_MASK                                     (1 << 12)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_VDD_CORE_VOLT_MGR_RST_SHIFT                                   8
+#define OMAP54XX_VDD_CORE_VOLT_MGR_RST_WIDTH                                   0x1
+#define OMAP54XX_VDD_CORE_VOLT_MGR_RST_MASK                                    (1 << 8)
+
+/* Used by PRM_VOLTCTRL */
+#define OMAP54XX_VDD_MM_I2C_DISABLE_SHIFT                                      14
+#define OMAP54XX_VDD_MM_I2C_DISABLE_WIDTH                                      0x1
+#define OMAP54XX_VDD_MM_I2C_DISABLE_MASK                                       (1 << 14)
+
+/* Used by PRM_VOLTCTRL */
+#define OMAP54XX_VDD_MM_PRESENCE_SHIFT                                         9
+#define OMAP54XX_VDD_MM_PRESENCE_WIDTH                                         0x1
+#define OMAP54XX_VDD_MM_PRESENCE_MASK                                          (1 << 9)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_VDD_MM_VOLT_MGR_RST_SHIFT                                     7
+#define OMAP54XX_VDD_MM_VOLT_MGR_RST_WIDTH                                     0x1
+#define OMAP54XX_VDD_MM_VOLT_MGR_RST_MASK                                      (1 << 7)
+
+/* Used by PRM_VOLTCTRL */
+#define OMAP54XX_VDD_MPU_I2C_DISABLE_SHIFT                                     13
+#define OMAP54XX_VDD_MPU_I2C_DISABLE_WIDTH                                     0x1
+#define OMAP54XX_VDD_MPU_I2C_DISABLE_MASK                                      (1 << 13)
+
+/* Used by PRM_VOLTCTRL */
+#define OMAP54XX_VDD_MPU_PRESENCE_SHIFT                                                8
+#define OMAP54XX_VDD_MPU_PRESENCE_WIDTH                                                0x1
+#define OMAP54XX_VDD_MPU_PRESENCE_MASK                                         (1 << 8)
+
+/* Used by PRM_RSTST */
+#define OMAP54XX_VDD_MPU_VOLT_MGR_RST_SHIFT                                    6
+#define OMAP54XX_VDD_MPU_VOLT_MGR_RST_WIDTH                                    0x1
+#define OMAP54XX_VDD_MPU_VOLT_MGR_RST_MASK                                     (1 << 6)
+
+/* Used by PRM_VC_CORE_ERRST */
+#define OMAP54XX_VFSM_RA_ERR_CORE_SHIFT                                                4
+#define OMAP54XX_VFSM_RA_ERR_CORE_WIDTH                                                0x1
+#define OMAP54XX_VFSM_RA_ERR_CORE_MASK                                         (1 << 4)
+
+/* Used by PRM_VC_MM_ERRST */
+#define OMAP54XX_VFSM_RA_ERR_MM_SHIFT                                          4
+#define OMAP54XX_VFSM_RA_ERR_MM_WIDTH                                          0x1
+#define OMAP54XX_VFSM_RA_ERR_MM_MASK                                           (1 << 4)
+
+/* Used by PRM_VC_MPU_ERRST */
+#define OMAP54XX_VFSM_RA_ERR_MPU_SHIFT                                         4
+#define OMAP54XX_VFSM_RA_ERR_MPU_WIDTH                                         0x1
+#define OMAP54XX_VFSM_RA_ERR_MPU_MASK                                          (1 << 4)
+
+/* Used by PRM_VC_CORE_ERRST */
+#define OMAP54XX_VFSM_SA_ERR_CORE_SHIFT                                                3
+#define OMAP54XX_VFSM_SA_ERR_CORE_WIDTH                                                0x1
+#define OMAP54XX_VFSM_SA_ERR_CORE_MASK                                         (1 << 3)
+
+/* Used by PRM_VC_MM_ERRST */
+#define OMAP54XX_VFSM_SA_ERR_MM_SHIFT                                          3
+#define OMAP54XX_VFSM_SA_ERR_MM_WIDTH                                          0x1
+#define OMAP54XX_VFSM_SA_ERR_MM_MASK                                           (1 << 3)
+
+/* Used by PRM_VC_MPU_ERRST */
+#define OMAP54XX_VFSM_SA_ERR_MPU_SHIFT                                         3
+#define OMAP54XX_VFSM_SA_ERR_MPU_WIDTH                                         0x1
+#define OMAP54XX_VFSM_SA_ERR_MPU_MASK                                          (1 << 3)
+
+/* Used by PRM_VC_CORE_ERRST */
+#define OMAP54XX_VFSM_TIMEOUT_ERR_CORE_SHIFT                                   5
+#define OMAP54XX_VFSM_TIMEOUT_ERR_CORE_WIDTH                                   0x1
+#define OMAP54XX_VFSM_TIMEOUT_ERR_CORE_MASK                                    (1 << 5)
+
+/* Used by PRM_VC_MM_ERRST */
+#define OMAP54XX_VFSM_TIMEOUT_ERR_MM_SHIFT                                     5
+#define OMAP54XX_VFSM_TIMEOUT_ERR_MM_WIDTH                                     0x1
+#define OMAP54XX_VFSM_TIMEOUT_ERR_MM_MASK                                      (1 << 5)
+
+/* Used by PRM_VC_MPU_ERRST */
+#define OMAP54XX_VFSM_TIMEOUT_ERR_MPU_SHIFT                                    5
+#define OMAP54XX_VFSM_TIMEOUT_ERR_MPU_WIDTH                                    0x1
+#define OMAP54XX_VFSM_TIMEOUT_ERR_MPU_MASK                                     (1 << 5)
+
+/* Used by PRM_VC_SMPS_CORE_CONFIG */
+#define OMAP54XX_VOLRA_VDD_CORE_L_SHIFT                                                8
+#define OMAP54XX_VOLRA_VDD_CORE_L_WIDTH                                                0x8
+#define OMAP54XX_VOLRA_VDD_CORE_L_MASK                                         (0xff << 8)
+
+/* Used by PRM_VC_SMPS_MM_CONFIG */
+#define OMAP54XX_VOLRA_VDD_MM_L_SHIFT                                          8
+#define OMAP54XX_VOLRA_VDD_MM_L_WIDTH                                          0x8
+#define OMAP54XX_VOLRA_VDD_MM_L_MASK                                           (0xff << 8)
+
+/* Used by PRM_VC_SMPS_MPU_CONFIG */
+#define OMAP54XX_VOLRA_VDD_MPU_L_SHIFT                                         8
+#define OMAP54XX_VOLRA_VDD_MPU_L_WIDTH                                         0x8
+#define OMAP54XX_VOLRA_VDD_MPU_L_MASK                                          (0xff << 8)
+
+/* Used by PRM_VOLTST_MM, PRM_VOLTST_MPU */
+#define OMAP54XX_VOLTSTATEST_SHIFT                                             0
+#define OMAP54XX_VOLTSTATEST_WIDTH                                             0x2
+#define OMAP54XX_VOLTSTATEST_MASK                                              (0x3 << 0)
+
+/* Used by PRM_VP_CORE_CONFIG, PRM_VP_MM_CONFIG, PRM_VP_MPU_CONFIG */
+#define OMAP54XX_VPENABLE_SHIFT                                                        0
+#define OMAP54XX_VPENABLE_WIDTH                                                        0x1
+#define OMAP54XX_VPENABLE_MASK                                                 (1 << 0)
+
+/* Used by PRM_VP_CORE_STATUS, PRM_VP_MM_STATUS, PRM_VP_MPU_STATUS */
+#define OMAP54XX_VPINIDLE_SHIFT                                                        0
+#define OMAP54XX_VPINIDLE_WIDTH                                                        0x1
+#define OMAP54XX_VPINIDLE_MASK                                                 (1 << 0)
+
+/* Used by PRM_VP_CORE_VOLTAGE, PRM_VP_MM_VOLTAGE, PRM_VP_MPU_VOLTAGE */
+#define OMAP54XX_VPVOLTAGE_SHIFT                                               0
+#define OMAP54XX_VPVOLTAGE_WIDTH                                               0x8
+#define OMAP54XX_VPVOLTAGE_MASK                                                        (0xff << 0)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_CORE_EQVALUE_EN_SHIFT                                      20
+#define OMAP54XX_VP_CORE_EQVALUE_EN_WIDTH                                      0x1
+#define OMAP54XX_VP_CORE_EQVALUE_EN_MASK                                       (1 << 20)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_CORE_EQVALUE_ST_SHIFT                                      20
+#define OMAP54XX_VP_CORE_EQVALUE_ST_WIDTH                                      0x1
+#define OMAP54XX_VP_CORE_EQVALUE_ST_MASK                                       (1 << 20)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_CORE_MAXVDD_EN_SHIFT                                       18
+#define OMAP54XX_VP_CORE_MAXVDD_EN_WIDTH                                       0x1
+#define OMAP54XX_VP_CORE_MAXVDD_EN_MASK                                                (1 << 18)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_CORE_MAXVDD_ST_SHIFT                                       18
+#define OMAP54XX_VP_CORE_MAXVDD_ST_WIDTH                                       0x1
+#define OMAP54XX_VP_CORE_MAXVDD_ST_MASK                                                (1 << 18)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_CORE_MINVDD_EN_SHIFT                                       17
+#define OMAP54XX_VP_CORE_MINVDD_EN_WIDTH                                       0x1
+#define OMAP54XX_VP_CORE_MINVDD_EN_MASK                                                (1 << 17)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_CORE_MINVDD_ST_SHIFT                                       17
+#define OMAP54XX_VP_CORE_MINVDD_ST_WIDTH                                       0x1
+#define OMAP54XX_VP_CORE_MINVDD_ST_MASK                                                (1 << 17)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_CORE_NOSMPSACK_EN_SHIFT                                    19
+#define OMAP54XX_VP_CORE_NOSMPSACK_EN_WIDTH                                    0x1
+#define OMAP54XX_VP_CORE_NOSMPSACK_EN_MASK                                     (1 << 19)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_CORE_NOSMPSACK_ST_SHIFT                                    19
+#define OMAP54XX_VP_CORE_NOSMPSACK_ST_WIDTH                                    0x1
+#define OMAP54XX_VP_CORE_NOSMPSACK_ST_MASK                                     (1 << 19)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_CORE_OPPCHANGEDONE_EN_SHIFT                                        16
+#define OMAP54XX_VP_CORE_OPPCHANGEDONE_EN_WIDTH                                        0x1
+#define OMAP54XX_VP_CORE_OPPCHANGEDONE_EN_MASK                                 (1 << 16)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_CORE_OPPCHANGEDONE_ST_SHIFT                                        16
+#define OMAP54XX_VP_CORE_OPPCHANGEDONE_ST_WIDTH                                        0x1
+#define OMAP54XX_VP_CORE_OPPCHANGEDONE_ST_MASK                                 (1 << 16)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_CORE_TRANXDONE_EN_SHIFT                                    21
+#define OMAP54XX_VP_CORE_TRANXDONE_EN_WIDTH                                    0x1
+#define OMAP54XX_VP_CORE_TRANXDONE_EN_MASK                                     (1 << 21)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_CORE_TRANXDONE_ST_SHIFT                                    21
+#define OMAP54XX_VP_CORE_TRANXDONE_ST_WIDTH                                    0x1
+#define OMAP54XX_VP_CORE_TRANXDONE_ST_MASK                                     (1 << 21)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_MM_EQVALUE_EN_SHIFT                                                28
+#define OMAP54XX_VP_MM_EQVALUE_EN_WIDTH                                                0x1
+#define OMAP54XX_VP_MM_EQVALUE_EN_MASK                                         (1 << 28)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_MM_EQVALUE_ST_SHIFT                                                28
+#define OMAP54XX_VP_MM_EQVALUE_ST_WIDTH                                                0x1
+#define OMAP54XX_VP_MM_EQVALUE_ST_MASK                                         (1 << 28)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_MM_MAXVDD_EN_SHIFT                                         26
+#define OMAP54XX_VP_MM_MAXVDD_EN_WIDTH                                         0x1
+#define OMAP54XX_VP_MM_MAXVDD_EN_MASK                                          (1 << 26)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_MM_MAXVDD_ST_SHIFT                                         26
+#define OMAP54XX_VP_MM_MAXVDD_ST_WIDTH                                         0x1
+#define OMAP54XX_VP_MM_MAXVDD_ST_MASK                                          (1 << 26)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_MM_MINVDD_EN_SHIFT                                         25
+#define OMAP54XX_VP_MM_MINVDD_EN_WIDTH                                         0x1
+#define OMAP54XX_VP_MM_MINVDD_EN_MASK                                          (1 << 25)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_MM_MINVDD_ST_SHIFT                                         25
+#define OMAP54XX_VP_MM_MINVDD_ST_WIDTH                                         0x1
+#define OMAP54XX_VP_MM_MINVDD_ST_MASK                                          (1 << 25)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_MM_NOSMPSACK_EN_SHIFT                                      27
+#define OMAP54XX_VP_MM_NOSMPSACK_EN_WIDTH                                      0x1
+#define OMAP54XX_VP_MM_NOSMPSACK_EN_MASK                                       (1 << 27)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_MM_NOSMPSACK_ST_SHIFT                                      27
+#define OMAP54XX_VP_MM_NOSMPSACK_ST_WIDTH                                      0x1
+#define OMAP54XX_VP_MM_NOSMPSACK_ST_MASK                                       (1 << 27)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_MM_OPPCHANGEDONE_EN_SHIFT                                  24
+#define OMAP54XX_VP_MM_OPPCHANGEDONE_EN_WIDTH                                  0x1
+#define OMAP54XX_VP_MM_OPPCHANGEDONE_EN_MASK                                   (1 << 24)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_MM_OPPCHANGEDONE_ST_SHIFT                                  24
+#define OMAP54XX_VP_MM_OPPCHANGEDONE_ST_WIDTH                                  0x1
+#define OMAP54XX_VP_MM_OPPCHANGEDONE_ST_MASK                                   (1 << 24)
+
+/* Used by PRM_IRQENABLE_IPU, PRM_IRQENABLE_MPU */
+#define OMAP54XX_VP_MM_TRANXDONE_EN_SHIFT                                      29
+#define OMAP54XX_VP_MM_TRANXDONE_EN_WIDTH                                      0x1
+#define OMAP54XX_VP_MM_TRANXDONE_EN_MASK                                       (1 << 29)
+
+/* Used by PRM_IRQSTATUS_IPU, PRM_IRQSTATUS_MPU */
+#define OMAP54XX_VP_MM_TRANXDONE_ST_SHIFT                                      29
+#define OMAP54XX_VP_MM_TRANXDONE_ST_WIDTH                                      0x1
+#define OMAP54XX_VP_MM_TRANXDONE_ST_MASK                                       (1 << 29)
+
+/* Used by PRM_IRQENABLE_MPU_2 */
+#define OMAP54XX_VP_MPU_EQVALUE_EN_SHIFT                                       4
+#define OMAP54XX_VP_MPU_EQVALUE_EN_WIDTH                                       0x1
+#define OMAP54XX_VP_MPU_EQVALUE_EN_MASK                                                (1 << 4)
+
+/* Used by PRM_IRQSTATUS_MPU_2 */
+#define OMAP54XX_VP_MPU_EQVALUE_ST_SHIFT                                       4
+#define OMAP54XX_VP_MPU_EQVALUE_ST_WIDTH                                       0x1
+#define OMAP54XX_VP_MPU_EQVALUE_ST_MASK                                                (1 << 4)
+
+/* Used by PRM_IRQENABLE_MPU_2 */
+#define OMAP54XX_VP_MPU_MAXVDD_EN_SHIFT                                                2
+#define OMAP54XX_VP_MPU_MAXVDD_EN_WIDTH                                                0x1
+#define OMAP54XX_VP_MPU_MAXVDD_EN_MASK                                         (1 << 2)
+
+/* Used by PRM_IRQSTATUS_MPU_2 */
+#define OMAP54XX_VP_MPU_MAXVDD_ST_SHIFT                                                2
+#define OMAP54XX_VP_MPU_MAXVDD_ST_WIDTH                                                0x1
+#define OMAP54XX_VP_MPU_MAXVDD_ST_MASK                                         (1 << 2)
+
+/* Used by PRM_IRQENABLE_MPU_2 */
+#define OMAP54XX_VP_MPU_MINVDD_EN_SHIFT                                                1
+#define OMAP54XX_VP_MPU_MINVDD_EN_WIDTH                                                0x1
+#define OMAP54XX_VP_MPU_MINVDD_EN_MASK                                         (1 << 1)
+
+/* Used by PRM_IRQSTATUS_MPU_2 */
+#define OMAP54XX_VP_MPU_MINVDD_ST_SHIFT                                                1
+#define OMAP54XX_VP_MPU_MINVDD_ST_WIDTH                                                0x1
+#define OMAP54XX_VP_MPU_MINVDD_ST_MASK                                         (1 << 1)
+
+/* Used by PRM_IRQENABLE_MPU_2 */
+#define OMAP54XX_VP_MPU_NOSMPSACK_EN_SHIFT                                     3
+#define OMAP54XX_VP_MPU_NOSMPSACK_EN_WIDTH                                     0x1
+#define OMAP54XX_VP_MPU_NOSMPSACK_EN_MASK                                      (1 << 3)
+
+/* Used by PRM_IRQSTATUS_MPU_2 */
+#define OMAP54XX_VP_MPU_NOSMPSACK_ST_SHIFT                                     3
+#define OMAP54XX_VP_MPU_NOSMPSACK_ST_WIDTH                                     0x1
+#define OMAP54XX_VP_MPU_NOSMPSACK_ST_MASK                                      (1 << 3)
+
+/* Used by PRM_IRQENABLE_MPU_2 */
+#define OMAP54XX_VP_MPU_OPPCHANGEDONE_EN_SHIFT                                 0
+#define OMAP54XX_VP_MPU_OPPCHANGEDONE_EN_WIDTH                                 0x1
+#define OMAP54XX_VP_MPU_OPPCHANGEDONE_EN_MASK                                  (1 << 0)
+
+/* Used by PRM_IRQSTATUS_MPU_2 */
+#define OMAP54XX_VP_MPU_OPPCHANGEDONE_ST_SHIFT                                 0
+#define OMAP54XX_VP_MPU_OPPCHANGEDONE_ST_WIDTH                                 0x1
+#define OMAP54XX_VP_MPU_OPPCHANGEDONE_ST_MASK                                  (1 << 0)
+
+/* Used by PRM_IRQENABLE_MPU_2 */
+#define OMAP54XX_VP_MPU_TRANXDONE_EN_SHIFT                                     5
+#define OMAP54XX_VP_MPU_TRANXDONE_EN_WIDTH                                     0x1
+#define OMAP54XX_VP_MPU_TRANXDONE_EN_MASK                                      (1 << 5)
+
+/* Used by PRM_IRQSTATUS_MPU_2 */
+#define OMAP54XX_VP_MPU_TRANXDONE_ST_SHIFT                                     5
+#define OMAP54XX_VP_MPU_TRANXDONE_ST_WIDTH                                     0x1
+#define OMAP54XX_VP_MPU_TRANXDONE_ST_MASK                                      (1 << 5)
+
+/* Used by PRM_SRAM_COUNT */
+#define OMAP54XX_VSETUPCNT_VALUE_SHIFT                                         8
+#define OMAP54XX_VSETUPCNT_VALUE_WIDTH                                         0x8
+#define OMAP54XX_VSETUPCNT_VALUE_MASK                                          (0xff << 8)
+
+/* Used by PRM_VP_CORE_VSTEPMAX, PRM_VP_MM_VSTEPMAX, PRM_VP_MPU_VSTEPMAX */
+#define OMAP54XX_VSTEPMAX_SHIFT                                                        0
+#define OMAP54XX_VSTEPMAX_WIDTH                                                        0x8
+#define OMAP54XX_VSTEPMAX_MASK                                                 (0xff << 0)
+
+/* Used by PRM_VP_CORE_VSTEPMIN, PRM_VP_MM_VSTEPMIN, PRM_VP_MPU_VSTEPMIN */
+#define OMAP54XX_VSTEPMIN_SHIFT                                                        0
+#define OMAP54XX_VSTEPMIN_WIDTH                                                        0x8
+#define OMAP54XX_VSTEPMIN_MASK                                                 (0xff << 0)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DISPC_DSP_SHIFT                                       2
+#define OMAP54XX_WKUPDEP_DISPC_DSP_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_DISPC_DSP_MASK                                                (1 << 2)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DISPC_IPU_SHIFT                                       1
+#define OMAP54XX_WKUPDEP_DISPC_IPU_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_DISPC_IPU_MASK                                                (1 << 1)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DISPC_MPU_SHIFT                                       0
+#define OMAP54XX_WKUPDEP_DISPC_MPU_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_DISPC_MPU_MASK                                                (1 << 0)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DISPC_SDMA_SHIFT                                      3
+#define OMAP54XX_WKUPDEP_DISPC_SDMA_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_DISPC_SDMA_MASK                                       (1 << 3)
+
+/* Used by PM_ABE_DMIC_WKDEP */
+#define OMAP54XX_WKUPDEP_DMIC_DMA_DSP_SHIFT                                    6
+#define OMAP54XX_WKUPDEP_DMIC_DMA_DSP_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_DMIC_DMA_DSP_MASK                                     (1 << 6)
+
+/* Used by PM_ABE_DMIC_WKDEP */
+#define OMAP54XX_WKUPDEP_DMIC_DMA_SDMA_SHIFT                                   7
+#define OMAP54XX_WKUPDEP_DMIC_DMA_SDMA_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_DMIC_DMA_SDMA_MASK                                    (1 << 7)
+
+/* Used by PM_ABE_DMIC_WKDEP */
+#define OMAP54XX_WKUPDEP_DMIC_IRQ_DSP_SHIFT                                    2
+#define OMAP54XX_WKUPDEP_DMIC_IRQ_DSP_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_DMIC_IRQ_DSP_MASK                                     (1 << 2)
+
+/* Used by PM_ABE_DMIC_WKDEP */
+#define OMAP54XX_WKUPDEP_DMIC_IRQ_MPU_SHIFT                                    0
+#define OMAP54XX_WKUPDEP_DMIC_IRQ_MPU_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_DMIC_IRQ_MPU_MASK                                     (1 << 0)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_A_DSP_SHIFT                                      6
+#define OMAP54XX_WKUPDEP_DSI1_A_DSP_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_DSI1_A_DSP_MASK                                       (1 << 6)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_A_IPU_SHIFT                                      5
+#define OMAP54XX_WKUPDEP_DSI1_A_IPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_DSI1_A_IPU_MASK                                       (1 << 5)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_A_MPU_SHIFT                                      4
+#define OMAP54XX_WKUPDEP_DSI1_A_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_DSI1_A_MPU_MASK                                       (1 << 4)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_A_SDMA_SHIFT                                     7
+#define OMAP54XX_WKUPDEP_DSI1_A_SDMA_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_DSI1_A_SDMA_MASK                                      (1 << 7)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_B_DSP_SHIFT                                      10
+#define OMAP54XX_WKUPDEP_DSI1_B_DSP_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_DSI1_B_DSP_MASK                                       (1 << 10)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_B_IPU_SHIFT                                      9
+#define OMAP54XX_WKUPDEP_DSI1_B_IPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_DSI1_B_IPU_MASK                                       (1 << 9)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_B_MPU_SHIFT                                      8
+#define OMAP54XX_WKUPDEP_DSI1_B_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_DSI1_B_MPU_MASK                                       (1 << 8)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_B_SDMA_SHIFT                                     11
+#define OMAP54XX_WKUPDEP_DSI1_B_SDMA_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_DSI1_B_SDMA_MASK                                      (1 << 11)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_C_DSP_SHIFT                                      17
+#define OMAP54XX_WKUPDEP_DSI1_C_DSP_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_DSI1_C_DSP_MASK                                       (1 << 17)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_C_IPU_SHIFT                                      16
+#define OMAP54XX_WKUPDEP_DSI1_C_IPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_DSI1_C_IPU_MASK                                       (1 << 16)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_C_MPU_SHIFT                                      15
+#define OMAP54XX_WKUPDEP_DSI1_C_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_DSI1_C_MPU_MASK                                       (1 << 15)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_DSI1_C_SDMA_SHIFT                                     18
+#define OMAP54XX_WKUPDEP_DSI1_C_SDMA_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_DSI1_C_SDMA_MASK                                      (1 << 18)
+
+/* Used by PM_WKUPAON_GPIO1_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO1_IRQ1_IPU_SHIFT                                  1
+#define OMAP54XX_WKUPDEP_GPIO1_IRQ1_IPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO1_IRQ1_IPU_MASK                                   (1 << 1)
+
+/* Used by PM_WKUPAON_GPIO1_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO1_IRQ1_MPU_SHIFT                                  0
+#define OMAP54XX_WKUPDEP_GPIO1_IRQ1_MPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO1_IRQ1_MPU_MASK                                   (1 << 0)
+
+/* Used by PM_WKUPAON_GPIO1_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO1_IRQ2_DSP_SHIFT                                  6
+#define OMAP54XX_WKUPDEP_GPIO1_IRQ2_DSP_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO1_IRQ2_DSP_MASK                                   (1 << 6)
+
+/* Used by PM_L4PER_GPIO2_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO2_IRQ1_IPU_SHIFT                                  1
+#define OMAP54XX_WKUPDEP_GPIO2_IRQ1_IPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO2_IRQ1_IPU_MASK                                   (1 << 1)
+
+/* Used by PM_L4PER_GPIO2_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO2_IRQ1_MPU_SHIFT                                  0
+#define OMAP54XX_WKUPDEP_GPIO2_IRQ1_MPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO2_IRQ1_MPU_MASK                                   (1 << 0)
+
+/* Used by PM_L4PER_GPIO2_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO2_IRQ2_DSP_SHIFT                                  6
+#define OMAP54XX_WKUPDEP_GPIO2_IRQ2_DSP_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO2_IRQ2_DSP_MASK                                   (1 << 6)
+
+/* Used by PM_L4PER_GPIO3_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO3_IRQ1_MPU_SHIFT                                  0
+#define OMAP54XX_WKUPDEP_GPIO3_IRQ1_MPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO3_IRQ1_MPU_MASK                                   (1 << 0)
+
+/* Used by PM_L4PER_GPIO3_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO3_IRQ2_DSP_SHIFT                                  6
+#define OMAP54XX_WKUPDEP_GPIO3_IRQ2_DSP_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO3_IRQ2_DSP_MASK                                   (1 << 6)
+
+/* Used by PM_L4PER_GPIO4_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO4_IRQ1_MPU_SHIFT                                  0
+#define OMAP54XX_WKUPDEP_GPIO4_IRQ1_MPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO4_IRQ1_MPU_MASK                                   (1 << 0)
+
+/* Used by PM_L4PER_GPIO4_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO4_IRQ2_DSP_SHIFT                                  6
+#define OMAP54XX_WKUPDEP_GPIO4_IRQ2_DSP_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO4_IRQ2_DSP_MASK                                   (1 << 6)
+
+/* Used by PM_L4PER_GPIO5_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO5_IRQ1_MPU_SHIFT                                  0
+#define OMAP54XX_WKUPDEP_GPIO5_IRQ1_MPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO5_IRQ1_MPU_MASK                                   (1 << 0)
+
+/* Used by PM_L4PER_GPIO5_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO5_IRQ2_DSP_SHIFT                                  6
+#define OMAP54XX_WKUPDEP_GPIO5_IRQ2_DSP_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO5_IRQ2_DSP_MASK                                   (1 << 6)
+
+/* Used by PM_L4PER_GPIO6_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO6_IRQ1_MPU_SHIFT                                  0
+#define OMAP54XX_WKUPDEP_GPIO6_IRQ1_MPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO6_IRQ1_MPU_MASK                                   (1 << 0)
+
+/* Used by PM_L4PER_GPIO6_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO6_IRQ2_DSP_SHIFT                                  6
+#define OMAP54XX_WKUPDEP_GPIO6_IRQ2_DSP_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO6_IRQ2_DSP_MASK                                   (1 << 6)
+
+/* Used by PM_L4PER_GPIO7_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO7_IRQ1_MPU_SHIFT                                  0
+#define OMAP54XX_WKUPDEP_GPIO7_IRQ1_MPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO7_IRQ1_MPU_MASK                                   (1 << 0)
+
+/* Used by PM_L4PER_GPIO8_WKDEP */
+#define OMAP54XX_WKUPDEP_GPIO8_IRQ1_MPU_SHIFT                                  0
+#define OMAP54XX_WKUPDEP_GPIO8_IRQ1_MPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_GPIO8_IRQ1_MPU_MASK                                   (1 << 0)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_HDMIDMA_SDMA_SHIFT                                    19
+#define OMAP54XX_WKUPDEP_HDMIDMA_SDMA_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_HDMIDMA_SDMA_MASK                                     (1 << 19)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_HDMIIRQ_DSP_SHIFT                                     14
+#define OMAP54XX_WKUPDEP_HDMIIRQ_DSP_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_HDMIIRQ_DSP_MASK                                      (1 << 14)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_HDMIIRQ_IPU_SHIFT                                     13
+#define OMAP54XX_WKUPDEP_HDMIIRQ_IPU_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_HDMIIRQ_IPU_MASK                                      (1 << 13)
+
+/* Used by PM_DSS_DSS_WKDEP */
+#define OMAP54XX_WKUPDEP_HDMIIRQ_MPU_SHIFT                                     12
+#define OMAP54XX_WKUPDEP_HDMIIRQ_MPU_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_HDMIIRQ_MPU_MASK                                      (1 << 12)
+
+/* Used by PM_L3INIT_HSI_WKDEP */
+#define OMAP54XX_WKUPDEP_HSI_DSP_DSP_SHIFT                                     6
+#define OMAP54XX_WKUPDEP_HSI_DSP_DSP_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_HSI_DSP_DSP_MASK                                      (1 << 6)
+
+/* Used by PM_L3INIT_HSI_WKDEP */
+#define OMAP54XX_WKUPDEP_HSI_MCU_IPU_SHIFT                                     1
+#define OMAP54XX_WKUPDEP_HSI_MCU_IPU_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_HSI_MCU_IPU_MASK                                      (1 << 1)
+
+/* Used by PM_L3INIT_HSI_WKDEP */
+#define OMAP54XX_WKUPDEP_HSI_MCU_MPU_SHIFT                                     0
+#define OMAP54XX_WKUPDEP_HSI_MCU_MPU_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_HSI_MCU_MPU_MASK                                      (1 << 0)
+
+/* Used by PM_L4PER_I2C1_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C1_DMA_SDMA_SHIFT                                   7
+#define OMAP54XX_WKUPDEP_I2C1_DMA_SDMA_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_I2C1_DMA_SDMA_MASK                                    (1 << 7)
+
+/* Used by PM_L4PER_I2C1_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C1_IRQ_IPU_SHIFT                                    1
+#define OMAP54XX_WKUPDEP_I2C1_IRQ_IPU_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_I2C1_IRQ_IPU_MASK                                     (1 << 1)
+
+/* Used by PM_L4PER_I2C1_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C1_IRQ_MPU_SHIFT                                    0
+#define OMAP54XX_WKUPDEP_I2C1_IRQ_MPU_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_I2C1_IRQ_MPU_MASK                                     (1 << 0)
+
+/* Used by PM_L4PER_I2C2_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C2_DMA_SDMA_SHIFT                                   7
+#define OMAP54XX_WKUPDEP_I2C2_DMA_SDMA_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_I2C2_DMA_SDMA_MASK                                    (1 << 7)
+
+/* Used by PM_L4PER_I2C2_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C2_IRQ_IPU_SHIFT                                    1
+#define OMAP54XX_WKUPDEP_I2C2_IRQ_IPU_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_I2C2_IRQ_IPU_MASK                                     (1 << 1)
+
+/* Used by PM_L4PER_I2C2_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C2_IRQ_MPU_SHIFT                                    0
+#define OMAP54XX_WKUPDEP_I2C2_IRQ_MPU_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_I2C2_IRQ_MPU_MASK                                     (1 << 0)
+
+/* Used by PM_L4PER_I2C3_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C3_DMA_SDMA_SHIFT                                   7
+#define OMAP54XX_WKUPDEP_I2C3_DMA_SDMA_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_I2C3_DMA_SDMA_MASK                                    (1 << 7)
+
+/* Used by PM_L4PER_I2C3_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C3_IRQ_IPU_SHIFT                                    1
+#define OMAP54XX_WKUPDEP_I2C3_IRQ_IPU_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_I2C3_IRQ_IPU_MASK                                     (1 << 1)
+
+/* Used by PM_L4PER_I2C3_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C3_IRQ_MPU_SHIFT                                    0
+#define OMAP54XX_WKUPDEP_I2C3_IRQ_MPU_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_I2C3_IRQ_MPU_MASK                                     (1 << 0)
+
+/* Used by PM_L4PER_I2C4_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C4_DMA_SDMA_SHIFT                                   7
+#define OMAP54XX_WKUPDEP_I2C4_DMA_SDMA_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_I2C4_DMA_SDMA_MASK                                    (1 << 7)
+
+/* Used by PM_L4PER_I2C4_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C4_IRQ_IPU_SHIFT                                    1
+#define OMAP54XX_WKUPDEP_I2C4_IRQ_IPU_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_I2C4_IRQ_IPU_MASK                                     (1 << 1)
+
+/* Used by PM_L4PER_I2C4_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C4_IRQ_MPU_SHIFT                                    0
+#define OMAP54XX_WKUPDEP_I2C4_IRQ_MPU_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_I2C4_IRQ_MPU_MASK                                     (1 << 0)
+
+/* Used by PM_L4PER_I2C5_WKDEP */
+#define OMAP54XX_WKUPDEP_I2C5_IRQ_MPU_SHIFT                                    0
+#define OMAP54XX_WKUPDEP_I2C5_IRQ_MPU_WIDTH                                    0x1
+#define OMAP54XX_WKUPDEP_I2C5_IRQ_MPU_MASK                                     (1 << 0)
+
+/* Used by PM_WKUPAON_KBD_WKDEP */
+#define OMAP54XX_WKUPDEP_KBD_MPU_SHIFT                                         0
+#define OMAP54XX_WKUPDEP_KBD_MPU_WIDTH                                         0x1
+#define OMAP54XX_WKUPDEP_KBD_MPU_MASK                                          (1 << 0)
+
+/* Used by PM_ABE_MCASP_WKDEP */
+#define OMAP54XX_WKUPDEP_MCASP_DMA_DSP_SHIFT                                   6
+#define OMAP54XX_WKUPDEP_MCASP_DMA_DSP_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_MCASP_DMA_DSP_MASK                                    (1 << 6)
+
+/* Used by PM_ABE_MCASP_WKDEP */
+#define OMAP54XX_WKUPDEP_MCASP_DMA_SDMA_SHIFT                                  7
+#define OMAP54XX_WKUPDEP_MCASP_DMA_SDMA_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_MCASP_DMA_SDMA_MASK                                   (1 << 7)
+
+/* Used by PM_ABE_MCASP_WKDEP */
+#define OMAP54XX_WKUPDEP_MCASP_IRQ_DSP_SHIFT                                   2
+#define OMAP54XX_WKUPDEP_MCASP_IRQ_DSP_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_MCASP_IRQ_DSP_MASK                                    (1 << 2)
+
+/* Used by PM_ABE_MCASP_WKDEP */
+#define OMAP54XX_WKUPDEP_MCASP_IRQ_MPU_SHIFT                                   0
+#define OMAP54XX_WKUPDEP_MCASP_IRQ_MPU_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_MCASP_IRQ_MPU_MASK                                    (1 << 0)
+
+/* Used by PM_ABE_MCBSP1_WKDEP */
+#define OMAP54XX_WKUPDEP_MCBSP1_DSP_SHIFT                                      2
+#define OMAP54XX_WKUPDEP_MCBSP1_DSP_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCBSP1_DSP_MASK                                       (1 << 2)
+
+/* Used by PM_ABE_MCBSP1_WKDEP */
+#define OMAP54XX_WKUPDEP_MCBSP1_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_MCBSP1_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCBSP1_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_ABE_MCBSP1_WKDEP */
+#define OMAP54XX_WKUPDEP_MCBSP1_SDMA_SHIFT                                     3
+#define OMAP54XX_WKUPDEP_MCBSP1_SDMA_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_MCBSP1_SDMA_MASK                                      (1 << 3)
+
+/* Used by PM_ABE_MCBSP2_WKDEP */
+#define OMAP54XX_WKUPDEP_MCBSP2_DSP_SHIFT                                      2
+#define OMAP54XX_WKUPDEP_MCBSP2_DSP_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCBSP2_DSP_MASK                                       (1 << 2)
+
+/* Used by PM_ABE_MCBSP2_WKDEP */
+#define OMAP54XX_WKUPDEP_MCBSP2_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_MCBSP2_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCBSP2_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_ABE_MCBSP2_WKDEP */
+#define OMAP54XX_WKUPDEP_MCBSP2_SDMA_SHIFT                                     3
+#define OMAP54XX_WKUPDEP_MCBSP2_SDMA_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_MCBSP2_SDMA_MASK                                      (1 << 3)
+
+/* Used by PM_ABE_MCBSP3_WKDEP */
+#define OMAP54XX_WKUPDEP_MCBSP3_DSP_SHIFT                                      2
+#define OMAP54XX_WKUPDEP_MCBSP3_DSP_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCBSP3_DSP_MASK                                       (1 << 2)
+
+/* Used by PM_ABE_MCBSP3_WKDEP */
+#define OMAP54XX_WKUPDEP_MCBSP3_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_MCBSP3_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCBSP3_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_ABE_MCBSP3_WKDEP */
+#define OMAP54XX_WKUPDEP_MCBSP3_SDMA_SHIFT                                     3
+#define OMAP54XX_WKUPDEP_MCBSP3_SDMA_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_MCBSP3_SDMA_MASK                                      (1 << 3)
+
+/* Used by PM_ABE_MCPDM_WKDEP */
+#define OMAP54XX_WKUPDEP_MCPDM_DMA_DSP_SHIFT                                   6
+#define OMAP54XX_WKUPDEP_MCPDM_DMA_DSP_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_MCPDM_DMA_DSP_MASK                                    (1 << 6)
+
+/* Used by PM_ABE_MCPDM_WKDEP */
+#define OMAP54XX_WKUPDEP_MCPDM_DMA_SDMA_SHIFT                                  7
+#define OMAP54XX_WKUPDEP_MCPDM_DMA_SDMA_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_MCPDM_DMA_SDMA_MASK                                   (1 << 7)
+
+/* Used by PM_ABE_MCPDM_WKDEP */
+#define OMAP54XX_WKUPDEP_MCPDM_IRQ_DSP_SHIFT                                   2
+#define OMAP54XX_WKUPDEP_MCPDM_IRQ_DSP_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_MCPDM_IRQ_DSP_MASK                                    (1 << 2)
+
+/* Used by PM_ABE_MCPDM_WKDEP */
+#define OMAP54XX_WKUPDEP_MCPDM_IRQ_MPU_SHIFT                                   0
+#define OMAP54XX_WKUPDEP_MCPDM_IRQ_MPU_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_MCPDM_IRQ_MPU_MASK                                    (1 << 0)
+
+/* Used by PM_L4PER_MCSPI1_WKDEP */
+#define OMAP54XX_WKUPDEP_MCSPI1_DSP_SHIFT                                      2
+#define OMAP54XX_WKUPDEP_MCSPI1_DSP_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCSPI1_DSP_MASK                                       (1 << 2)
+
+/* Used by PM_L4PER_MCSPI1_WKDEP */
+#define OMAP54XX_WKUPDEP_MCSPI1_IPU_SHIFT                                      1
+#define OMAP54XX_WKUPDEP_MCSPI1_IPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCSPI1_IPU_MASK                                       (1 << 1)
+
+/* Used by PM_L4PER_MCSPI1_WKDEP */
+#define OMAP54XX_WKUPDEP_MCSPI1_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_MCSPI1_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCSPI1_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_L4PER_MCSPI1_WKDEP */
+#define OMAP54XX_WKUPDEP_MCSPI1_SDMA_SHIFT                                     3
+#define OMAP54XX_WKUPDEP_MCSPI1_SDMA_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_MCSPI1_SDMA_MASK                                      (1 << 3)
+
+/* Used by PM_L4PER_MCSPI2_WKDEP */
+#define OMAP54XX_WKUPDEP_MCSPI2_IPU_SHIFT                                      1
+#define OMAP54XX_WKUPDEP_MCSPI2_IPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCSPI2_IPU_MASK                                       (1 << 1)
+
+/* Used by PM_L4PER_MCSPI2_WKDEP */
+#define OMAP54XX_WKUPDEP_MCSPI2_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_MCSPI2_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCSPI2_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_L4PER_MCSPI2_WKDEP */
+#define OMAP54XX_WKUPDEP_MCSPI2_SDMA_SHIFT                                     3
+#define OMAP54XX_WKUPDEP_MCSPI2_SDMA_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_MCSPI2_SDMA_MASK                                      (1 << 3)
+
+/* Used by PM_L4PER_MCSPI3_WKDEP */
+#define OMAP54XX_WKUPDEP_MCSPI3_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_MCSPI3_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCSPI3_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_L4PER_MCSPI3_WKDEP */
+#define OMAP54XX_WKUPDEP_MCSPI3_SDMA_SHIFT                                     3
+#define OMAP54XX_WKUPDEP_MCSPI3_SDMA_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_MCSPI3_SDMA_MASK                                      (1 << 3)
+
+/* Used by PM_L4PER_MCSPI4_WKDEP */
+#define OMAP54XX_WKUPDEP_MCSPI4_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_MCSPI4_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_MCSPI4_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_L4PER_MCSPI4_WKDEP */
+#define OMAP54XX_WKUPDEP_MCSPI4_SDMA_SHIFT                                     3
+#define OMAP54XX_WKUPDEP_MCSPI4_SDMA_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_MCSPI4_SDMA_MASK                                      (1 << 3)
+
+/* Used by PM_L3INIT_MMC1_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC1_DSP_SHIFT                                                2
+#define OMAP54XX_WKUPDEP_MMC1_DSP_WIDTH                                                0x1
+#define OMAP54XX_WKUPDEP_MMC1_DSP_MASK                                         (1 << 2)
+
+/* Used by PM_L3INIT_MMC1_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC1_IPU_SHIFT                                                1
+#define OMAP54XX_WKUPDEP_MMC1_IPU_WIDTH                                                0x1
+#define OMAP54XX_WKUPDEP_MMC1_IPU_MASK                                         (1 << 1)
+
+/* Used by PM_L3INIT_MMC1_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC1_MPU_SHIFT                                                0
+#define OMAP54XX_WKUPDEP_MMC1_MPU_WIDTH                                                0x1
+#define OMAP54XX_WKUPDEP_MMC1_MPU_MASK                                         (1 << 0)
+
+/* Used by PM_L3INIT_MMC1_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC1_SDMA_SHIFT                                       3
+#define OMAP54XX_WKUPDEP_MMC1_SDMA_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_MMC1_SDMA_MASK                                                (1 << 3)
+
+/* Used by PM_L3INIT_MMC2_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC2_DSP_SHIFT                                                2
+#define OMAP54XX_WKUPDEP_MMC2_DSP_WIDTH                                                0x1
+#define OMAP54XX_WKUPDEP_MMC2_DSP_MASK                                         (1 << 2)
+
+/* Used by PM_L3INIT_MMC2_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC2_IPU_SHIFT                                                1
+#define OMAP54XX_WKUPDEP_MMC2_IPU_WIDTH                                                0x1
+#define OMAP54XX_WKUPDEP_MMC2_IPU_MASK                                         (1 << 1)
+
+/* Used by PM_L3INIT_MMC2_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC2_MPU_SHIFT                                                0
+#define OMAP54XX_WKUPDEP_MMC2_MPU_WIDTH                                                0x1
+#define OMAP54XX_WKUPDEP_MMC2_MPU_MASK                                         (1 << 0)
+
+/* Used by PM_L3INIT_MMC2_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC2_SDMA_SHIFT                                       3
+#define OMAP54XX_WKUPDEP_MMC2_SDMA_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_MMC2_SDMA_MASK                                                (1 << 3)
+
+/* Used by PM_L4PER_MMC3_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC3_IPU_SHIFT                                                1
+#define OMAP54XX_WKUPDEP_MMC3_IPU_WIDTH                                                0x1
+#define OMAP54XX_WKUPDEP_MMC3_IPU_MASK                                         (1 << 1)
+
+/* Used by PM_L4PER_MMC3_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC3_MPU_SHIFT                                                0
+#define OMAP54XX_WKUPDEP_MMC3_MPU_WIDTH                                                0x1
+#define OMAP54XX_WKUPDEP_MMC3_MPU_MASK                                         (1 << 0)
+
+/* Used by PM_L4PER_MMC3_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC3_SDMA_SHIFT                                       3
+#define OMAP54XX_WKUPDEP_MMC3_SDMA_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_MMC3_SDMA_MASK                                                (1 << 3)
+
+/* Used by PM_L4PER_MMC4_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC4_MPU_SHIFT                                                0
+#define OMAP54XX_WKUPDEP_MMC4_MPU_WIDTH                                                0x1
+#define OMAP54XX_WKUPDEP_MMC4_MPU_MASK                                         (1 << 0)
+
+/* Used by PM_L4PER_MMC4_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC4_SDMA_SHIFT                                       3
+#define OMAP54XX_WKUPDEP_MMC4_SDMA_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_MMC4_SDMA_MASK                                                (1 << 3)
+
+/* Used by PM_L4PER_MMC5_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC5_MPU_SHIFT                                                0
+#define OMAP54XX_WKUPDEP_MMC5_MPU_WIDTH                                                0x1
+#define OMAP54XX_WKUPDEP_MMC5_MPU_MASK                                         (1 << 0)
+
+/* Used by PM_L4PER_MMC5_WKDEP */
+#define OMAP54XX_WKUPDEP_MMC5_SDMA_SHIFT                                       3
+#define OMAP54XX_WKUPDEP_MMC5_SDMA_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_MMC5_SDMA_MASK                                                (1 << 3)
+
+/* Used by PM_L3INIT_SATA_WKDEP */
+#define OMAP54XX_WKUPDEP_SATA_MPU_SHIFT                                                0
+#define OMAP54XX_WKUPDEP_SATA_MPU_WIDTH                                                0x1
+#define OMAP54XX_WKUPDEP_SATA_MPU_MASK                                         (1 << 0)
+
+/* Used by PM_ABE_SLIMBUS1_WKDEP */
+#define OMAP54XX_WKUPDEP_SLIMBUS1_DMA_DSP_SHIFT                                        6
+#define OMAP54XX_WKUPDEP_SLIMBUS1_DMA_DSP_WIDTH                                        0x1
+#define OMAP54XX_WKUPDEP_SLIMBUS1_DMA_DSP_MASK                                 (1 << 6)
+
+/* Used by PM_ABE_SLIMBUS1_WKDEP */
+#define OMAP54XX_WKUPDEP_SLIMBUS1_DMA_SDMA_SHIFT                               7
+#define OMAP54XX_WKUPDEP_SLIMBUS1_DMA_SDMA_WIDTH                               0x1
+#define OMAP54XX_WKUPDEP_SLIMBUS1_DMA_SDMA_MASK                                        (1 << 7)
+
+/* Used by PM_ABE_SLIMBUS1_WKDEP */
+#define OMAP54XX_WKUPDEP_SLIMBUS1_IRQ_DSP_SHIFT                                        2
+#define OMAP54XX_WKUPDEP_SLIMBUS1_IRQ_DSP_WIDTH                                        0x1
+#define OMAP54XX_WKUPDEP_SLIMBUS1_IRQ_DSP_MASK                                 (1 << 2)
+
+/* Used by PM_ABE_SLIMBUS1_WKDEP */
+#define OMAP54XX_WKUPDEP_SLIMBUS1_IRQ_MPU_SHIFT                                        0
+#define OMAP54XX_WKUPDEP_SLIMBUS1_IRQ_MPU_WIDTH                                        0x1
+#define OMAP54XX_WKUPDEP_SLIMBUS1_IRQ_MPU_MASK                                 (1 << 0)
+
+/* Used by PM_COREAON_SMARTREFLEX_CORE_WKDEP */
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_CORE_IPU_SHIFT                            1
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_CORE_IPU_WIDTH                            0x1
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_CORE_IPU_MASK                             (1 << 1)
+
+/* Used by PM_COREAON_SMARTREFLEX_CORE_WKDEP */
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_CORE_MPU_SHIFT                            0
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_CORE_MPU_WIDTH                            0x1
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_CORE_MPU_MASK                             (1 << 0)
+
+/* Used by PM_COREAON_SMARTREFLEX_MM_WKDEP */
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_MM_MPU_SHIFT                              0
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_MM_MPU_WIDTH                              0x1
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_MM_MPU_MASK                               (1 << 0)
+
+/* Used by PM_COREAON_SMARTREFLEX_MPU_WKDEP */
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_MPU_MPU_SHIFT                             0
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_MPU_MPU_WIDTH                             0x1
+#define OMAP54XX_WKUPDEP_SMARTREFLEX_MPU_MPU_MASK                              (1 << 0)
+
+/* Used by PM_L4PER_TIMER10_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER10_MPU_SHIFT                                     0
+#define OMAP54XX_WKUPDEP_TIMER10_MPU_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_TIMER10_MPU_MASK                                      (1 << 0)
+
+/* Used by PM_L4PER_TIMER11_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER11_IPU_SHIFT                                     1
+#define OMAP54XX_WKUPDEP_TIMER11_IPU_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_TIMER11_IPU_MASK                                      (1 << 1)
+
+/* Used by PM_L4PER_TIMER11_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER11_MPU_SHIFT                                     0
+#define OMAP54XX_WKUPDEP_TIMER11_MPU_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_TIMER11_MPU_MASK                                      (1 << 0)
+
+/* Used by PM_WKUPAON_TIMER12_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER12_MPU_SHIFT                                     0
+#define OMAP54XX_WKUPDEP_TIMER12_MPU_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_TIMER12_MPU_MASK                                      (1 << 0)
+
+/* Used by PM_WKUPAON_TIMER1_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER1_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_TIMER1_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER1_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_L4PER_TIMER2_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER2_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_TIMER2_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER2_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_L4PER_TIMER3_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER3_IPU_SHIFT                                      1
+#define OMAP54XX_WKUPDEP_TIMER3_IPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER3_IPU_MASK                                       (1 << 1)
+
+/* Used by PM_L4PER_TIMER3_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER3_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_TIMER3_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER3_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_L4PER_TIMER4_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER4_IPU_SHIFT                                      1
+#define OMAP54XX_WKUPDEP_TIMER4_IPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER4_IPU_MASK                                       (1 << 1)
+
+/* Used by PM_L4PER_TIMER4_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER4_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_TIMER4_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER4_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_ABE_TIMER5_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER5_DSP_SHIFT                                      2
+#define OMAP54XX_WKUPDEP_TIMER5_DSP_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER5_DSP_MASK                                       (1 << 2)
+
+/* Used by PM_ABE_TIMER5_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER5_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_TIMER5_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER5_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_ABE_TIMER6_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER6_DSP_SHIFT                                      2
+#define OMAP54XX_WKUPDEP_TIMER6_DSP_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER6_DSP_MASK                                       (1 << 2)
+
+/* Used by PM_ABE_TIMER6_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER6_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_TIMER6_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER6_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_ABE_TIMER7_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER7_DSP_SHIFT                                      2
+#define OMAP54XX_WKUPDEP_TIMER7_DSP_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER7_DSP_MASK                                       (1 << 2)
+
+/* Used by PM_ABE_TIMER7_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER7_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_TIMER7_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER7_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_ABE_TIMER8_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER8_DSP_SHIFT                                      2
+#define OMAP54XX_WKUPDEP_TIMER8_DSP_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER8_DSP_MASK                                       (1 << 2)
+
+/* Used by PM_ABE_TIMER8_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER8_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_TIMER8_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER8_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_L4PER_TIMER9_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER9_IPU_SHIFT                                      1
+#define OMAP54XX_WKUPDEP_TIMER9_IPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER9_IPU_MASK                                       (1 << 1)
+
+/* Used by PM_L4PER_TIMER9_WKDEP */
+#define OMAP54XX_WKUPDEP_TIMER9_MPU_SHIFT                                      0
+#define OMAP54XX_WKUPDEP_TIMER9_MPU_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_TIMER9_MPU_MASK                                       (1 << 0)
+
+/* Used by PM_L4PER_UART1_WKDEP */
+#define OMAP54XX_WKUPDEP_UART1_MPU_SHIFT                                       0
+#define OMAP54XX_WKUPDEP_UART1_MPU_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_UART1_MPU_MASK                                                (1 << 0)
+
+/* Used by PM_L4PER_UART1_WKDEP */
+#define OMAP54XX_WKUPDEP_UART1_SDMA_SHIFT                                      3
+#define OMAP54XX_WKUPDEP_UART1_SDMA_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_UART1_SDMA_MASK                                       (1 << 3)
+
+/* Used by PM_L4PER_UART2_WKDEP */
+#define OMAP54XX_WKUPDEP_UART2_MPU_SHIFT                                       0
+#define OMAP54XX_WKUPDEP_UART2_MPU_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_UART2_MPU_MASK                                                (1 << 0)
+
+/* Used by PM_L4PER_UART2_WKDEP */
+#define OMAP54XX_WKUPDEP_UART2_SDMA_SHIFT                                      3
+#define OMAP54XX_WKUPDEP_UART2_SDMA_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_UART2_SDMA_MASK                                       (1 << 3)
+
+/* Used by PM_L4PER_UART3_WKDEP */
+#define OMAP54XX_WKUPDEP_UART3_DSP_SHIFT                                       2
+#define OMAP54XX_WKUPDEP_UART3_DSP_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_UART3_DSP_MASK                                                (1 << 2)
+
+/* Used by PM_L4PER_UART3_WKDEP */
+#define OMAP54XX_WKUPDEP_UART3_IPU_SHIFT                                       1
+#define OMAP54XX_WKUPDEP_UART3_IPU_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_UART3_IPU_MASK                                                (1 << 1)
+
+/* Used by PM_L4PER_UART3_WKDEP */
+#define OMAP54XX_WKUPDEP_UART3_MPU_SHIFT                                       0
+#define OMAP54XX_WKUPDEP_UART3_MPU_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_UART3_MPU_MASK                                                (1 << 0)
+
+/* Used by PM_L4PER_UART3_WKDEP */
+#define OMAP54XX_WKUPDEP_UART3_SDMA_SHIFT                                      3
+#define OMAP54XX_WKUPDEP_UART3_SDMA_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_UART3_SDMA_MASK                                       (1 << 3)
+
+/* Used by PM_L4PER_UART4_WKDEP */
+#define OMAP54XX_WKUPDEP_UART4_MPU_SHIFT                                       0
+#define OMAP54XX_WKUPDEP_UART4_MPU_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_UART4_MPU_MASK                                                (1 << 0)
+
+/* Used by PM_L4PER_UART4_WKDEP */
+#define OMAP54XX_WKUPDEP_UART4_SDMA_SHIFT                                      3
+#define OMAP54XX_WKUPDEP_UART4_SDMA_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_UART4_SDMA_MASK                                       (1 << 3)
+
+/* Used by PM_L4PER_UART5_WKDEP */
+#define OMAP54XX_WKUPDEP_UART5_MPU_SHIFT                                       0
+#define OMAP54XX_WKUPDEP_UART5_MPU_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_UART5_MPU_MASK                                                (1 << 0)
+
+/* Used by PM_L4PER_UART5_WKDEP */
+#define OMAP54XX_WKUPDEP_UART5_SDMA_SHIFT                                      3
+#define OMAP54XX_WKUPDEP_UART5_SDMA_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_UART5_SDMA_MASK                                       (1 << 3)
+
+/* Used by PM_L4PER_UART6_WKDEP */
+#define OMAP54XX_WKUPDEP_UART6_MPU_SHIFT                                       0
+#define OMAP54XX_WKUPDEP_UART6_MPU_WIDTH                                       0x1
+#define OMAP54XX_WKUPDEP_UART6_MPU_MASK                                                (1 << 0)
+
+/* Used by PM_L4PER_UART6_WKDEP */
+#define OMAP54XX_WKUPDEP_UART6_SDMA_SHIFT                                      3
+#define OMAP54XX_WKUPDEP_UART6_SDMA_WIDTH                                      0x1
+#define OMAP54XX_WKUPDEP_UART6_SDMA_MASK                                       (1 << 3)
+
+/* Used by PM_L3INIT_UNIPRO2_WKDEP */
+#define OMAP54XX_WKUPDEP_UNIPRO2_MPU_SHIFT                                     0
+#define OMAP54XX_WKUPDEP_UNIPRO2_MPU_WIDTH                                     0x1
+#define OMAP54XX_WKUPDEP_UNIPRO2_MPU_MASK                                      (1 << 0)
+
+/* Used by PM_L3INIT_USB_HOST_HS_WKDEP */
+#define OMAP54XX_WKUPDEP_USB_HOST_HS_IPU_SHIFT                                 1
+#define OMAP54XX_WKUPDEP_USB_HOST_HS_IPU_WIDTH                                 0x1
+#define OMAP54XX_WKUPDEP_USB_HOST_HS_IPU_MASK                                  (1 << 1)
+
+/* Used by PM_L3INIT_USB_HOST_HS_WKDEP */
+#define OMAP54XX_WKUPDEP_USB_HOST_HS_MPU_SHIFT                                 0
+#define OMAP54XX_WKUPDEP_USB_HOST_HS_MPU_WIDTH                                 0x1
+#define OMAP54XX_WKUPDEP_USB_HOST_HS_MPU_MASK                                  (1 << 0)
+
+/* Used by PM_L3INIT_USB_OTG_SS_WKDEP */
+#define OMAP54XX_WKUPDEP_USB_OTG_SS_IPU_SHIFT                                  1
+#define OMAP54XX_WKUPDEP_USB_OTG_SS_IPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_USB_OTG_SS_IPU_MASK                                   (1 << 1)
+
+/* Used by PM_L3INIT_USB_OTG_SS_WKDEP */
+#define OMAP54XX_WKUPDEP_USB_OTG_SS_MPU_SHIFT                                  0
+#define OMAP54XX_WKUPDEP_USB_OTG_SS_MPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_USB_OTG_SS_MPU_MASK                                   (1 << 0)
+
+/* Used by PM_L3INIT_USB_TLL_HS_WKDEP */
+#define OMAP54XX_WKUPDEP_USB_TLL_HS_IPU_SHIFT                                  1
+#define OMAP54XX_WKUPDEP_USB_TLL_HS_IPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_USB_TLL_HS_IPU_MASK                                   (1 << 1)
+
+/* Used by PM_L3INIT_USB_TLL_HS_WKDEP */
+#define OMAP54XX_WKUPDEP_USB_TLL_HS_MPU_SHIFT                                  0
+#define OMAP54XX_WKUPDEP_USB_TLL_HS_MPU_WIDTH                                  0x1
+#define OMAP54XX_WKUPDEP_USB_TLL_HS_MPU_MASK                                   (1 << 0)
+
+/* Used by PM_WKUPAON_WD_TIMER2_WKDEP */
+#define OMAP54XX_WKUPDEP_WD_TIMER2_MPU_SHIFT                                   0
+#define OMAP54XX_WKUPDEP_WD_TIMER2_MPU_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_WD_TIMER2_MPU_MASK                                    (1 << 0)
+
+/* Used by PM_ABE_WD_TIMER3_WKDEP */
+#define OMAP54XX_WKUPDEP_WD_TIMER3_MPU_SHIFT                                   0
+#define OMAP54XX_WKUPDEP_WD_TIMER3_MPU_WIDTH                                   0x1
+#define OMAP54XX_WKUPDEP_WD_TIMER3_MPU_MASK                                    (1 << 0)
+
+/* Used by PRM_IO_PMCTRL */
+#define OMAP54XX_WUCLK_CTRL_SHIFT                                              8
+#define OMAP54XX_WUCLK_CTRL_WIDTH                                              0x1
+#define OMAP54XX_WUCLK_CTRL_MASK                                               (1 << 8)
+
+/* Used by PRM_IO_PMCTRL */
+#define OMAP54XX_WUCLK_STATUS_SHIFT                                            9
+#define OMAP54XX_WUCLK_STATUS_WIDTH                                            0x1
+#define OMAP54XX_WUCLK_STATUS_MASK                                             (1 << 9)
+
+/* Used by REVISION_PRM */
+#define OMAP54XX_X_MAJOR_SHIFT                                                 8
+#define OMAP54XX_X_MAJOR_WIDTH                                                 0x3
+#define OMAP54XX_X_MAJOR_MASK                                                  (0x7 << 8)
+
+/* Used by REVISION_PRM */
+#define OMAP54XX_Y_MINOR_SHIFT                                                 0
+#define OMAP54XX_Y_MINOR_WIDTH                                                 0x6
+#define OMAP54XX_Y_MINOR_MASK                                                  (0x3f << 0)
+#endif
index 418de9c3b3195e0ccc283fc90a70a486beafa3b3..3f16083e20ce2eb8923e1b6c4b38ae52546a8cd3 100644 (file)
@@ -179,10 +179,22 @@ static int omap2xxx_pwrdm_read_pwrst(struct powerdomain *pwrdm)
        return omap2xxx_pwrst_to_common_pwrst(omap2xxx_pwrst);
 }
 
+static int omap2xxx_pwrdm_read_prev_pwrst(struct powerdomain *pwrdm)
+{
+       u8 omap2xxx_pwrst;
+
+       omap2xxx_pwrst = omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs,
+                                                      OMAP2_PM_PWSTST,
+                                                      OMAP24XX_LASTSTATEENTERED_MASK);
+
+       return omap2xxx_pwrst_to_common_pwrst(omap2xxx_pwrst);
+}
+
 struct pwrdm_ops omap2_pwrdm_operations = {
        .pwrdm_set_next_pwrst   = omap2xxx_pwrdm_set_next_pwrst,
        .pwrdm_read_next_pwrst  = omap2xxx_pwrdm_read_next_pwrst,
        .pwrdm_read_pwrst       = omap2xxx_pwrdm_read_pwrst,
+       .pwrdm_read_prev_pwrst  = omap2xxx_pwrdm_read_prev_pwrst,
        .pwrdm_set_logic_retst  = omap2_pwrdm_set_logic_retst,
        .pwrdm_set_mem_onst     = omap2_pwrdm_set_mem_onst,
        .pwrdm_set_mem_retst    = omap2_pwrdm_set_mem_retst,
index a3e121f94a864e63108ba1c54522aeb904395918..947f6adfed0c91be9df6d517ee41c2d40a477d21 100644 (file)
@@ -210,6 +210,7 @@ int omap2_clkdm_read_wkdep(struct clockdomain *clkdm1,
                                             PM_WKDEP, (1 << clkdm2->dep_bit));
 }
 
+/* XXX Caller must hold the clkdm's powerdomain lock */
 int omap2_clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
 {
        struct clkdm_dep *cd;
@@ -221,7 +222,7 @@ int omap2_clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
 
                /* PRM accesses are slow, so minimize them */
                mask |= 1 << cd->clkdm->dep_bit;
-               atomic_set(&cd->wkdep_usecount, 0);
+               cd->wkdep_usecount = 0;
        }
 
        omap2_prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs,
index 1ac73883f8913c5894aff5f5267222c9f35343ee..44c0d7216aa72e1f5f578e0c7e4d8da1ada8871a 100644 (file)
@@ -110,11 +110,11 @@ int am33xx_prm_assert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs)
  * -EINVAL upon an argument error, -EEXIST if the submodule was already out
  * of reset, or -EBUSY if the submodule did not exit reset promptly.
  */
-int am33xx_prm_deassert_hardreset(u8 shift, s16 inst,
+int am33xx_prm_deassert_hardreset(u8 shift, u8 st_shift, s16 inst,
                u16 rstctrl_offs, u16 rstst_offs)
 {
        int c;
-       u32 mask = 1 << shift;
+       u32 mask = 1 << st_shift;
 
        /* Check the current status to avoid  de-asserting the line twice */
        if (am33xx_prm_is_hardreset_asserted(shift, inst, rstctrl_offs) == 0)
@@ -122,11 +122,14 @@ int am33xx_prm_deassert_hardreset(u8 shift, s16 inst,
 
        /* Clear the reset status by writing 1 to the status bit */
        am33xx_prm_rmw_reg_bits(0xffffffff, mask, inst, rstst_offs);
+
        /* de-assert the reset control line */
+       mask = 1 << shift;
+
        am33xx_prm_rmw_reg_bits(mask, 0, inst, rstctrl_offs);
-       /* wait the status to be set */
 
-       omap_test_timeout(am33xx_prm_is_hardreset_asserted(shift, inst,
+       /* wait the status to be set */
+       omap_test_timeout(am33xx_prm_is_hardreset_asserted(st_shift, inst,
                                                           rstst_offs),
                          MAX_MODULE_HARDRESET_WAIT, c);
 
index 3f25c563a82185bf07c423787a0df41b71ca9798..9b9918dfb119967e91c3c1ee791bb48e63219b41 100644 (file)
 #define AM33XX_PM_CEFUSE_PWRSTST_OFFSET                0x0004
 #define AM33XX_PM_CEFUSE_PWRSTST               AM33XX_PRM_REGADDR(AM33XX_PRM_CEFUSE_MOD, 0x0004)
 
+#ifndef __ASSEMBLER__
 extern u32 am33xx_prm_read_reg(s16 inst, u16 idx);
 extern void am33xx_prm_write_reg(u32 val, s16 inst, u16 idx);
 extern u32 am33xx_prm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);
@@ -124,6 +125,7 @@ extern void am33xx_prm_global_warm_sw_reset(void);
 extern int am33xx_prm_is_hardreset_asserted(u8 shift, s16 inst,
                u16 rstctrl_offs);
 extern int am33xx_prm_assert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs);
-extern int am33xx_prm_deassert_hardreset(u8 shift, s16 inst,
+extern int am33xx_prm_deassert_hardreset(u8 shift, u8 st_shift, s16 inst,
                u16 rstctrl_offs, u16 rstst_offs);
+#endif /* ASSEMBLER */
 #endif
index c05a343d465dcc1374a7c3a19142ca2e75d77517..cb24b26f36e9d256090e82541a4e8ba3c67123a1 100644 (file)
@@ -81,13 +81,13 @@ static struct prm_reset_src_map omap44xx_prm_reset_src_map[] = {
 /* Read a register in a CM/PRM instance in the PRM module */
 u32 omap4_prm_read_inst_reg(s16 inst, u16 reg)
 {
-       return __raw_readl(OMAP44XX_PRM_REGADDR(inst, reg));
+       return __raw_readl(prm_base + inst + reg);
 }
 
 /* Write into a register in a CM/PRM instance in the PRM module */
 void omap4_prm_write_inst_reg(u32 val, s16 inst, u16 reg)
 {
-       __raw_writel(val, OMAP44XX_PRM_REGADDR(inst, reg));
+       __raw_writel(val, prm_base + inst + reg);
 }
 
 /* Read-modify-write a register in a PRM module. Caller must lock */
@@ -650,7 +650,7 @@ static struct prm_ll_data omap44xx_prm_ll_data = {
 
 int __init omap44xx_prm_init(void)
 {
-       if (!cpu_is_omap44xx())
+       if (!cpu_is_omap44xx() && !soc_is_omap54xx())
                return 0;
 
        return prm_register(&omap44xx_prm_ll_data);
diff --git a/arch/arm/mach-omap2/prm54xx.h b/arch/arm/mach-omap2/prm54xx.h
new file mode 100644 (file)
index 0000000..4b1614a
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * OMAP54xx PRM instance offset macros
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Paul Walmsley (paul@pwsan.com)
+ * Rajendra Nayak (rnayak@ti.com)
+ * Benoit Cousson (b-cousson@ti.com)
+ *
+ * This file is automatically generated from the OMAP hardware databases.
+ * We respectfully ask that any modifications to this file be coordinated
+ * with the public linux-omap@vger.kernel.org mailing list and the
+ * authors above to ensure that the autogeneration scripts are kept
+ * up-to-date with the file contents.
+ *
+ * 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 __ARCH_ARM_MACH_OMAP2_PRM54XX_H
+#define __ARCH_ARM_MACH_OMAP2_PRM54XX_H
+
+#include "prcm-common.h"
+#include "prm.h"
+
+#define OMAP54XX_PRM_BASE              0x4ae06000
+
+#define OMAP54XX_PRM_REGADDR(inst, reg)                                \
+       OMAP2_L4_IO_ADDRESS(OMAP54XX_PRM_BASE + (inst) + (reg))
+
+
+/* PRM instances */
+#define OMAP54XX_PRM_OCP_SOCKET_INST   0x0000
+#define OMAP54XX_PRM_CKGEN_INST                0x0100
+#define OMAP54XX_PRM_MPU_INST          0x0300
+#define OMAP54XX_PRM_DSP_INST          0x0400
+#define OMAP54XX_PRM_ABE_INST          0x0500
+#define OMAP54XX_PRM_COREAON_INST      0x0600
+#define OMAP54XX_PRM_CORE_INST         0x0700
+#define OMAP54XX_PRM_IVA_INST          0x1200
+#define OMAP54XX_PRM_CAM_INST          0x1300
+#define OMAP54XX_PRM_DSS_INST          0x1400
+#define OMAP54XX_PRM_GPU_INST          0x1500
+#define OMAP54XX_PRM_L3INIT_INST       0x1600
+#define OMAP54XX_PRM_CUSTEFUSE_INST    0x1700
+#define OMAP54XX_PRM_WKUPAON_INST      0x1800
+#define OMAP54XX_PRM_WKUPAON_CM_INST   0x1900
+#define OMAP54XX_PRM_EMU_INST          0x1a00
+#define OMAP54XX_PRM_EMU_CM_INST       0x1b00
+#define OMAP54XX_PRM_DEVICE_INST       0x1c00
+#define OMAP54XX_PRM_INSTR_INST                0x1f00
+
+/* PRM clockdomain register offsets (from instance start) */
+#define OMAP54XX_PRM_WKUPAON_CM_WKUPAON_CDOFFS 0x0000
+#define OMAP54XX_PRM_EMU_CM_EMU_CDOFFS         0x0000
+
+/* PRM */
+
+/* PRM.OCP_SOCKET_PRM register offsets */
+#define OMAP54XX_REVISION_PRM_OFFSET                           0x0000
+#define OMAP54XX_PRM_IRQSTATUS_MPU_OFFSET                      0x0010
+#define OMAP54XX_PRM_IRQSTATUS_MPU_2_OFFSET                    0x0014
+#define OMAP54XX_PRM_IRQENABLE_MPU_OFFSET                      0x0018
+#define OMAP54XX_PRM_IRQENABLE_MPU_2_OFFSET                    0x001c
+#define OMAP54XX_PRM_IRQSTATUS_IPU_OFFSET                      0x0020
+#define OMAP54XX_PRM_IRQENABLE_IPU_OFFSET                      0x0028
+#define OMAP54XX_PRM_IRQSTATUS_DSP_OFFSET                      0x0030
+#define OMAP54XX_PRM_IRQENABLE_DSP_OFFSET                      0x0038
+#define OMAP54XX_CM_PRM_PROFILING_CLKCTRL_OFFSET               0x0040
+#define OMAP54XX_CM_PRM_PROFILING_CLKCTRL                      OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_OCP_SOCKET_INST, 0x0040)
+#define OMAP54XX_PRM_DEBUG_OUT_OFFSET                          0x0084
+#define OMAP54XX_PRM_DEBUG_TRANS_CFG_OFFSET                    0x0090
+#define OMAP54XX_PRM_DEBUG_OFF_TRANS_OFFSET                    0x0094
+#define OMAP54XX_PRM_DEBUG_CORE_RET_TRANS_OFFSET               0x0098
+#define OMAP54XX_PRM_DEBUG_MPU_RET_TRANS_OFFSET                        0x009c
+#define OMAP54XX_PRM_DEBUG_MM_RET_TRANS_OFFSET                 0x00a0
+#define OMAP54XX_PRM_DEBUG_WKUPAON_FD_TRANS_OFFSET             0x00a4
+
+/* PRM.CKGEN_PRM register offsets */
+#define OMAP54XX_CM_CLKSEL_ABE_DSS_SYS_OFFSET                  0x0000
+#define OMAP54XX_CM_CLKSEL_ABE_DSS_SYS                         OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_CKGEN_INST, 0x0000)
+#define OMAP54XX_CM_CLKSEL_WKUPAON_OFFSET                      0x0008
+#define OMAP54XX_CM_CLKSEL_WKUPAON                             OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_CKGEN_INST, 0x0008)
+#define OMAP54XX_CM_CLKSEL_ABE_PLL_REF_OFFSET                  0x000c
+#define OMAP54XX_CM_CLKSEL_ABE_PLL_REF                         OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_CKGEN_INST, 0x000c)
+#define OMAP54XX_CM_CLKSEL_SYS_OFFSET                          0x0010
+#define OMAP54XX_CM_CLKSEL_SYS                                 OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_CKGEN_INST, 0x0010)
+
+/* PRM.MPU_PRM register offsets */
+#define OMAP54XX_PM_MPU_PWRSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_PM_MPU_PWRSTST_OFFSET                         0x0004
+#define OMAP54XX_RM_MPU_MPU_CONTEXT_OFFSET                     0x0024
+
+/* PRM.DSP_PRM register offsets */
+#define OMAP54XX_PM_DSP_PWRSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_PM_DSP_PWRSTST_OFFSET                         0x0004
+#define OMAP54XX_RM_DSP_RSTCTRL_OFFSET                         0x0010
+#define OMAP54XX_RM_DSP_RSTST_OFFSET                           0x0014
+#define OMAP54XX_RM_DSP_DSP_CONTEXT_OFFSET                     0x0024
+
+/* PRM.ABE_PRM register offsets */
+#define OMAP54XX_PM_ABE_PWRSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_PM_ABE_PWRSTST_OFFSET                         0x0004
+#define OMAP54XX_RM_ABE_AESS_CONTEXT_OFFSET                    0x002c
+#define OMAP54XX_PM_ABE_MCPDM_WKDEP_OFFSET                     0x0030
+#define OMAP54XX_RM_ABE_MCPDM_CONTEXT_OFFSET                   0x0034
+#define OMAP54XX_PM_ABE_DMIC_WKDEP_OFFSET                      0x0038
+#define OMAP54XX_RM_ABE_DMIC_CONTEXT_OFFSET                    0x003c
+#define OMAP54XX_PM_ABE_MCASP_WKDEP_OFFSET                     0x0040
+#define OMAP54XX_RM_ABE_MCASP_CONTEXT_OFFSET                   0x0044
+#define OMAP54XX_PM_ABE_MCBSP1_WKDEP_OFFSET                    0x0048
+#define OMAP54XX_RM_ABE_MCBSP1_CONTEXT_OFFSET                  0x004c
+#define OMAP54XX_PM_ABE_MCBSP2_WKDEP_OFFSET                    0x0050
+#define OMAP54XX_RM_ABE_MCBSP2_CONTEXT_OFFSET                  0x0054
+#define OMAP54XX_PM_ABE_MCBSP3_WKDEP_OFFSET                    0x0058
+#define OMAP54XX_RM_ABE_MCBSP3_CONTEXT_OFFSET                  0x005c
+#define OMAP54XX_PM_ABE_SLIMBUS1_WKDEP_OFFSET                  0x0060
+#define OMAP54XX_RM_ABE_SLIMBUS1_CONTEXT_OFFSET                        0x0064
+#define OMAP54XX_PM_ABE_TIMER5_WKDEP_OFFSET                    0x0068
+#define OMAP54XX_RM_ABE_TIMER5_CONTEXT_OFFSET                  0x006c
+#define OMAP54XX_PM_ABE_TIMER6_WKDEP_OFFSET                    0x0070
+#define OMAP54XX_RM_ABE_TIMER6_CONTEXT_OFFSET                  0x0074
+#define OMAP54XX_PM_ABE_TIMER7_WKDEP_OFFSET                    0x0078
+#define OMAP54XX_RM_ABE_TIMER7_CONTEXT_OFFSET                  0x007c
+#define OMAP54XX_PM_ABE_TIMER8_WKDEP_OFFSET                    0x0080
+#define OMAP54XX_RM_ABE_TIMER8_CONTEXT_OFFSET                  0x0084
+#define OMAP54XX_PM_ABE_WD_TIMER3_WKDEP_OFFSET                 0x0088
+#define OMAP54XX_RM_ABE_WD_TIMER3_CONTEXT_OFFSET               0x008c
+
+/* PRM.COREAON_PRM register offsets */
+#define OMAP54XX_PM_COREAON_SMARTREFLEX_MPU_WKDEP_OFFSET       0x0028
+#define OMAP54XX_RM_COREAON_SMARTREFLEX_MPU_CONTEXT_OFFSET     0x002c
+#define OMAP54XX_PM_COREAON_SMARTREFLEX_MM_WKDEP_OFFSET                0x0030
+#define OMAP54XX_RM_COREAON_SMARTREFLEX_MM_CONTEXT_OFFSET      0x0034
+#define OMAP54XX_PM_COREAON_SMARTREFLEX_CORE_WKDEP_OFFSET      0x0038
+#define OMAP54XX_RM_COREAON_SMARTREFLEX_CORE_CONTEXT_OFFSET    0x003c
+
+/* PRM.CORE_PRM register offsets */
+#define OMAP54XX_PM_CORE_PWRSTCTRL_OFFSET                      0x0000
+#define OMAP54XX_PM_CORE_PWRSTST_OFFSET                                0x0004
+#define OMAP54XX_RM_L3MAIN1_L3_MAIN_1_CONTEXT_OFFSET           0x0024
+#define OMAP54XX_RM_L3MAIN2_L3_MAIN_2_CONTEXT_OFFSET           0x0124
+#define OMAP54XX_RM_L3MAIN2_GPMC_CONTEXT_OFFSET                        0x012c
+#define OMAP54XX_RM_L3MAIN2_OCMC_RAM_CONTEXT_OFFSET            0x0134
+#define OMAP54XX_RM_IPU_RSTCTRL_OFFSET                         0x0210
+#define OMAP54XX_RM_IPU_RSTST_OFFSET                           0x0214
+#define OMAP54XX_RM_IPU_IPU_CONTEXT_OFFSET                     0x0224
+#define OMAP54XX_RM_DMA_DMA_SYSTEM_CONTEXT_OFFSET              0x0324
+#define OMAP54XX_RM_EMIF_DMM_CONTEXT_OFFSET                    0x0424
+#define OMAP54XX_RM_EMIF_EMIF_OCP_FW_CONTEXT_OFFSET            0x042c
+#define OMAP54XX_RM_EMIF_EMIF1_CONTEXT_OFFSET                  0x0434
+#define OMAP54XX_RM_EMIF_EMIF2_CONTEXT_OFFSET                  0x043c
+#define OMAP54XX_RM_EMIF_EMIF_DLL_CONTEXT_OFFSET               0x0444
+#define OMAP54XX_RM_C2C_C2C_CONTEXT_OFFSET                     0x0524
+#define OMAP54XX_RM_C2C_MODEM_ICR_CONTEXT_OFFSET               0x052c
+#define OMAP54XX_RM_C2C_C2C_OCP_FW_CONTEXT_OFFSET              0x0534
+#define OMAP54XX_RM_L4CFG_L4_CFG_CONTEXT_OFFSET                        0x0624
+#define OMAP54XX_RM_L4CFG_SPINLOCK_CONTEXT_OFFSET              0x062c
+#define OMAP54XX_RM_L4CFG_MAILBOX_CONTEXT_OFFSET               0x0634
+#define OMAP54XX_RM_L4CFG_SAR_ROM_CONTEXT_OFFSET               0x063c
+#define OMAP54XX_RM_L4CFG_OCP2SCP2_CONTEXT_OFFSET              0x0644
+#define OMAP54XX_RM_L3INSTR_L3_MAIN_3_CONTEXT_OFFSET           0x0724
+#define OMAP54XX_RM_L3INSTR_L3_INSTR_CONTEXT_OFFSET            0x072c
+#define OMAP54XX_RM_L3INSTR_OCP_WP_NOC_CONTEXT_OFFSET          0x0744
+#define OMAP54XX_RM_MIPIEXT_LLI_CONTEXT_OFFSET                 0x0824
+#define OMAP54XX_RM_MIPIEXT_LLI_OCP_FW_CONTEXT_OFFSET          0x082c
+#define OMAP54XX_RM_MIPIEXT_MPHY_CONTEXT_OFFSET                        0x0834
+#define OMAP54XX_PM_L4PER_TIMER10_WKDEP_OFFSET                 0x0928
+#define OMAP54XX_RM_L4PER_TIMER10_CONTEXT_OFFSET               0x092c
+#define OMAP54XX_PM_L4PER_TIMER11_WKDEP_OFFSET                 0x0930
+#define OMAP54XX_RM_L4PER_TIMER11_CONTEXT_OFFSET               0x0934
+#define OMAP54XX_PM_L4PER_TIMER2_WKDEP_OFFSET                  0x0938
+#define OMAP54XX_RM_L4PER_TIMER2_CONTEXT_OFFSET                        0x093c
+#define OMAP54XX_PM_L4PER_TIMER3_WKDEP_OFFSET                  0x0940
+#define OMAP54XX_RM_L4PER_TIMER3_CONTEXT_OFFSET                        0x0944
+#define OMAP54XX_PM_L4PER_TIMER4_WKDEP_OFFSET                  0x0948
+#define OMAP54XX_RM_L4PER_TIMER4_CONTEXT_OFFSET                        0x094c
+#define OMAP54XX_PM_L4PER_TIMER9_WKDEP_OFFSET                  0x0950
+#define OMAP54XX_RM_L4PER_TIMER9_CONTEXT_OFFSET                        0x0954
+#define OMAP54XX_RM_L4PER_ELM_CONTEXT_OFFSET                   0x095c
+#define OMAP54XX_PM_L4PER_GPIO2_WKDEP_OFFSET                   0x0960
+#define OMAP54XX_RM_L4PER_GPIO2_CONTEXT_OFFSET                 0x0964
+#define OMAP54XX_PM_L4PER_GPIO3_WKDEP_OFFSET                   0x0968
+#define OMAP54XX_RM_L4PER_GPIO3_CONTEXT_OFFSET                 0x096c
+#define OMAP54XX_PM_L4PER_GPIO4_WKDEP_OFFSET                   0x0970
+#define OMAP54XX_RM_L4PER_GPIO4_CONTEXT_OFFSET                 0x0974
+#define OMAP54XX_PM_L4PER_GPIO5_WKDEP_OFFSET                   0x0978
+#define OMAP54XX_RM_L4PER_GPIO5_CONTEXT_OFFSET                 0x097c
+#define OMAP54XX_PM_L4PER_GPIO6_WKDEP_OFFSET                   0x0980
+#define OMAP54XX_RM_L4PER_GPIO6_CONTEXT_OFFSET                 0x0984
+#define OMAP54XX_RM_L4PER_HDQ1W_CONTEXT_OFFSET                 0x098c
+#define OMAP54XX_PM_L4PER_I2C1_WKDEP_OFFSET                    0x09a0
+#define OMAP54XX_RM_L4PER_I2C1_CONTEXT_OFFSET                  0x09a4
+#define OMAP54XX_PM_L4PER_I2C2_WKDEP_OFFSET                    0x09a8
+#define OMAP54XX_RM_L4PER_I2C2_CONTEXT_OFFSET                  0x09ac
+#define OMAP54XX_PM_L4PER_I2C3_WKDEP_OFFSET                    0x09b0
+#define OMAP54XX_RM_L4PER_I2C3_CONTEXT_OFFSET                  0x09b4
+#define OMAP54XX_PM_L4PER_I2C4_WKDEP_OFFSET                    0x09b8
+#define OMAP54XX_RM_L4PER_I2C4_CONTEXT_OFFSET                  0x09bc
+#define OMAP54XX_RM_L4PER_L4_PER_CONTEXT_OFFSET                        0x09c0
+#define OMAP54XX_PM_L4PER_MCSPI1_WKDEP_OFFSET                  0x09f0
+#define OMAP54XX_RM_L4PER_MCSPI1_CONTEXT_OFFSET                        0x09f4
+#define OMAP54XX_PM_L4PER_MCSPI2_WKDEP_OFFSET                  0x09f8
+#define OMAP54XX_RM_L4PER_MCSPI2_CONTEXT_OFFSET                        0x09fc
+#define OMAP54XX_PM_L4PER_MCSPI3_WKDEP_OFFSET                  0x0a00
+#define OMAP54XX_RM_L4PER_MCSPI3_CONTEXT_OFFSET                        0x0a04
+#define OMAP54XX_PM_L4PER_MCSPI4_WKDEP_OFFSET                  0x0a08
+#define OMAP54XX_RM_L4PER_MCSPI4_CONTEXT_OFFSET                        0x0a0c
+#define OMAP54XX_PM_L4PER_GPIO7_WKDEP_OFFSET                   0x0a10
+#define OMAP54XX_RM_L4PER_GPIO7_CONTEXT_OFFSET                 0x0a14
+#define OMAP54XX_PM_L4PER_GPIO8_WKDEP_OFFSET                   0x0a18
+#define OMAP54XX_RM_L4PER_GPIO8_CONTEXT_OFFSET                 0x0a1c
+#define OMAP54XX_PM_L4PER_MMC3_WKDEP_OFFSET                    0x0a20
+#define OMAP54XX_RM_L4PER_MMC3_CONTEXT_OFFSET                  0x0a24
+#define OMAP54XX_PM_L4PER_MMC4_WKDEP_OFFSET                    0x0a28
+#define OMAP54XX_RM_L4PER_MMC4_CONTEXT_OFFSET                  0x0a2c
+#define OMAP54XX_PM_L4PER_UART1_WKDEP_OFFSET                   0x0a40
+#define OMAP54XX_RM_L4PER_UART1_CONTEXT_OFFSET                 0x0a44
+#define OMAP54XX_PM_L4PER_UART2_WKDEP_OFFSET                   0x0a48
+#define OMAP54XX_RM_L4PER_UART2_CONTEXT_OFFSET                 0x0a4c
+#define OMAP54XX_PM_L4PER_UART3_WKDEP_OFFSET                   0x0a50
+#define OMAP54XX_RM_L4PER_UART3_CONTEXT_OFFSET                 0x0a54
+#define OMAP54XX_RM_L4PER_UART4_CONTEXT_OFFSET                 0x0a58
+#define OMAP54XX_PM_L4PER_UART4_WKDEP_OFFSET                   0x0a5c
+#define OMAP54XX_PM_L4PER_MMC5_WKDEP_OFFSET                    0x0a60
+#define OMAP54XX_RM_L4PER_MMC5_CONTEXT_OFFSET                  0x0a64
+#define OMAP54XX_PM_L4PER_I2C5_WKDEP_OFFSET                    0x0a68
+#define OMAP54XX_RM_L4PER_I2C5_CONTEXT_OFFSET                  0x0a6c
+#define OMAP54XX_PM_L4PER_UART5_WKDEP_OFFSET                   0x0a70
+#define OMAP54XX_RM_L4PER_UART5_CONTEXT_OFFSET                 0x0a74
+#define OMAP54XX_PM_L4PER_UART6_WKDEP_OFFSET                   0x0a78
+#define OMAP54XX_RM_L4PER_UART6_CONTEXT_OFFSET                 0x0a7c
+#define OMAP54XX_RM_L4SEC_AES1_CONTEXT_OFFSET                  0x0aa4
+#define OMAP54XX_RM_L4SEC_AES2_CONTEXT_OFFSET                  0x0aac
+#define OMAP54XX_RM_L4SEC_DES3DES_CONTEXT_OFFSET               0x0ab4
+#define OMAP54XX_RM_L4SEC_FPKA_CONTEXT_OFFSET                  0x0abc
+#define OMAP54XX_RM_L4SEC_RNG_CONTEXT_OFFSET                   0x0ac4
+#define OMAP54XX_RM_L4SEC_SHA2MD5_CONTEXT_OFFSET               0x0acc
+#define OMAP54XX_RM_L4SEC_DMA_CRYPTO_CONTEXT_OFFSET            0x0adc
+
+/* PRM.IVA_PRM register offsets */
+#define OMAP54XX_PM_IVA_PWRSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_PM_IVA_PWRSTST_OFFSET                         0x0004
+#define OMAP54XX_RM_IVA_RSTCTRL_OFFSET                         0x0010
+#define OMAP54XX_RM_IVA_RSTST_OFFSET                           0x0014
+#define OMAP54XX_RM_IVA_IVA_CONTEXT_OFFSET                     0x0024
+#define OMAP54XX_RM_IVA_SL2_CONTEXT_OFFSET                     0x002c
+
+/* PRM.CAM_PRM register offsets */
+#define OMAP54XX_PM_CAM_PWRSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_PM_CAM_PWRSTST_OFFSET                         0x0004
+#define OMAP54XX_RM_CAM_ISS_CONTEXT_OFFSET                     0x0024
+#define OMAP54XX_RM_CAM_FDIF_CONTEXT_OFFSET                    0x002c
+#define OMAP54XX_RM_CAM_CAL_CONTEXT_OFFSET                     0x0034
+
+/* PRM.DSS_PRM register offsets */
+#define OMAP54XX_PM_DSS_PWRSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_PM_DSS_PWRSTST_OFFSET                         0x0004
+#define OMAP54XX_PM_DSS_DSS_WKDEP_OFFSET                       0x0020
+#define OMAP54XX_RM_DSS_DSS_CONTEXT_OFFSET                     0x0024
+#define OMAP54XX_RM_DSS_BB2D_CONTEXT_OFFSET                    0x0034
+
+/* PRM.GPU_PRM register offsets */
+#define OMAP54XX_PM_GPU_PWRSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_PM_GPU_PWRSTST_OFFSET                         0x0004
+#define OMAP54XX_RM_GPU_GPU_CONTEXT_OFFSET                     0x0024
+
+/* PRM.L3INIT_PRM register offsets */
+#define OMAP54XX_PM_L3INIT_PWRSTCTRL_OFFSET                    0x0000
+#define OMAP54XX_PM_L3INIT_PWRSTST_OFFSET                      0x0004
+#define OMAP54XX_PM_L3INIT_MMC1_WKDEP_OFFSET                   0x0028
+#define OMAP54XX_RM_L3INIT_MMC1_CONTEXT_OFFSET                 0x002c
+#define OMAP54XX_PM_L3INIT_MMC2_WKDEP_OFFSET                   0x0030
+#define OMAP54XX_RM_L3INIT_MMC2_CONTEXT_OFFSET                 0x0034
+#define OMAP54XX_PM_L3INIT_HSI_WKDEP_OFFSET                    0x0038
+#define OMAP54XX_RM_L3INIT_HSI_CONTEXT_OFFSET                  0x003c
+#define OMAP54XX_PM_L3INIT_UNIPRO2_WKDEP_OFFSET                        0x0040
+#define OMAP54XX_RM_L3INIT_UNIPRO2_CONTEXT_OFFSET              0x0044
+#define OMAP54XX_PM_L3INIT_USB_HOST_HS_WKDEP_OFFSET            0x0058
+#define OMAP54XX_RM_L3INIT_USB_HOST_HS_CONTEXT_OFFSET          0x005c
+#define OMAP54XX_PM_L3INIT_USB_TLL_HS_WKDEP_OFFSET             0x0068
+#define OMAP54XX_RM_L3INIT_USB_TLL_HS_CONTEXT_OFFSET           0x006c
+#define OMAP54XX_RM_L3INIT_IEEE1500_2_OCP_CONTEXT_OFFSET       0x007c
+#define OMAP54XX_PM_L3INIT_SATA_WKDEP_OFFSET                   0x0088
+#define OMAP54XX_RM_L3INIT_SATA_CONTEXT_OFFSET                 0x008c
+#define OMAP54XX_RM_L3INIT_OCP2SCP1_CONTEXT_OFFSET             0x00e4
+#define OMAP54XX_RM_L3INIT_OCP2SCP3_CONTEXT_OFFSET             0x00ec
+#define OMAP54XX_PM_L3INIT_USB_OTG_SS_WKDEP_OFFSET             0x00f0
+#define OMAP54XX_RM_L3INIT_USB_OTG_SS_CONTEXT_OFFSET           0x00f4
+
+/* PRM.CUSTEFUSE_PRM register offsets */
+#define OMAP54XX_PM_CUSTEFUSE_PWRSTCTRL_OFFSET                 0x0000
+#define OMAP54XX_PM_CUSTEFUSE_PWRSTST_OFFSET                   0x0004
+#define OMAP54XX_RM_CUSTEFUSE_EFUSE_CTRL_CUST_CONTEXT_OFFSET   0x0024
+
+/* PRM.WKUPAON_PRM register offsets */
+#define OMAP54XX_RM_WKUPAON_L4_WKUP_CONTEXT_OFFSET             0x0024
+#define OMAP54XX_RM_WKUPAON_WD_TIMER1_CONTEXT_OFFSET           0x002c
+#define OMAP54XX_PM_WKUPAON_WD_TIMER2_WKDEP_OFFSET             0x0030
+#define OMAP54XX_RM_WKUPAON_WD_TIMER2_CONTEXT_OFFSET           0x0034
+#define OMAP54XX_PM_WKUPAON_GPIO1_WKDEP_OFFSET                 0x0038
+#define OMAP54XX_RM_WKUPAON_GPIO1_CONTEXT_OFFSET               0x003c
+#define OMAP54XX_PM_WKUPAON_TIMER1_WKDEP_OFFSET                        0x0040
+#define OMAP54XX_RM_WKUPAON_TIMER1_CONTEXT_OFFSET              0x0044
+#define OMAP54XX_PM_WKUPAON_TIMER12_WKDEP_OFFSET               0x0048
+#define OMAP54XX_RM_WKUPAON_TIMER12_CONTEXT_OFFSET             0x004c
+#define OMAP54XX_RM_WKUPAON_COUNTER_32K_CONTEXT_OFFSET         0x0054
+#define OMAP54XX_RM_WKUPAON_SAR_RAM_CONTEXT_OFFSET             0x0064
+#define OMAP54XX_PM_WKUPAON_KBD_WKDEP_OFFSET                   0x0078
+#define OMAP54XX_RM_WKUPAON_KBD_CONTEXT_OFFSET                 0x007c
+
+/* PRM.WKUPAON_CM register offsets */
+#define OMAP54XX_CM_WKUPAON_CLKSTCTRL_OFFSET                   0x0000
+#define OMAP54XX_CM_WKUPAON_L4_WKUP_CLKCTRL_OFFSET             0x0020
+#define OMAP54XX_CM_WKUPAON_L4_WKUP_CLKCTRL                    OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_WKUPAON_CM_INST, 0x0020)
+#define OMAP54XX_CM_WKUPAON_WD_TIMER1_CLKCTRL_OFFSET           0x0028
+#define OMAP54XX_CM_WKUPAON_WD_TIMER1_CLKCTRL                  OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_WKUPAON_CM_INST, 0x0028)
+#define OMAP54XX_CM_WKUPAON_WD_TIMER2_CLKCTRL_OFFSET           0x0030
+#define OMAP54XX_CM_WKUPAON_WD_TIMER2_CLKCTRL                  OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_WKUPAON_CM_INST, 0x0030)
+#define OMAP54XX_CM_WKUPAON_GPIO1_CLKCTRL_OFFSET               0x0038
+#define OMAP54XX_CM_WKUPAON_GPIO1_CLKCTRL                      OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_WKUPAON_CM_INST, 0x0038)
+#define OMAP54XX_CM_WKUPAON_TIMER1_CLKCTRL_OFFSET              0x0040
+#define OMAP54XX_CM_WKUPAON_TIMER1_CLKCTRL                     OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_WKUPAON_CM_INST, 0x0040)
+#define OMAP54XX_CM_WKUPAON_TIMER12_CLKCTRL_OFFSET             0x0048
+#define OMAP54XX_CM_WKUPAON_TIMER12_CLKCTRL                    OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_WKUPAON_CM_INST, 0x0048)
+#define OMAP54XX_CM_WKUPAON_COUNTER_32K_CLKCTRL_OFFSET         0x0050
+#define OMAP54XX_CM_WKUPAON_COUNTER_32K_CLKCTRL                        OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_WKUPAON_CM_INST, 0x0050)
+#define OMAP54XX_CM_WKUPAON_SAR_RAM_CLKCTRL_OFFSET             0x0060
+#define OMAP54XX_CM_WKUPAON_SAR_RAM_CLKCTRL                    OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_WKUPAON_CM_INST, 0x0060)
+#define OMAP54XX_CM_WKUPAON_KBD_CLKCTRL_OFFSET                 0x0078
+#define OMAP54XX_CM_WKUPAON_KBD_CLKCTRL                                OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_WKUPAON_CM_INST, 0x0078)
+#define OMAP54XX_CM_WKUPAON_SCRM_CLKCTRL_OFFSET                        0x0090
+#define OMAP54XX_CM_WKUPAON_SCRM_CLKCTRL                       OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_WKUPAON_CM_INST, 0x0090)
+#define OMAP54XX_CM_WKUPAON_IO_SRCOMP_CLKCTRL_OFFSET           0x0098
+#define OMAP54XX_CM_WKUPAON_IO_SRCOMP_CLKCTRL                  OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_WKUPAON_CM_INST, 0x0098)
+
+/* PRM.EMU_PRM register offsets */
+#define OMAP54XX_PM_EMU_PWRSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_PM_EMU_PWRSTST_OFFSET                         0x0004
+#define OMAP54XX_RM_EMU_DEBUGSS_CONTEXT_OFFSET                 0x0024
+
+/* PRM.EMU_CM register offsets */
+#define OMAP54XX_CM_EMU_CLKSTCTRL_OFFSET                       0x0000
+#define OMAP54XX_CM_EMU_DYNAMICDEP_OFFSET                      0x0008
+#define OMAP54XX_CM_EMU_DEBUGSS_CLKCTRL_OFFSET                 0x0020
+#define OMAP54XX_CM_EMU_DEBUGSS_CLKCTRL                                OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_EMU_CM_INST, 0x0020)
+#define OMAP54XX_CM_EMU_MPU_EMU_DBG_CLKCTRL_OFFSET             0x0028
+#define OMAP54XX_CM_EMU_MPU_EMU_DBG_CLKCTRL                    OMAP54XX_PRM_REGADDR(OMAP54XX_PRM_EMU_CM_INST, 0x0028)
+
+/* PRM.DEVICE_PRM register offsets */
+#define OMAP54XX_PRM_RSTCTRL_OFFSET                            0x0000
+#define OMAP54XX_PRM_RSTST_OFFSET                              0x0004
+#define OMAP54XX_PRM_RSTTIME_OFFSET                            0x0008
+#define OMAP54XX_PRM_CLKREQCTRL_OFFSET                         0x000c
+#define OMAP54XX_PRM_VOLTCTRL_OFFSET                           0x0010
+#define OMAP54XX_PRM_PWRREQCTRL_OFFSET                         0x0014
+#define OMAP54XX_PRM_PSCON_COUNT_OFFSET                                0x0018
+#define OMAP54XX_PRM_IO_COUNT_OFFSET                           0x001c
+#define OMAP54XX_PRM_IO_PMCTRL_OFFSET                          0x0020
+#define OMAP54XX_PRM_VOLTSETUP_WARMRESET_OFFSET                        0x0024
+#define OMAP54XX_PRM_VOLTSETUP_CORE_OFF_OFFSET                 0x0028
+#define OMAP54XX_PRM_VOLTSETUP_MPU_OFF_OFFSET                  0x002c
+#define OMAP54XX_PRM_VOLTSETUP_MM_OFF_OFFSET                   0x0030
+#define OMAP54XX_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET           0x0034
+#define OMAP54XX_PRM_VOLTSETUP_MPU_RET_SLEEP_OFFSET            0x0038
+#define OMAP54XX_PRM_VOLTSETUP_MM_RET_SLEEP_OFFSET             0x003c
+#define OMAP54XX_PRM_VP_CORE_CONFIG_OFFSET                     0x0040
+#define OMAP54XX_PRM_VP_CORE_STATUS_OFFSET                     0x0044
+#define OMAP54XX_PRM_VP_CORE_VLIMITTO_OFFSET                   0x0048
+#define OMAP54XX_PRM_VP_CORE_VOLTAGE_OFFSET                    0x004c
+#define OMAP54XX_PRM_VP_CORE_VSTEPMAX_OFFSET                   0x0050
+#define OMAP54XX_PRM_VP_CORE_VSTEPMIN_OFFSET                   0x0054
+#define OMAP54XX_PRM_VP_MPU_CONFIG_OFFSET                      0x0058
+#define OMAP54XX_PRM_VP_MPU_STATUS_OFFSET                      0x005c
+#define OMAP54XX_PRM_VP_MPU_VLIMITTO_OFFSET                    0x0060
+#define OMAP54XX_PRM_VP_MPU_VOLTAGE_OFFSET                     0x0064
+#define OMAP54XX_PRM_VP_MPU_VSTEPMAX_OFFSET                    0x0068
+#define OMAP54XX_PRM_VP_MPU_VSTEPMIN_OFFSET                    0x006c
+#define OMAP54XX_PRM_VP_MM_CONFIG_OFFSET                       0x0070
+#define OMAP54XX_PRM_VP_MM_STATUS_OFFSET                       0x0074
+#define OMAP54XX_PRM_VP_MM_VLIMITTO_OFFSET                     0x0078
+#define OMAP54XX_PRM_VP_MM_VOLTAGE_OFFSET                      0x007c
+#define OMAP54XX_PRM_VP_MM_VSTEPMAX_OFFSET                     0x0080
+#define OMAP54XX_PRM_VP_MM_VSTEPMIN_OFFSET                     0x0084
+#define OMAP54XX_PRM_VC_SMPS_CORE_CONFIG_OFFSET                        0x0088
+#define OMAP54XX_PRM_VC_SMPS_MM_CONFIG_OFFSET                  0x008c
+#define OMAP54XX_PRM_VC_SMPS_MPU_CONFIG_OFFSET                 0x0090
+#define OMAP54XX_PRM_VC_VAL_CMD_VDD_CORE_L_OFFSET              0x0094
+#define OMAP54XX_PRM_VC_VAL_CMD_VDD_MM_L_OFFSET                        0x0098
+#define OMAP54XX_PRM_VC_VAL_CMD_VDD_MPU_L_OFFSET               0x009c
+#define OMAP54XX_PRM_VC_VAL_BYPASS_OFFSET                      0x00a0
+#define OMAP54XX_PRM_VC_CORE_ERRST_OFFSET                      0x00a4
+#define OMAP54XX_PRM_VC_MM_ERRST_OFFSET                                0x00a8
+#define OMAP54XX_PRM_VC_MPU_ERRST_OFFSET                       0x00ac
+#define OMAP54XX_PRM_VC_BYPASS_ERRST_OFFSET                    0x00b0
+#define OMAP54XX_PRM_VC_CFG_I2C_MODE_OFFSET                    0x00b4
+#define OMAP54XX_PRM_VC_CFG_I2C_CLK_OFFSET                     0x00b8
+#define OMAP54XX_PRM_SRAM_COUNT_OFFSET                         0x00bc
+#define OMAP54XX_PRM_SRAM_WKUP_SETUP_OFFSET                    0x00c0
+#define OMAP54XX_PRM_SLDO_CORE_SETUP_OFFSET                    0x00c4
+#define OMAP54XX_PRM_SLDO_CORE_CTRL_OFFSET                     0x00c8
+#define OMAP54XX_PRM_SLDO_MPU_SETUP_OFFSET                     0x00cc
+#define OMAP54XX_PRM_SLDO_MPU_CTRL_OFFSET                      0x00d0
+#define OMAP54XX_PRM_SLDO_MM_SETUP_OFFSET                      0x00d4
+#define OMAP54XX_PRM_SLDO_MM_CTRL_OFFSET                       0x00d8
+#define OMAP54XX_PRM_ABBLDO_MPU_SETUP_OFFSET                   0x00dc
+#define OMAP54XX_PRM_ABBLDO_MPU_CTRL_OFFSET                    0x00e0
+#define OMAP54XX_PRM_ABBLDO_MM_SETUP_OFFSET                    0x00e4
+#define OMAP54XX_PRM_ABBLDO_MM_CTRL_OFFSET                     0x00e8
+#define OMAP54XX_PRM_BANDGAP_SETUP_OFFSET                      0x00ec
+#define OMAP54XX_PRM_DEVICE_OFF_CTRL_OFFSET                    0x00f0
+#define OMAP54XX_PRM_PHASE1_CNDP_OFFSET                                0x00f4
+#define OMAP54XX_PRM_PHASE2A_CNDP_OFFSET                       0x00f8
+#define OMAP54XX_PRM_PHASE2B_CNDP_OFFSET                       0x00fc
+#define OMAP54XX_PRM_MODEM_IF_CTRL_OFFSET                      0x0100
+#define OMAP54XX_PRM_VOLTST_MPU_OFFSET                         0x0110
+#define OMAP54XX_PRM_VOLTST_MM_OFFSET                          0x0114
+
+/* Function prototypes */
+# ifndef __ASSEMBLER__
+
+extern u32 omap4_prm_read_inst_reg(s16 inst, u16 idx);
+extern void omap4_prm_write_inst_reg(u32 val, s16 inst, u16 idx);
+extern u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);
+
+/* OMAP4-specific VP functions */
+u32 omap4_prm_vp_check_txdone(u8 vp_id);
+void omap4_prm_vp_clear_txdone(u8 vp_id);
+
+/*
+ * OMAP4 access functions for voltage controller (VC) and
+ * voltage proccessor (VP) in the PRM.
+ */
+extern u32 omap4_prm_vcvp_read(u8 offset);
+extern void omap4_prm_vcvp_write(u32 val, u8 offset);
+extern u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset);
+
+/* PRM interrupt-related functions */
+extern void omap44xx_prm_read_pending_irqs(unsigned long *events);
+extern void omap44xx_prm_ocp_barrier(void);
+extern void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask);
+extern void omap44xx_prm_restore_irqen(u32 *saved_mask);
+
+# endif
+
+#endif
index c12320c0ae952e46d05a40d50d6d260994ff98d7..430fb1db14c255e1af193333c4bd0116399decc9 100644 (file)
 #include "common.h"
 #include "prcm-common.h"
 #include "prm44xx.h"
+#include "prm54xx.h"
 #include "prminst44xx.h"
 #include "prm-regbits-44xx.h"
 #include "prcm44xx.h"
 #include "prcm_mpu44xx.h"
+#include "soc.h"
 
 static void __iomem *_prm_bases[OMAP4_MAX_PRCM_PARTITIONS];
 
@@ -165,17 +167,19 @@ int omap4_prminst_deassert_hardreset(u8 shift, u8 part, s16 inst,
 void omap4_prminst_global_warm_sw_reset(void)
 {
        u32 v;
+       s16 dev_inst = cpu_is_omap44xx() ? OMAP4430_PRM_DEVICE_INST :
+                                          OMAP54XX_PRM_DEVICE_INST;
 
        v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
-                                   OMAP4430_PRM_DEVICE_INST,
+                                   dev_inst,
                                    OMAP4_PRM_RSTCTRL_OFFSET);
        v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK;
        omap4_prminst_write_inst_reg(v, OMAP4430_PRM_PARTITION,
-                                OMAP4430_PRM_DEVICE_INST,
+                                dev_inst,
                                 OMAP4_PRM_RSTCTRL_OFFSET);
 
        /* OCP barrier */
        v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
-                                   OMAP4430_PRM_DEVICE_INST,
+                                   dev_inst,
                                    OMAP4_PRM_RSTCTRL_OFFSET);
 }
diff --git a/arch/arm/mach-omap2/sata.c b/arch/arm/mach-omap2/sata.c
new file mode 100644 (file)
index 0000000..63fdde3
--- /dev/null
@@ -0,0 +1,611 @@
+/**
+ * sata.c - The ahci sata device init functions
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Keshava Munegowda <keshava_mgowda@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/omap_ocp2scp.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/ahci_platform.h>
+#include <linux/clk.h>
+#include <plat/sata.h>
+
+#include "omap_device.h"
+#include "soc.h"
+
+#define OMAP_SATA_HWMODNAME    "sata"
+#define OMAP_OCP2SCP3_HWMODNAME        "ocp2scp3"
+#define AHCI_PLAT_DEVNAME      "ahci"
+
+#define OMAP_SATA_PLL_CONTROL                                  0x00
+#define OMAP_SATA_PLL_STATUS                                   0x04
+#define OMAP_SATA_PLL_STATUS_LOCK_SHIFT                                1
+#define OMAP_SATA_PLL_STATUS_LOCK                              1
+#define OMAP_SATA_PLL_STATUS_RESET_DONE                                1
+#define OMAP_SATA_PLL_STATUS_LDOPWDN_SHIFT                     15
+#define OMAP_SATA_PLL_STATUS_TICOPWDN_SHIFT                    16
+
+#define OMAP_SATA_PLL_GO                                       0x08
+
+#define OMAP_SATA_PLL_CFG1                                     0x0c
+#define OMAP_SATA_PLL_CFG1_M                                   625
+#define OMAP_SATA_PLL_CFG1_M_MASK                              0xfff
+#define OMAP_SATA_PLL_CFG1_M_SHIFT                             9
+
+#define OMAP_SATA_PLL_CFG1_N                                   7
+#define OMAP_SATA_PLL_CFG1_N_MASK                              0xff
+#define OMAP_SATA_PLL_CFG1_N_SHIFT                             1
+
+#define OMAP_SATA_PLL_CFG2                                     0x10
+#define OMAP_SATA_PLL_CFG2_SELFREQDCO                          4
+#define OMAP_SATA_PLL_CFG2_SELFREQDCO_MASK                     7
+#define OMAP_SATA_PLL_CFG2_SELFREQDCO_SHIFT                    1
+#define OMAP_SATA_PLL_CFG2_REFSEL                              0
+#define OMAP_SATA_PLL_CFG2_REFSEL_MASK                         3
+#define OMAP_SATA_PLL_CFG2_REFSEL_SHIFT                                21
+#define OMAP_SATA_PLL_CFG2_LOCKSEL                             1
+#define OMAP_SATA_PLL_CFG2_LOCKSEL_MASK                                3
+#define OMAP_SATA_PLL_CFG2_LOCKSEL_SHIFT                       9
+#define OMAP_SATA_PLL_CFG2_IDLE                                        1
+#define OMAP_SATA_PLL_CFG2_IDLE_MASK                           1
+
+#define OMAP_SATA_PLL_CFG3                                     0x14
+
+#define OMAP_SATA_PLL_CFG3_SD                                  6
+#define OMAP_SATA_PLL_CFG3_SD_MASK                             0xff
+#define OMAP_SATA_PLL_CFG3_SD_SHIF                             10
+
+#define OMAP_SATA_PLL_CFG4                                     0x20
+#define OMAP_SATA_PLL_CFG4_REGM_F                              0
+#define OMAP_SATA_PLL_CFG4_REGM_F_MASK                         0x3ffff
+#define OMAP_SATA_PLL_CFG4_REGM2                               1
+#define OMAP_SATA_PLL_CFG4_REGM2_MASK                          0x1fc
+#define OMAP_SATA_PLL_CFG4_REGM2_SHIFT                         18
+
+#define OMAP_OCP2SCP3_SYSCONFIG                                        0x10
+#define OMAP_OCP2SCP3_SYSCONFIG_RESET_MASK                     1
+#define OMAP_OCP2SCP3_SYSCONFIG_RESET_SHIFT                    1
+#define OMAP_OCP2SCP3_SYSCONFIG_RESET                          1
+
+#define OMAP_OCP2SCP3_SYSSTATUS                                        0x14
+#define OMAP_OCP2SCP3_SYSSTATUS_RESETDONE                      1
+
+#define OMAP_OCP2SCP3_TIMING                                   0x18
+#define OMAP_OCP2SCP3_TIMING_DIV_MASK                          0x7
+#define OMAP_OCP2SCP3_TIMING_DIV_SHIFT                         7
+#define OMAP_OCP2SCP3_TIMING_DIV                               1
+#define OMAP_OCP2SCP3_TIMING_SYNC1_MASK                                0x7
+#define OMAP_OCP2SCP3_TIMING_SYNC1_SHIFT                       4
+#define OMAP_OCP2SCP3_TIMING_SYNC1                             0
+#define OMAP_OCP2SCP3_TIMING_SYNC2_MASK                                0xF
+#define OMAP_OCP2SCP3_TIMING_SYNC2                             0xF
+
+#define SATAPHYRX_IO_A2D_OVRIDES_REG1                          0x44
+#define SATAPHYRX_IO_A2D_OVRIDES_REG1_MEMCDR_LOS_SRC_MASK      3
+#define SATAPHYRX_IO_A2D_OVRIDES_REG1_MEMCDR_LOS_SRC_SHIFT     9
+
+#define OMAP_CTRL_MODULE_CORE                                  0x4a002000
+#define OMAP_CTRL_MODULE_CORE_SIZE                             2048
+
+#define OMAP_CTRL_SATA_PHY_POWER                               0x374
+#define SATA_PWRCTL_CLK_FREQ_SHIFT                             22
+#define SATA_PWRCTL_CLK_CMD_SHIFT                              14
+
+#define OMAP_CTRL_SATA_EXT_MODE                                        0x3ac
+
+/* Enable the 19.2 Mhz frequency */
+#define SATA_PWRCTL_CLK_FREQ                                   19
+
+/* Enable Tx and Rx phys */
+#define SATA_PWRCTL_CLK_CMD                                    3
+
+struct omap_sata_platdata {
+       void __iomem            *ocp2scp3, *phyrx, *pll;
+       struct omap_ocp2scp_dev *dev_attr;
+       struct clk      *ref_clk;
+};
+
+static struct omap_sata_platdata       omap_sata_data;
+static struct ahci_platform_data       sata_pdata;
+static u64                             sata_dmamask = DMA_BIT_MASK(32);
+
+
+static void __iomem                    *sataphy_pwr;
+
+static struct omap_device_pm_latency omap_sata_latency[] = {
+         {
+               .deactivate_func = omap_device_idle_hwmods,
+               .activate_func   = omap_device_enable_hwmods,
+               .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
+         },
+};
+
+static inline void omap_sata_writel(void __iomem *base, u32 reg, u32 val)
+{
+       __raw_writel(val, base + reg);
+}
+
+static inline u32 omap_sata_readl(void __iomem *base, u32 reg)
+{
+       return __raw_readl(base + reg);
+}
+
+static void sataphy_pwr_init(void)
+{
+       sataphy_pwr = ioremap(OMAP_CTRL_MODULE_CORE,
+                             OMAP_CTRL_MODULE_CORE_SIZE);
+       if (!sataphy_pwr)
+               pr_err("%s: ioremap of 0x%X failed\n", __func__,
+                      OMAP_CTRL_MODULE_CORE);
+}
+
+static void  sataphy_pwr_on(void)
+{
+       if (!sataphy_pwr) {
+               pr_err("%s: 0x%X not accessible\n", __func__,
+                      OMAP_CTRL_MODULE_CORE);
+               return;
+       }
+
+       omap_sata_writel(sataphy_pwr, OMAP_CTRL_SATA_PHY_POWER,
+                        ((SATA_PWRCTL_CLK_FREQ << SATA_PWRCTL_CLK_FREQ_SHIFT)|
+                        (SATA_PWRCTL_CLK_CMD << SATA_PWRCTL_CLK_CMD_SHIFT)));
+       omap_sata_writel(sataphy_pwr, OMAP_CTRL_SATA_EXT_MODE, 1);
+}
+
+static void sataphy_pwr_off(void)
+{
+       u32 reg;
+
+       if (!sataphy_pwr) {
+               pr_err("%s: 0x%X not accessible\n", __func__,
+                      OMAP_CTRL_MODULE_CORE);
+               return;
+       }
+
+       reg = omap_sata_readl(sataphy_pwr, OMAP_CTRL_SATA_PHY_POWER);
+       reg &= ~(SATA_PWRCTL_CLK_CMD << SATA_PWRCTL_CLK_CMD_SHIFT);
+       omap_sata_writel(sataphy_pwr, OMAP_CTRL_SATA_PHY_POWER, reg);
+}
+
+static void sataphy_pwr_deinit(void)
+{
+       iounmap(sataphy_pwr);
+}
+
+
+static void omap_sataphyrx_init(struct device *dev, void __iomem *base)
+{
+       u32 reg;
+
+       reg = omap_sata_readl(base, SATAPHYRX_IO_A2D_OVRIDES_REG1);
+       reg &= ~(SATAPHYRX_IO_A2D_OVRIDES_REG1_MEMCDR_LOS_SRC_MASK <<
+                SATAPHYRX_IO_A2D_OVRIDES_REG1_MEMCDR_LOS_SRC_SHIFT);
+       omap_sata_writel(base, SATAPHYRX_IO_A2D_OVRIDES_REG1, reg);
+}
+
+static void omap_ocp2scp_init(struct device *dev, void __iomem *base)
+{
+       unsigned long   timeout;
+       u32             reg;
+
+       /* config the bridge timing */
+       reg  = omap_sata_readl(base, OMAP_OCP2SCP3_TIMING);
+       reg &= (~(OMAP_OCP2SCP3_TIMING_DIV_MASK <<
+                       OMAP_OCP2SCP3_TIMING_DIV_SHIFT) |
+               ~(OMAP_OCP2SCP3_TIMING_SYNC1_MASK <<
+                       OMAP_OCP2SCP3_TIMING_SYNC1_SHIFT) |
+               ~(OMAP_OCP2SCP3_TIMING_SYNC2_MASK));
+
+       reg |= ((OMAP_OCP2SCP3_TIMING_DIV <<
+                       OMAP_OCP2SCP3_TIMING_DIV_SHIFT) |
+               (OMAP_OCP2SCP3_TIMING_SYNC1 <<
+                       OMAP_OCP2SCP3_TIMING_SYNC1_SHIFT) |
+               (OMAP_OCP2SCP3_TIMING_SYNC2));
+       omap_sata_writel(base, OMAP_OCP2SCP3_TIMING, reg);
+
+       /* do the soft reset of ocp2scp3 */
+       reg  = omap_sata_readl(base, OMAP_OCP2SCP3_SYSCONFIG);
+       reg &= ~(OMAP_OCP2SCP3_SYSCONFIG_RESET_MASK <<
+               OMAP_OCP2SCP3_SYSCONFIG_RESET_SHIFT);
+       reg |= (OMAP_OCP2SCP3_SYSCONFIG_RESET <<
+               OMAP_OCP2SCP3_SYSCONFIG_RESET_SHIFT);
+       omap_sata_writel(base, OMAP_OCP2SCP3_SYSCONFIG, reg);
+
+       /* wait for the reset to complete */
+       timeout = jiffies + msecs_to_jiffies(10000);
+       do {
+               cpu_relax();
+               reg = omap_sata_readl(base, OMAP_OCP2SCP3_SYSSTATUS) &
+                       OMAP_OCP2SCP3_SYSSTATUS_RESETDONE;
+               if (time_after(jiffies, timeout) && !reg) {
+                       dev_err(dev, "OCP2SCP RESET[0x%08x] timed out\n", reg);
+                       break;
+               }
+       } while (!reg);
+}
+
+static int sata_dpll_wait_lock(struct device *dev, void __iomem *base)
+{
+       unsigned long timeout;
+
+       /* Poll for the PLL lock */
+       timeout = jiffies + msecs_to_jiffies(10000);
+       while (!(omap_sata_readl(base, OMAP_SATA_PLL_STATUS) &
+               (OMAP_SATA_PLL_STATUS_LOCK <<
+               OMAP_SATA_PLL_STATUS_LOCK_SHIFT))) {
+               cpu_relax();
+
+               if (time_after(jiffies, timeout)) {
+                       dev_err(dev, "sata phy pll lock timed out\n");
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static int sata_dpll_config(struct device *dev, void __iomem *base)
+{
+       u32             reg;
+       unsigned long   timeout;
+
+       /* Make sure ADPLLLJM is out of reset before configuring it */
+       timeout = jiffies + msecs_to_jiffies(10000);
+       while (!(omap_sata_readl(base, OMAP_SATA_PLL_STATUS) &
+               OMAP_SATA_PLL_STATUS_RESET_DONE)) {
+               cpu_relax();
+
+               if (time_after(jiffies, timeout)) {
+                       dev_err(dev, "SATA PLL RESET - timed out\n");
+                       return -1;
+               }
+       }
+       reg  = omap_sata_readl(base, OMAP_SATA_PLL_CFG1);
+       reg &= ~((OMAP_SATA_PLL_CFG1_M_MASK << OMAP_SATA_PLL_CFG1_M_SHIFT) |
+                (OMAP_SATA_PLL_CFG1_N_MASK << OMAP_SATA_PLL_CFG1_N_SHIFT));
+       reg |= ((OMAP_SATA_PLL_CFG1_M << OMAP_SATA_PLL_CFG1_M_SHIFT) |
+               (OMAP_SATA_PLL_CFG1_N << OMAP_SATA_PLL_CFG1_N_SHIFT));
+       omap_sata_writel(base, OMAP_SATA_PLL_CFG1, reg);
+
+       reg  = omap_sata_readl(base, OMAP_SATA_PLL_CFG2);
+       reg &= (~(OMAP_SATA_PLL_CFG2_SELFREQDCO_MASK <<
+                 OMAP_SATA_PLL_CFG2_SELFREQDCO_SHIFT) |
+               ~(OMAP_SATA_PLL_CFG2_REFSEL_MASK <<
+                 OMAP_SATA_PLL_CFG2_REFSEL_SHIFT) |
+               ~(OMAP_SATA_PLL_CFG2_LOCKSEL_MASK <<
+                 OMAP_SATA_PLL_CFG2_LOCKSEL_SHIFT));
+
+       reg |= ((OMAP_SATA_PLL_CFG2_SELFREQDCO <<
+                OMAP_SATA_PLL_CFG2_SELFREQDCO_SHIFT) |
+               (OMAP_SATA_PLL_CFG2_REFSEL <<
+                OMAP_SATA_PLL_CFG2_REFSEL_SHIFT) |
+               (OMAP_SATA_PLL_CFG2_LOCKSEL <<
+                OMAP_SATA_PLL_CFG2_LOCKSEL_SHIFT));
+       omap_sata_writel(base, OMAP_SATA_PLL_CFG2, reg);
+
+       reg  = omap_sata_readl(base, OMAP_SATA_PLL_CFG3);
+       reg &= ~(OMAP_SATA_PLL_CFG3_SD_MASK << OMAP_SATA_PLL_CFG3_SD_SHIF);
+       reg |= (OMAP_SATA_PLL_CFG3_SD << OMAP_SATA_PLL_CFG3_SD_SHIF);
+       omap_sata_writel(base, OMAP_SATA_PLL_CFG3, reg);
+
+       reg  = omap_sata_readl(base, OMAP_SATA_PLL_CFG4);
+       reg &= (~(OMAP_SATA_PLL_CFG4_REGM_F_MASK) |
+               ~(OMAP_SATA_PLL_CFG4_REGM2_MASK <<
+                 OMAP_SATA_PLL_CFG4_REGM2_SHIFT));
+       reg |= ((OMAP_SATA_PLL_CFG4_REGM_F) |
+               (OMAP_SATA_PLL_CFG4_REGM2   <<
+                OMAP_SATA_PLL_CFG4_REGM2_SHIFT));
+       omap_sata_writel(base, OMAP_SATA_PLL_CFG4, reg);
+       omap_sata_writel(base, OMAP_SATA_PLL_GO, 1);
+
+       return sata_dpll_wait_lock(dev, base);
+}
+
+
+static struct resource *ocp2scp_get_resource_bynme(struct omap_ocp2scp_dev *dev,
+                                                  unsigned int      type,
+                                                  const char        *name)
+{
+       struct resource *res = dev->res;
+
+       while (dev && res && res->start != res->end) {
+               if (type == resource_type(res) && !strcmp(res->name, name))
+                       return res;
+               res++;
+       }
+       return NULL;
+}
+
+static int sata_phy_init(struct device *dev)
+{
+       struct resource                 *res;
+       struct platform_device          *pdev;
+       struct ahci_platform_data       *pdata;
+       struct omap_sata_platdata       *spdata;
+       struct omap_ocp2scp_dev         *ocp2scpdev;
+       int                             ret;
+
+       pdev =  container_of(dev, struct platform_device, dev);
+       pdata = dev->platform_data;
+
+       if (!pdata || !pdata->priv) {
+               dev_err(dev, "No platform data\n");
+               return -ENODEV;
+       }
+       spdata = pdata->priv;
+       ocp2scpdev = spdata->dev_attr;
+       spdata->ref_clk = clk_get(dev, "sata_ref_clk");
+       if (IS_ERR(spdata->ref_clk)) {
+               ret = PTR_ERR(spdata->ref_clk);
+               dev_err(dev, "sata_ref_clk failed:%d\n", ret);
+               return ret;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ocp2scp3");
+       if (!res) {
+               dev_err(dev, "ocp2scp3 get resource failed\n");
+               return -ENXIO;
+       }
+
+       spdata->ocp2scp3 = ioremap(res->start, resource_size(res));
+       if (!spdata->ocp2scp3) {
+               dev_err(dev, "can't map ocp2scp3 0x%X\n", res->start);
+               return -ENOMEM;
+       }
+
+       res = ocp2scp_get_resource_bynme(ocp2scpdev, IORESOURCE_MEM,
+                                         "sata_pll");
+       if (!res) {
+               dev_err(dev, "sata_pll get resource failed\n");
+               ret =  -ENXIO;
+               goto pll_err_end;
+       }
+
+       spdata->pll = ioremap(res->start, resource_size(res));
+       if (!spdata->pll) {
+               dev_err(dev, "can't map sata_pll 0x%X\n", res->start);
+               ret =  -ENOMEM;
+               goto pll_err_end;
+       }
+
+       res = ocp2scp_get_resource_bynme(ocp2scpdev, IORESOURCE_MEM,
+                                         "sata_phy_rx");
+       if (!res) {
+               dev_err(dev, "sata_phy_rx get resource failed\n");
+               ret =  -ENXIO;
+               goto rx_err_end;
+       }
+
+       spdata->phyrx = ioremap(res->start, resource_size(res));
+       if (!spdata->phyrx) {
+               dev_err(dev, "can't map sata_phy_rx 0x%X\n", res->start);
+               ret =  -ENOMEM;
+               goto rx_err_end;
+       }
+
+       clk_enable(spdata->ref_clk);
+
+       omap_ocp2scp_init(dev, spdata->ocp2scp3);
+       sata_dpll_config(dev, spdata->pll);
+       sata_dpll_wait_lock(dev, spdata->pll);
+       omap_sataphyrx_init(dev, spdata->phyrx);
+       sataphy_pwr_init();
+       sataphy_pwr_on();
+
+       return 0;
+
+rx_err_end:
+       iounmap(spdata->pll);
+
+pll_err_end:
+       iounmap(spdata->ocp2scp3);
+
+       return ret;
+}
+
+static void sata_phy_exit(struct device *dev)
+{
+       struct platform_device          *pdev;
+       struct ahci_platform_data       *pdata;
+       struct omap_sata_platdata       *spdata;
+
+       pdev =  container_of(dev, struct platform_device, dev);
+       pdata = dev->platform_data;
+
+       if (!pdata || !pdata->priv) {
+               dev_err(dev, "No platform data\n");
+               return;
+       }
+       spdata = pdata->priv;
+
+       clk_disable(spdata->ref_clk);
+       clk_put(spdata->ref_clk);
+
+       iounmap(spdata->phyrx);
+       iounmap(spdata->pll);
+       iounmap(spdata->ocp2scp3);
+
+       sataphy_pwr_off();
+       sataphy_pwr_deinit();
+}
+
+static int omap_ahci_plat_init(struct device *dev, void __iomem *base)
+{
+       pm_runtime_enable(dev);
+       pm_runtime_get_sync(dev);
+       sata_phy_init(dev);
+       return 0;
+}
+
+static void omap_ahci_plat_exit(struct device *dev)
+{
+       pm_runtime_put_sync(dev);
+       pm_runtime_disable(dev);
+       sata_phy_exit(dev);
+}
+
+#ifdef CONFIG_PM
+
+static int omap_sata_suspend(struct device *dev)
+{
+       struct ahci_platform_data       *pdata;
+       struct omap_sata_platdata       *spdata;
+       unsigned long                   timeout;
+       u32                             reg;
+
+       pdata = dev->platform_data;
+       if (!pdata || !pdata->priv) {
+               dev_err(dev, "No platform data\n");
+               return -ENXIO;
+       }
+       spdata = pdata->priv;
+       if (!spdata->ref_clk || !spdata->pll) {
+               dev_err(dev, "No refclk and pll\n");
+               return -EPERM;
+       }
+
+       reg  = omap_sata_readl(spdata->pll, OMAP_SATA_PLL_CFG2);
+       reg &= ~OMAP_SATA_PLL_CFG2_IDLE_MASK;
+       reg |= OMAP_SATA_PLL_CFG2_IDLE;
+       omap_sata_writel(spdata->pll, OMAP_SATA_PLL_CFG2, reg);
+
+       /* wait for internal LDO and internal oscilator power down */
+       timeout = jiffies + msecs_to_jiffies(10000);
+       do {
+               cpu_relax();
+               reg = omap_sata_readl(spdata->pll, OMAP_SATA_PLL_STATUS);
+               reg = (reg >> OMAP_SATA_PLL_STATUS_LDOPWDN_SHIFT) &
+                     (reg >> OMAP_SATA_PLL_STATUS_TICOPWDN_SHIFT);
+               if (time_after(jiffies, timeout) & !reg) {
+                       dev_err(dev, "ADPLLLJM IDLE[0x%08x] - timed out\n",
+                               reg);
+                       return -EIO;
+               }
+       } while (!reg);
+
+#ifdef OMAP_SATA_PHY_PWR
+       sataphy_pwr_off();
+#endif
+
+       clk_disable(spdata->ref_clk);
+       return 0;
+}
+
+
+static int omap_sata_resume(struct device *dev)
+{
+       struct ahci_platform_data       *pdata;
+       struct omap_sata_platdata       *spdata;
+       u32                             reg;
+
+       pdata = dev->platform_data;
+       if (!pdata || !pdata->priv) {
+               dev_err(dev, "No platform data\n");
+               return -ENXIO;
+       }
+       spdata = pdata->priv;
+       if (!spdata->ref_clk || !spdata->pll ||
+           !spdata->ocp2scp3 || !spdata->phyrx) {
+               dev_err(dev, "No refclk , pll and ocp2scp3\n");
+               return -EPERM;
+       }
+
+       clk_enable(spdata->ref_clk);
+       omap_ocp2scp_init(dev, spdata->ocp2scp3);
+       reg  = omap_sata_readl(spdata->pll, OMAP_SATA_PLL_CFG2);
+       if (reg & OMAP_SATA_PLL_CFG2_IDLE_MASK) {
+               dev_err(dev, "sata pll in idle\n");
+               reg &= ~OMAP_SATA_PLL_CFG2_IDLE_MASK;
+               omap_sata_writel(spdata->pll, OMAP_SATA_PLL_CFG2, reg);
+       } else {
+               dev_err(dev, "sata pll not in idle, so reconfigure pll\n");
+               omap_sataphyrx_init(dev, spdata->phyrx);
+               sata_dpll_config(dev, spdata->pll);
+       }
+       sata_dpll_wait_lock(dev, spdata->pll);
+
+#ifdef OMAP_SATA_PHY_PWR
+       sataphy_pwr_on();
+#endif
+
+       return 0;
+}
+
+#else
+static int omap_sata_suspend(struct device *dev)
+{
+       return -EPERM;
+}
+
+static int omap_sata_resume(struct device *dev)
+{
+       return -EPERM;
+}
+
+#endif
+
+void __init omap_sata_init(void)
+{
+       struct omap_hwmod       *hwmod[2];
+       struct platform_device  *pdev;
+       struct device           *dev;
+       int                     oh_cnt = 1;
+
+       /* For now sata init works only for omap5 */
+       if (!soc_is_omap54xx())
+               return;
+
+       sata_pdata.init         = omap_ahci_plat_init;
+       sata_pdata.exit         = omap_ahci_plat_exit;
+       sata_pdata.suspend      = omap_sata_suspend;
+       sata_pdata.resume       = omap_sata_resume;
+
+       hwmod[0] = omap_hwmod_lookup(OMAP_SATA_HWMODNAME);
+       if (!hwmod[0]) {
+               pr_err("Could not look up %s\n", OMAP_SATA_HWMODNAME);
+               return;
+       }
+
+       hwmod[1] = omap_hwmod_lookup(OMAP_OCP2SCP3_HWMODNAME);
+       if (hwmod[1]) {
+               oh_cnt++;
+       } else {
+               pr_err("Could not look up %s\n", OMAP_OCP2SCP3_HWMODNAME);
+               return;
+       }
+
+       omap_sata_data.dev_attr = (struct omap_ocp2scp_dev *)hwmod[1]->dev_attr;
+       sata_pdata.priv = &omap_sata_data;
+
+       pdev = omap_device_build_ss(AHCI_PLAT_DEVNAME, PLATFORM_DEVID_AUTO, hwmod, oh_cnt,
+                               (void *)&sata_pdata, sizeof(sata_pdata),
+                               omap_sata_latency,
+                               ARRAY_SIZE(omap_sata_latency), false);
+       if (IS_ERR(pdev)) {
+               pr_err("Could not build hwmod device %s\n",
+                      OMAP_SATA_HWMODNAME);
+               return;
+       }
+       dev = &pdev->dev;
+       get_device(dev);
+       dev->dma_mask = &sata_dmamask;
+       dev->coherent_dma_mask = DMA_BIT_MASK(32);
+       put_device(dev);
+}
diff --git a/arch/arm/mach-omap2/scrm54xx.h b/arch/arm/mach-omap2/scrm54xx.h
new file mode 100644 (file)
index 0000000..57e86c8
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * OMAP54XX SCRM registers and bitfields
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Benoit Cousson (b-cousson@ti.com)
+ *
+ * This file is automatically generated from the OMAP hardware databases.
+ * We respectfully ask that any modifications to this file be coordinated
+ * with the public linux-omap@vger.kernel.org mailing list and the
+ * authors above to ensure that the autogeneration scripts are kept
+ * up-to-date with the file contents.
+ *
+ * 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 __ARCH_ARM_MACH_OMAP2_SCRM_54XX_H
+#define __ARCH_ARM_MACH_OMAP2_SCRM_54XX_H
+
+#define OMAP5_SCRM_BASE                0x4ae0a000
+
+#define OMAP54XX_SCRM_REGADDR(reg)                             \
+       OMAP2_L4_IO_ADDRESS(OMAP5_SCRM_BASE + (reg))
+
+/* SCRM */
+
+/* SCRM.SCRM register offsets */
+#define OMAP5_SCRM_REVISION_SCRM_OFFSET                0x0000
+#define OMAP5_SCRM_REVISION_SCRM               OMAP54XX_SCRM_REGADDR(0x0000)
+#define OMAP5_SCRM_CLKSETUPTIME_OFFSET         0x0100
+#define OMAP5_SCRM_CLKSETUPTIME                        OMAP54XX_SCRM_REGADDR(0x0100)
+#define OMAP5_SCRM_PMICSETUPTIME_OFFSET                0x0104
+#define OMAP5_SCRM_PMICSETUPTIME               OMAP54XX_SCRM_REGADDR(0x0104)
+#define OMAP5_SCRM_ALTCLKSRC_OFFSET            0x0110
+#define OMAP5_SCRM_ALTCLKSRC                   OMAP54XX_SCRM_REGADDR(0x0110)
+#define OMAP5_SCRM_MODEMCLKM_OFFSET            0x0118
+#define OMAP5_SCRM_MODEMCLKM                   OMAP54XX_SCRM_REGADDR(0x0118)
+#define OMAP5_SCRM_D2DCLKM_OFFSET              0x011c
+#define OMAP5_SCRM_D2DCLKM                     OMAP54XX_SCRM_REGADDR(0x011c)
+#define OMAP5_SCRM_EXTCLKREQ_OFFSET            0x0200
+#define OMAP5_SCRM_EXTCLKREQ                   OMAP54XX_SCRM_REGADDR(0x0200)
+#define OMAP5_SCRM_ACCCLKREQ_OFFSET            0x0204
+#define OMAP5_SCRM_ACCCLKREQ                   OMAP54XX_SCRM_REGADDR(0x0204)
+#define OMAP5_SCRM_PWRREQ_OFFSET               0x0208
+#define OMAP5_SCRM_PWRREQ                      OMAP54XX_SCRM_REGADDR(0x0208)
+#define OMAP5_SCRM_AUXCLKREQ0_OFFSET           0x0210
+#define OMAP5_SCRM_AUXCLKREQ0                  OMAP54XX_SCRM_REGADDR(0x0210)
+#define OMAP5_SCRM_AUXCLKREQ1_OFFSET           0x0214
+#define OMAP5_SCRM_AUXCLKREQ1                  OMAP54XX_SCRM_REGADDR(0x0214)
+#define OMAP5_SCRM_AUXCLKREQ2_OFFSET           0x0218
+#define OMAP5_SCRM_AUXCLKREQ2                  OMAP54XX_SCRM_REGADDR(0x0218)
+#define OMAP5_SCRM_AUXCLKREQ3_OFFSET           0x021c
+#define OMAP5_SCRM_AUXCLKREQ3                  OMAP54XX_SCRM_REGADDR(0x021c)
+#define OMAP5_SCRM_AUXCLKREQ4_OFFSET           0x0220
+#define OMAP5_SCRM_AUXCLKREQ4                  OMAP54XX_SCRM_REGADDR(0x0220)
+#define OMAP5_SCRM_AUXCLKREQ5_OFFSET           0x0224
+#define OMAP5_SCRM_AUXCLKREQ5                  OMAP54XX_SCRM_REGADDR(0x0224)
+#define OMAP5_SCRM_D2DCLKREQ_OFFSET            0x0234
+#define OMAP5_SCRM_D2DCLKREQ                   OMAP54XX_SCRM_REGADDR(0x0234)
+#define OMAP5_SCRM_AUXCLK0_OFFSET              0x0310
+#define OMAP5_SCRM_AUXCLK0                     OMAP54XX_SCRM_REGADDR(0x0310)
+#define OMAP5_SCRM_AUXCLK1_OFFSET              0x0314
+#define OMAP5_SCRM_AUXCLK1                     OMAP54XX_SCRM_REGADDR(0x0314)
+#define OMAP5_SCRM_AUXCLK2_OFFSET              0x0318
+#define OMAP5_SCRM_AUXCLK2                     OMAP54XX_SCRM_REGADDR(0x0318)
+#define OMAP5_SCRM_AUXCLK3_OFFSET              0x031c
+#define OMAP5_SCRM_AUXCLK3                     OMAP54XX_SCRM_REGADDR(0x031c)
+#define OMAP5_SCRM_AUXCLK4_OFFSET              0x0320
+#define OMAP5_SCRM_AUXCLK4                     OMAP54XX_SCRM_REGADDR(0x0320)
+#define OMAP5_SCRM_AUXCLK5_OFFSET              0x0324
+#define OMAP5_SCRM_AUXCLK5                     OMAP54XX_SCRM_REGADDR(0x0324)
+#define OMAP5_SCRM_RSTTIME_OFFSET              0x0400
+#define OMAP5_SCRM_RSTTIME                     OMAP54XX_SCRM_REGADDR(0x0400)
+#define OMAP5_SCRM_MODEMRSTCTRL_OFFSET         0x0418
+#define OMAP5_SCRM_MODEMRSTCTRL                        OMAP54XX_SCRM_REGADDR(0x0418)
+#define OMAP5_SCRM_D2DRSTCTRL_OFFSET           0x041c
+#define OMAP5_SCRM_D2DRSTCTRL                  OMAP54XX_SCRM_REGADDR(0x041c)
+#define OMAP5_SCRM_EXTPWRONRSTCTRL_OFFSET      0x0420
+#define OMAP5_SCRM_EXTPWRONRSTCTRL             OMAP54XX_SCRM_REGADDR(0x0420)
+#define OMAP5_SCRM_EXTWARMRSTST_OFFSET         0x0510
+#define OMAP5_SCRM_EXTWARMRSTST                        OMAP54XX_SCRM_REGADDR(0x0510)
+#define OMAP5_SCRM_APEWARMRSTST_OFFSET         0x0514
+#define OMAP5_SCRM_APEWARMRSTST                        OMAP54XX_SCRM_REGADDR(0x0514)
+#define OMAP5_SCRM_MODEMWARMRSTST_OFFSET       0x0518
+#define OMAP5_SCRM_MODEMWARMRSTST              OMAP54XX_SCRM_REGADDR(0x0518)
+#define OMAP5_SCRM_D2DWARMRSTST_OFFSET         0x051c
+#define OMAP5_SCRM_D2DWARMRSTST                        OMAP54XX_SCRM_REGADDR(0x051c)
+
+/*
+ * Used by AUXCLKREQ0, AUXCLKREQ1, AUXCLKREQ2, AUXCLKREQ3, AUXCLKREQ4,
+ * AUXCLKREQ5, D2DCLKREQ
+ */
+#define OMAP5_ACCURACY_SHIFT                   1
+#define OMAP5_ACCURACY_WIDTH                   0x1
+#define OMAP5_ACCURACY_MASK                    (1 << 1)
+
+/* Used by APEWARMRSTST */
+#define OMAP5_APEWARMRSTST_SHIFT               1
+#define OMAP5_APEWARMRSTST_WIDTH               0x1
+#define OMAP5_APEWARMRSTST_MASK                        (1 << 1)
+
+/* Used by AUXCLK0, AUXCLK1, AUXCLK2, AUXCLK3, AUXCLK4, AUXCLK5 */
+#define OMAP5_CLKDIV_SHIFT                     16
+#define OMAP5_CLKDIV_WIDTH                     0x4
+#define OMAP5_CLKDIV_MASK                      (0xf << 16)
+
+/* Used by D2DCLKM, MODEMCLKM */
+#define OMAP5_CLK_32KHZ_SHIFT                  0
+#define OMAP5_CLK_32KHZ_WIDTH                  0x1
+#define OMAP5_CLK_32KHZ_MASK                   (1 << 0)
+
+/* Used by D2DRSTCTRL, MODEMRSTCTRL */
+#define OMAP5_COLDRST_SHIFT                    0
+#define OMAP5_COLDRST_WIDTH                    0x1
+#define OMAP5_COLDRST_MASK                     (1 << 0)
+
+/* Used by D2DWARMRSTST */
+#define OMAP5_D2DWARMRSTST_SHIFT               3
+#define OMAP5_D2DWARMRSTST_WIDTH               0x1
+#define OMAP5_D2DWARMRSTST_MASK                        (1 << 3)
+
+/* Used by AUXCLK0 */
+#define OMAP5_DISABLECLK_SHIFT                 9
+#define OMAP5_DISABLECLK_WIDTH                 0x1
+#define OMAP5_DISABLECLK_MASK                  (1 << 9)
+
+/* Used by CLKSETUPTIME */
+#define OMAP5_DOWNTIME_SHIFT                   16
+#define OMAP5_DOWNTIME_WIDTH                   0x6
+#define OMAP5_DOWNTIME_MASK                    (0x3f << 16)
+
+/* Used by AUXCLK0, AUXCLK1, AUXCLK2, AUXCLK3, AUXCLK4, AUXCLK5 */
+#define OMAP5_ENABLE_SHIFT                     8
+#define OMAP5_ENABLE_WIDTH                     0x1
+#define OMAP5_ENABLE_MASK                      (1 << 8)
+
+/* Renamed from ENABLE Used by EXTPWRONRSTCTRL */
+#define OMAP5_ENABLE_0_0_SHIFT                 0
+#define OMAP5_ENABLE_0_0_WIDTH                 0x1
+#define OMAP5_ENABLE_0_0_MASK                  (1 << 0)
+
+/* Used by ALTCLKSRC */
+#define OMAP5_ENABLE_EXT_SHIFT                 3
+#define OMAP5_ENABLE_EXT_WIDTH                 0x1
+#define OMAP5_ENABLE_EXT_MASK                  (1 << 3)
+
+/* Used by ALTCLKSRC */
+#define OMAP5_ENABLE_INT_SHIFT                 2
+#define OMAP5_ENABLE_INT_WIDTH                 0x1
+#define OMAP5_ENABLE_INT_MASK                  (1 << 2)
+
+/* Used by EXTWARMRSTST */
+#define OMAP5_EXTWARMRSTST_SHIFT               0
+#define OMAP5_EXTWARMRSTST_WIDTH               0x1
+#define OMAP5_EXTWARMRSTST_MASK                        (1 << 0)
+
+/*
+ * Used by AUXCLKREQ0, AUXCLKREQ1, AUXCLKREQ2, AUXCLKREQ3, AUXCLKREQ4,
+ * AUXCLKREQ5
+ */
+#define OMAP5_MAPPING_SHIFT                    2
+#define OMAP5_MAPPING_WIDTH                    0x3
+#define OMAP5_MAPPING_MASK                     (0x7 << 2)
+
+/* Used by ALTCLKSRC */
+#define OMAP5_MODE_SHIFT                       0
+#define OMAP5_MODE_WIDTH                       0x2
+#define OMAP5_MODE_MASK                                (0x3 << 0)
+
+/* Used by MODEMWARMRSTST */
+#define OMAP5_MODEMWARMRSTST_SHIFT             2
+#define OMAP5_MODEMWARMRSTST_WIDTH             0x1
+#define OMAP5_MODEMWARMRSTST_MASK              (1 << 2)
+
+/*
+ * Used by ACCCLKREQ, AUXCLK0, AUXCLK1, AUXCLK2, AUXCLK3, AUXCLK4, AUXCLK5,
+ * AUXCLKREQ0, AUXCLKREQ1, AUXCLKREQ2, AUXCLKREQ3, AUXCLKREQ4, AUXCLKREQ5,
+ * D2DCLKREQ, EXTCLKREQ, PWRREQ
+ */
+#define OMAP5_POLARITY_SHIFT                   0
+#define OMAP5_POLARITY_WIDTH                   0x1
+#define OMAP5_POLARITY_MASK                    (1 << 0)
+
+/* Used by EXTPWRONRSTCTRL */
+#define OMAP5_PWRONRST_SHIFT                   1
+#define OMAP5_PWRONRST_WIDTH                   0x1
+#define OMAP5_PWRONRST_MASK                    (1 << 1)
+
+/* Used by REVISION_SCRM */
+#define OMAP5_REV_SHIFT                                0
+#define OMAP5_REV_WIDTH                                0x8
+#define OMAP5_REV_MASK                         (0xff << 0)
+
+/* Used by RSTTIME */
+#define OMAP5_RSTTIME_SHIFT                    0
+#define OMAP5_RSTTIME_WIDTH                    0x4
+#define OMAP5_RSTTIME_MASK                     (0xf << 0)
+
+/* Used by CLKSETUPTIME */
+#define OMAP5_SETUPTIME_SHIFT                  0
+#define OMAP5_SETUPTIME_WIDTH                  0xc
+#define OMAP5_SETUPTIME_MASK                   (0xfff << 0)
+
+/* Used by PMICSETUPTIME */
+#define OMAP5_SLEEPTIME_SHIFT                  0
+#define OMAP5_SLEEPTIME_WIDTH                  0x6
+#define OMAP5_SLEEPTIME_MASK                   (0x3f << 0)
+
+/* Used by AUXCLK0, AUXCLK1, AUXCLK2, AUXCLK3, AUXCLK4, AUXCLK5 */
+#define OMAP5_SRCSELECT_SHIFT                  1
+#define OMAP5_SRCSELECT_WIDTH                  0x2
+#define OMAP5_SRCSELECT_MASK                   (0x3 << 1)
+
+/* Used by D2DCLKM */
+#define OMAP5_SYSCLK_SHIFT                     1
+#define OMAP5_SYSCLK_WIDTH                     0x1
+#define OMAP5_SYSCLK_MASK                      (1 << 1)
+
+/* Used by PMICSETUPTIME */
+#define OMAP5_WAKEUPTIME_SHIFT                 16
+#define OMAP5_WAKEUPTIME_WIDTH                 0x6
+#define OMAP5_WAKEUPTIME_MASK                  (0x3f << 16)
+
+/* Used by D2DRSTCTRL, MODEMRSTCTRL */
+#define OMAP5_WARMRST_SHIFT                    1
+#define OMAP5_WARMRST_WIDTH                    0x1
+#define OMAP5_WARMRST_MASK                     (1 << 1)
+
+#endif
index 04fdbc4c499bb9d85b1ff920e46ede6d11891a69..037e69108662505e0b7c5e130aa15bdef9ff20eb 100644 (file)
@@ -95,38 +95,9 @@ static void omap_uart_enable_wakeup(struct device *dev, bool enable)
                omap_hwmod_disable_wakeup(od->hwmods[0]);
 }
 
-/*
- * Errata i291: [UART]:Cannot Acknowledge Idle Requests
- * in Smartidle Mode When Configured for DMA Operations.
- * WA: configure uart in force idle mode.
- */
-static void omap_uart_set_noidle(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct omap_device *od = to_omap_device(pdev);
-
-       omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_NO);
-}
-
-static void omap_uart_set_smartidle(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct omap_device *od = to_omap_device(pdev);
-       u8 idlemode;
-
-       if (od->hwmods[0]->class->sysc->idlemodes & SIDLE_SMART_WKUP)
-               idlemode = HWMOD_IDLEMODE_SMART_WKUP;
-       else
-               idlemode = HWMOD_IDLEMODE_SMART;
-
-       omap_hwmod_set_slave_idlemode(od->hwmods[0], idlemode);
-}
-
 #else
 static void omap_uart_enable_wakeup(struct device *dev, bool enable)
 {}
-static void omap_uart_set_noidle(struct device *dev) {}
-static void omap_uart_set_smartidle(struct device *dev) {}
 #endif /* CONFIG_PM */
 
 #ifdef CONFIG_OMAP_MUX
@@ -299,8 +270,6 @@ void __init omap_serial_init_port(struct omap_board_data *bdata,
        omap_up.uartclk = OMAP24XX_BASE_BAUD * 16;
        omap_up.flags = UPF_BOOT_AUTOCONF;
        omap_up.get_context_loss_count = omap_pm_get_dev_context_loss_count;
-       omap_up.set_forceidle = omap_uart_set_smartidle;
-       omap_up.set_noidle = omap_uart_set_noidle;
        omap_up.enable_wakeup = omap_uart_enable_wakeup;
        omap_up.dma_rx_buf_size = info->dma_rx_buf_size;
        omap_up.dma_rx_timeout = info->dma_rx_timeout;
diff --git a/arch/arm/mach-omap2/sleep33xx.S b/arch/arm/mach-omap2/sleep33xx.S
new file mode 100644 (file)
index 0000000..98fa76c
--- /dev/null
@@ -0,0 +1,584 @@
+/*
+ * Low level suspend code for AM33XX SoCs
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Vaibhav Bedia <vaibhav.bedia@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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/linkage.h>
+#include <linux/ti_emif.h>
+#include <asm/memory.h>
+#include <asm/assembler.h>
+
+#include "cm33xx.h"
+#include "pm33xx.h"
+#include "prm33xx.h"
+#include "control.h"
+
+       .text
+       .align 3
+
+       .macro  pll_bypass, name, clk_mode_addr, idlest_addr, pll_mode
+pll_bypass_\name:
+       ldr     r0, \clk_mode_addr
+       ldr     r1, [r0]
+       str     r1, clk_mode_\pll_mode
+       bic     r1, r1, #(7 << 0)
+       orr     r1, r1, #0x5
+       str     r1, [r0]
+       ldr     r0, \idlest_addr
+wait_pll_bypass_\name:
+       ldr     r1, [r0]
+       tst     r1, #0x0
+       bne     wait_pll_bypass_\name
+       .endm
+
+       .macro  pll_lock, name, clk_mode_addr, idlest_addr, pll_mode
+pll_lock_\name:
+       ldr     r0, \clk_mode_addr
+       ldr     r1, clk_mode_\pll_mode
+       str     r1, [r0]
+       and     r1, r1, #0x7
+       cmp     r1, #0x7
+       bne     pll_mode_restored_\name
+       ldr     r0, \idlest_addr
+wait_pll_lock_\name:
+       ldr     r1, [r0]
+       ands    r1, #0x1
+       beq     wait_pll_lock_\name
+pll_mode_restored_\name:
+       nop
+       .endm
+
+       .macro  ddr_self_refresh, num
+ddr_self_refresh_\num:
+       add     r1, r0, #EMIF_POWER_MANAGEMENT_CONTROL
+       ldr     r2, [r1]
+       orr     r2, r2, #0xa0           @ a reasonable delay for entering SR
+       str     r2, [r1, #0]
+       str     r2, [r1, #4]            @ write to shadow register also
+
+       ldr     r2, ddr_start           @ do a dummy access to DDR
+       ldr     r3, [r2, #0]
+       ldr     r3, [r1, #0]            @ now set the LP MODE to Self-Refresh
+       orr     r3, r3, #0x200
+       str     r3, [r1, #0]
+
+       mov     r1, #0x1000             @ Give some time for system to enter SR
+wait_sr_\num:
+       subs    r1, r1, #1
+       bne     wait_sr_\num
+       .endm
+
+       .macro  wait_sdram_config, num
+wait_sdram_config_\num:
+       mov     r0, #0x100
+wait_sc_\num:
+       subs    r0, r0 ,#1
+       bne     wait_sc_\num
+       .endm
+
+ENTRY(am33xx_do_wfi)
+       stmfd   sp!, {r4 - r11, lr}     @ save registers on stack
+       /* Get the EMIF virtual address */
+       ldr     r0, emif_addr_func
+       blx     r0
+       /* Save it for later use */
+       str     r0, emif_addr_virt
+
+       /* This ensures isb */
+       ldr     r0, dcache_flush
+       blx     r0
+
+       /* Same as v7_flush_icache_all - saving a branch */
+       mov     r0, #0
+       mcr     p15, 0, r0, c7, c5, 0   @ I+BTB cache invalidate
+
+       ldr     r0, emif_addr_virt
+       /* Save EMIF configuration */
+       ldr     r1, [r0, #EMIF_SDRAM_CONFIG]
+       str     r1, emif_sdcfg_val
+       ldr     r1, [r0, #EMIF_SDRAM_REFRESH_CONTROL]
+       str     r1, emif_ref_ctrl_val
+       ldr     r1, [r0, #EMIF_SDRAM_TIMING_1]
+       str     r1, emif_timing1_val
+       ldr     r1, [r0, #EMIF_SDRAM_TIMING_2]
+       str     r1, emif_timing2_val
+       ldr     r1, [r0, #EMIF_SDRAM_TIMING_3]
+       str     r1, emif_timing3_val
+       ldr     r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
+       str     r1, emif_pmcr_val
+       ldr     r1, [r0, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
+       str     r1, emif_pmcr_shdw_val
+       ldr     r1, [r0, #EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG]
+       str     r1, emif_zqcfg_val
+       ldr     r1, [r0, #EMIF_DDR_PHY_CTRL_1]
+       str     r1, emif_rd_lat_val
+
+       /* Ensure that all the writes to DDR leave the A8 */
+       dsb
+       dmb
+       isb
+
+       ddr_self_refresh        1
+
+       /* Disable EMIF at this point */
+       ldr     r1, virt_emif_clkctrl
+       ldr     r2, [r1]
+       bic     r2, r2, #(3 << 0)
+       str     r2, [r1]
+
+       ldr     r1, virt_emif_clkctrl
+wait_emif_disable:
+       ldr     r2, [r1]
+       ldr     r3, module_disabled_val
+       cmp     r2, r3
+       bne     wait_emif_disable
+
+       /*
+        * For the MPU WFI to be registered as an interrupt
+        * to WKUP_M3, MPU_CLKCTRL.MODULEMODE needs to be set
+        * to DISABLED
+        */
+       ldr     r1, virt_mpu_clkctrl
+       ldr     r2, [r1]
+       bic     r2, r2, #(3 << 0)
+       str     r2, [r1]
+
+       /* DDR3 reset override and mDDR mode selection */
+       ldr     r0, virt_ddr_io_ctrl
+       mov     r1, #(0x9 << 28)
+       str     r1, [r0]
+
+       /* Weak pull down for DQ, DM */
+       ldr     r1, virt_ddr_data0_ioctrl
+       ldr     r2, susp_io_pull_data
+       str     r2, [r1]
+
+       ldr     r1, virt_ddr_data1_ioctrl
+       ldr     r2, susp_io_pull_data
+       str     r2, [r1]
+
+       /* Disable VTP */
+       ldr     r1, virt_ddr_vtp_ctrl
+       ldr     r2, susp_vtp_ctrl_val
+       str     r2, [r1]
+
+       /* Enable SRAM LDO ret mode */
+       ldr     r0, virt_sram_ldo_addr
+       ldr     r1, [r0]
+       orr     r1, #1
+       str     r1, [r0]
+
+put_pll_bypass:
+       /* Put the PLLs in bypass mode */
+       pll_bypass      core, virt_core_clk_mode, virt_core_idlest, core_val
+       pll_bypass      ddr, virt_ddr_clk_mode, virt_ddr_idlest, ddr_val
+       pll_bypass      disp, virt_disp_clk_mode, virt_disp_idlest, disp_val
+       pll_bypass      per, virt_per_clk_mode, virt_per_idlest, per_val
+       pll_bypass      mpu, virt_mpu_clk_mode, virt_mpu_idlest, mpu_val
+
+       dsb
+       dmb
+       isb
+
+       wfi
+
+       /* NOPs to ensure the A8 pipeline is clean */
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       /* We come here in case of an abort due to a late interrupt */
+
+       /* Set MPU_CLKCTRL.MODULEMODE back to ENABLE */
+       ldr     r1, virt_mpu_clkctrl
+       mov     r2, #0x2
+       str     r2, [r1]
+
+       /* Relock the PLLs */
+       pll_lock        mpu_abt, virt_mpu_clk_mode, virt_mpu_idlest, mpu_val
+       pll_lock        per_abt, virt_per_clk_mode, virt_per_idlest, per_val
+       pll_lock        disp_abt, virt_disp_clk_mode, virt_disp_idlest, disp_val
+       pll_lock        ddr_abt, virt_ddr_clk_mode, virt_ddr_idlest, ddr_val
+       pll_lock        core_abt, virt_core_clk_mode, virt_core_idlest, core_val
+
+       /* Disable SRAM LDO ret mode */
+       ldr     r0, virt_sram_ldo_addr
+       ldr     r1, [r0]
+       bic     r1, #1
+       str     r1, [r0]
+
+       /* Restore the pull for DQ, DM */
+       ldr     r1, virt_ddr_data0_ioctrl
+       ldr     r2, resume_io_pull_data
+       str     r2, [r1]
+
+       ldr     r1, virt_ddr_data1_ioctrl
+       ldr     r2, resume_io_pull_data
+       str     r2, [r1]
+
+       /* Enable EMIF */
+       ldr     r1, virt_emif_clkctrl
+       mov     r2, #0x2
+       str     r2, [r1]
+wait_emif_enable:
+       ldr     r3, [r1]
+       cmp     r2, r3
+       bne     wait_emif_enable
+
+       /* Enable VTP */
+config_vtp_abt:
+       ldr     r0, virt_ddr_vtp_ctrl
+       ldr     r1, [r0]
+       mov     r2, #0x0        @ clear the register
+       str     r2, [r0]
+       mov     r2, #0x6        @ write the filter value
+       str     r2, [r0]
+
+       ldr     r1, [r0]
+       ldr     r2, vtp_enable  @ set the enable bit
+       orr     r2, r2, r1
+       str     r2, [r0]
+
+       ldr     r1, [r0]        @ toggle the CLRZ bit
+       bic     r1, #1
+       str     r1, [r0]
+
+       ldr     r1, [r0]
+       orr     r1, #1
+       str     r1, [r0]
+
+poll_vtp_ready_abt:
+       ldr     r1, [r0]        @ poll for VTP ready
+       tst     r1, #(AM33XX_VTP_CTRL_READY)
+       beq     poll_vtp_ready_abt
+
+       /* DDR3 reset override and mDDR mode clear */
+       ldr     r0, virt_ddr_io_ctrl
+       mov     r1, #0
+       str     r1, [r0]
+
+emif_self_refresh_dis:
+       /* Disable EMIF self-refresh */
+       ldr     r0, emif_addr_virt
+       add     r0, r0, #EMIF_POWER_MANAGEMENT_CONTROL
+       ldr     r1, [r0]
+       bic     r1, r1, #LP_MODE_MASK
+       str     r1, [r0]
+       str     r1, [r0, #4]
+
+       /*
+        * A write to SDRAM CONFIG register triggers
+        * an init sequence and hence it must be done
+        * at the end
+        */
+       ldr r0, emif_addr_virt
+       add r0, r0, #EMIF_SDRAM_CONFIG
+       ldr r4, emif_sdcfg_val
+       str r4, [r0]
+
+       mov r0, #0x1000
+wait_abt:
+       subs   r0, r0, #1
+       bne wait_abt
+
+       /* Let the suspend code know about the abort */
+       mov     r0, #1
+       ldmfd   sp!, {r4 - r11, pc}     @ restore regs and return
+ENDPROC(am33xx_do_wfi)
+
+       .align
+ENTRY(am33xx_resume_offset)
+       .word . - am33xx_do_wfi
+
+ENTRY(am33xx_resume_from_deep_sleep)
+       /* Take the PLLs out of LP_BYPASS */
+       pll_lock        mpu, phys_mpu_clk_mode, phys_mpu_idlest, mpu_val
+       pll_lock        per, phys_per_clk_mode, phys_per_idlest, per_val
+       pll_lock        disp, phys_disp_clk_mode, phys_disp_idlest, disp_val
+       pll_lock        ddr, phys_ddr_clk_mode, phys_ddr_idlest, ddr_val
+       pll_lock        core, phys_core_clk_mode, phys_core_idlest, core_val
+
+       /* Disable SRAM LDO ret mode */
+       ldr     r0, phys_sram_ldo_addr
+       ldr     r1, [r0]
+       bic     r1, #1
+       str     r1, [r0]
+
+       /* Restore the pull for DQ, DM */
+       ldr     r1, phys_ddr_data0_ioctrl
+       ldr     r2, resume_io_pull_data
+       str     r2, [r1]
+
+       ldr     r1, phys_ddr_data1_ioctrl
+       ldr     r2, resume_io_pull_data
+       str     r2, [r1]
+
+config_vtp:
+       ldr     r0, phys_ddr_vtp_ctrl
+       ldr     r1, [r0]
+       mov     r2, #0x0        @ clear the register
+       str     r2, [r0]
+       mov     r2, #0x6        @ write the filter value
+       str     r2, [r0]
+
+       ldr     r1, [r0]
+       ldr     r2, vtp_enable  @ set the enable bit
+       orr     r2, r2, r1
+       str     r2, [r0]
+
+       ldr     r1, [r0]        @ toggle the CLRZ bit
+       bic     r1, #1
+       str     r1, [r0]
+
+       ldr     r1, [r0]
+       orr     r1, #1
+       str     r1, [r0]
+
+poll_vtp_ready:
+       ldr     r1, [r0]        @ poll for VTP ready
+       tst     r1, #AM33XX_VTP_CTRL_READY
+       beq     poll_vtp_ready
+
+       /* DDR3 reset override and mDDR mode clear */
+       ldr     r0, phys_ddr_io_ctrl
+       mov     r1, #0
+       str     r1, [r0]
+
+       /* Enable EMIF */
+       ldr     r1, phys_emif_clkctrl
+       mov     r2, #0x2
+       str     r2, [r1]
+wait_emif_enable1:
+       ldr     r3, [r1]
+       cmp     r2, r3
+       bne     wait_emif_enable1
+
+config_emif_timings:
+       ldr     r3, emif_phys_addr
+       ldr     r4, emif_rd_lat_val
+       str     r4, [r3, #EMIF_DDR_PHY_CTRL_1]
+       str     r4, [r3, #EMIF_DDR_PHY_CTRL_1_SHDW]
+       ldr     r4, emif_timing1_val
+       str     r4, [r3, #EMIF_SDRAM_TIMING_1]
+       str     r4, [r3, #EMIF_SDRAM_TIMING_1_SHDW]
+       ldr     r4, emif_timing2_val
+       str     r4, [r3, #EMIF_SDRAM_TIMING_2]
+       str     r4, [r3, #EMIF_SDRAM_TIMING_2_SHDW]
+       ldr     r4, emif_timing3_val
+       str     r4, [r3, #EMIF_SDRAM_TIMING_3]
+       str     r4, [r3, #EMIF_SDRAM_TIMING_3_SHDW]
+       ldr     r4, emif_ref_ctrl_val
+       str     r4, [r3, #EMIF_SDRAM_REFRESH_CONTROL]
+       str     r4, [r3, #EMIF_SDRAM_REFRESH_CTRL_SHDW]
+       ldr     r4, emif_pmcr_val
+       str     r4, [r3, #EMIF_POWER_MANAGEMENT_CONTROL]
+       ldr     r4, emif_pmcr_shdw_val
+       str     r4, [r3, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
+
+       /*
+        * A write to SDRAM CONFIG register triggers
+        * an init sequence and hence it must be done
+        * at the end
+        */
+       ldr     r4, emif_sdcfg_val
+       str     r4, [r3, #EMIF_SDRAM_CONFIG]
+
+       /* Back from la-la-land. Kill some time for sanity to settle in */
+       mov     r0, #0x1000
+wait_resume:
+       subs    r0, r0, #1
+       bne     wait_resume
+
+       /* We are back. Branch to the common CPU resume routine */
+       mov     r0, #0
+       ldr     pc, resume_addr
+ENDPROC(am33xx_resume_from_deep_sleep)
+
+
+/*
+ * Local variables
+ */
+       .align
+resume_addr:
+       .word   cpu_resume - PAGE_OFFSET + 0x80000000
+dcache_flush:
+       .word   v7_flush_dcache_all
+emif_addr_func:
+       .word   am33xx_get_emif_base
+ddr_start:
+       .word   PAGE_OFFSET
+emif_phys_addr:
+       .word   AM33XX_EMIF_BASE
+virt_mpu_idlest:
+       .word   AM33XX_CM_IDLEST_DPLL_MPU
+virt_mpu_clk_mode:
+       .word   AM33XX_CM_CLKMODE_DPLL_MPU
+phys_mpu_clk_mode:
+       .word   (AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+               AM33XX_CM_CLKMODE_DPLL_MPU_OFFSET)
+phys_mpu_idlest:
+       .word   (AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+               AM33XX_CM_IDLEST_DPLL_MPU_OFFSET)
+virt_core_idlest:
+       .word   AM33XX_CM_IDLEST_DPLL_CORE
+virt_core_clk_mode:
+       .word   AM33XX_CM_CLKMODE_DPLL_CORE
+phys_core_clk_mode:
+       .word   (AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+               AM33XX_CM_CLKMODE_DPLL_CORE_OFFSET)
+phys_core_idlest:
+       .word   (AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+               AM33XX_CM_IDLEST_DPLL_CORE_OFFSET)
+virt_per_idlest:
+       .word   AM33XX_CM_IDLEST_DPLL_PER
+virt_per_clk_mode:
+       .word   AM33XX_CM_CLKMODE_DPLL_PER
+phys_per_clk_mode:
+       .word   (AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+               AM33XX_CM_CLKMODE_DPLL_PER_OFFSET)
+phys_per_idlest:
+       .word   (AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+               AM33XX_CM_IDLEST_DPLL_PER_OFFSET)
+virt_disp_idlest:
+       .word   AM33XX_CM_IDLEST_DPLL_DISP
+virt_disp_clk_mode:
+       .word   AM33XX_CM_CLKMODE_DPLL_DISP
+phys_disp_clk_mode:
+       .word   (AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+               AM33XX_CM_CLKMODE_DPLL_DISP_OFFSET)
+phys_disp_idlest:
+       .word   (AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+               AM33XX_CM_IDLEST_DPLL_DISP_OFFSET)
+virt_ddr_idlest:
+       .word   AM33XX_CM_IDLEST_DPLL_DDR
+virt_ddr_clk_mode:
+       .word   AM33XX_CM_CLKMODE_DPLL_DDR
+phys_ddr_clk_mode:
+       .word   (AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+               AM33XX_CM_CLKMODE_DPLL_DDR_OFFSET)
+phys_ddr_idlest:
+       .word   (AM33XX_CM_BASE + AM33XX_CM_WKUP_MOD + \
+               AM33XX_CM_IDLEST_DPLL_DDR_OFFSET)
+virt_sram_ldo_addr:
+       .word   AM33XX_PRM_LDO_SRAM_MPU_CTRL
+phys_sram_ldo_addr:
+       .word   (AM33XX_PRM_BASE + AM33XX_PRM_DEVICE_MOD + \
+               AM33XX_PRM_LDO_SRAM_MPU_CTRL_OFFSET)
+virt_mpu_clkctrl:
+       .word   AM33XX_CM_MPU_MPU_CLKCTRL
+virt_emif_clkctrl:
+       .word   AM33XX_CM_PER_EMIF_CLKCTRL
+phys_emif_clkctrl:
+       .word   (AM33XX_CM_BASE + AM33XX_CM_PER_MOD + \
+               AM33XX_CM_PER_EMIF_CLKCTRL_OFFSET)
+module_disabled_val:
+       .word   0x30000
+
+/* DDR related defines */
+virt_ddr_io_ctrl:
+       .word   AM33XX_CTRL_REGADDR(AM33XX_DDR_IO_CTRL)
+phys_ddr_io_ctrl:
+       .word   AM33XX_CTRL_BASE + AM33XX_DDR_IO_CTRL
+virt_ddr_vtp_ctrl:
+       .word   AM33XX_CTRL_REGADDR(AM33XX_VTP0_CTRL_REG)
+phys_ddr_vtp_ctrl:
+       .word   AM33XX_CTRL_BASE + AM33XX_VTP0_CTRL_REG
+virt_ddr_cmd0_ioctrl:
+       .word   AM33XX_CTRL_REGADDR(AM33XX_DDR_CMD0_IOCTRL)
+phys_ddr_cmd0_ioctrl:
+       .word   AM33XX_CTRL_BASE + AM33XX_DDR_CMD0_IOCTRL
+virt_ddr_cmd1_ioctrl:
+       .word   AM33XX_CTRL_REGADDR(AM33XX_DDR_CMD1_IOCTRL)
+phys_ddr_cmd1_ioctrl:
+       .word   AM33XX_CTRL_BASE + AM33XX_DDR_CMD1_IOCTRL
+virt_ddr_cmd2_ioctrl:
+       .word   AM33XX_CTRL_REGADDR(AM33XX_DDR_CMD2_IOCTRL)
+phys_ddr_cmd2_ioctrl:
+       .word   AM33XX_CTRL_BASE + AM33XX_DDR_CMD2_IOCTRL
+virt_ddr_data0_ioctrl:
+       .word   AM33XX_CTRL_REGADDR(AM33XX_DDR_DATA0_IOCTRL)
+phys_ddr_data0_ioctrl:
+       .word   AM33XX_CTRL_BASE + AM33XX_DDR_DATA0_IOCTRL
+virt_ddr_data1_ioctrl:
+       .word   AM33XX_CTRL_REGADDR(AM33XX_DDR_DATA1_IOCTRL)
+phys_ddr_data1_ioctrl:
+       .word   AM33XX_CTRL_BASE + AM33XX_DDR_DATA1_IOCTRL
+vtp_enable:
+       .word   AM33XX_VTP_CTRL_ENABLE
+
+/*
+ * Values recommended by the HW team. These change the pulls
+ * on certain IOs of DATA and CMD macros
+ */
+susp_io_pull_data:
+       .word   0x3FF00003
+susp_io_pull_cmd1:
+       .word   0xFFE0018B
+susp_io_pull_cmd2:
+       .word   0xFFA0098B
+resume_io_pull_data:
+       .word   0x18B
+resume_io_pull_cmd:
+       .word   0x18B
+susp_vtp_ctrl_val:
+       .word   0x10117
+
+/* Placeholder for storing EMIF configuration */
+emif_addr_virt:
+       .word   0xDEADBEEF
+emif_rd_lat_val:
+       .word   0xDEADBEEF
+emif_timing1_val:
+       .word   0xDEADBEEF
+emif_timing2_val:
+       .word   0xDEADBEEF
+emif_timing3_val:
+       .word   0xDEADBEEF
+emif_sdcfg_val:
+       .word   0xDEADBEEF
+emif_ref_ctrl_val:
+       .word   0xDEADBEEF
+emif_zqcfg_val:
+       .word   0xDEADBEEF
+emif_pmcr_val:
+       .word   0xDEADBEEF
+emif_pmcr_shdw_val:
+       .word   0xDEADBEEF
+
+/* Placeholder for storing PLL mode */
+clk_mode_mpu_val:
+       .word   0xDEADBEEF
+clk_mode_per_val:
+       .word   0xDEADBEEF
+clk_mode_disp_val:
+       .word   0xDEADBEEF
+clk_mode_ddr_val:
+       .word   0xDEADBEEF
+clk_mode_core_val:
+       .word   0xDEADBEEF
+
+       .align 3
+ENTRY(am33xx_do_wfi_sz)
+       .word   . - am33xx_do_wfi
+
similarity index 74%
rename from arch/arm/mach-omap2/sleep44xx.S
rename to arch/arm/mach-omap2/sleep_omap4plus.S
index 88ff83a0942eb742382ae6c1eec4e4d49951ff11..ea318be292cca977f2aecec6b177f777ce2d60bc 100644 (file)
@@ -326,6 +326,139 @@ skip_l2en:
 
        b       cpu_resume                      @ Jump to generic resume
 ENDPROC(omap4_cpu_resume)
+
+/*
+ * ================================
+ * == OMAP5 CPU suspend finisher ==
+ * ================================
+ *
+ * OMAP5 MPUSS states for the context save:
+ * save_state =
+ *     0 - Nothing lost and no need to save: MPUSS INA/CSWR
+ *     1 - CPUx L1 and logic lost: CPU OFF, MPUSS INA/CSWR
+ *     2 - CPUx L1 and logic lost + GIC lost: MPUSS OSWR
+ *     3 - CPUx L1 and logic lost + GIC + L2 lost: DEVICE OFF
+ */
+ENTRY(omap5_finish_suspend)
+       stmfd   sp!, {r4-r12, lr}
+       cmp     r0, #0x0
+       beq     do_wfi                          @ No lowpower state, jump to WFI
+
+       /*
+        * Flush all data from the L1 data cache before disabling
+        * SCTLR.C bit.
+        */
+       bl      omap4_get_sar_ram_base
+       ldr     r9, [r0, #OMAP_TYPE_OFFSET]
+       cmp     r9, #0x1                        @ Check for HS device
+       bne     skip_secure_l1_clean_op
+       mov     r0, #0                          @ Clean secure L1
+       stmfd   r13!, {r4-r12, r14}
+       ldr     r12, =OMAP5_MON_CACHES_CLEAN_INDEX
+       DO_SMC
+       ldmfd   r13!, {r4-r12, r14}
+skip_secure_l1_clean_op:
+       bl      v7_flush_dcache_louis
+
+       /*
+        * Clear the SCTLR.C bit to prevent further data cache
+        * allocation. Clearing SCTLR.C would make all the data accesses
+        * strongly ordered and would not hit the cache.
+        */
+       mrc     p15, 0, r0, c1, c0, 0
+       bic     r0, r0, #(1 << 2)               @ Disable the C bit
+       mcr     p15, 0, r0, c1, c0, 0
+       isb
+
+       /* Clean and Invalidate L1 data cache. */
+       bl      v7_flush_dcache_louis
+
+       /*
+        * Take CPU out of Symmetric Multiprocessing (SMP) mode and thus
+        * preventing the CPU from receiving cache, TLB, or BTB
+        * maintenance operations broadcast by other CPUs in the cluster.
+        */
+       mrc     p15, 0, r0, c1, c1, 2           @ Read NSACR data
+       tst     r0, #(1 << 18)
+       mrcne   p15, 0, r0, c1, c0, 1
+       bicne   r0, r0, #(1 << 6)               @ Disable SMP bit
+       mcrne   p15, 0, r0, c1, c0, 1
+       isb
+       dsb
+
+       bl      omap4_get_sar_ram_base
+       mov     r8, r0
+       mrc     p15, 0, r5, c0, c0, 5           @ Read MPIDR
+       ands    r5, r5, #0x0f
+       ldreq   r0, [r8, #L2X0_SAVE_OFFSET0]    @ Retrieve L2 state
+       ldrne   r0, [r8, #L2X0_SAVE_OFFSET1]
+       cmp     r0, #3
+       bne     do_wfi
+       bl      omap4_get_sar_ram_base
+       ldr     r9, [r0, #OMAP_TYPE_OFFSET]
+       cmp     r9, #0x1                        @ Check for HS device
+       bne     skip_secure_l2_clean_op
+       mov     r0, #1                          @ Clean secure L2
+       stmfd   r13!, {r4-r12, r14}
+       ldr     r12, =OMAP5_MON_CACHES_CLEAN_INDEX
+       DO_SMC
+       ldmfd   r13!, {r4-r12, r14}
+skip_secure_l2_clean_op:
+       mov     r0, #2                          @ Flush L2
+       bl      v7_flush_dcache_all
+
+do_wfi:
+       bl      omap_do_wfi
+
+       /*
+        * CPU is here when it failed to enter OFF/DORMANT or
+        * no low power state was attempted.
+        */
+       mrc     p15, 0, r0, c1, c0, 0
+       tst     r0, #(1 << 2)                   @ Check C bit enabled?
+       orreq   r0, r0, #(1 << 2)               @ Enable the C bit
+       mcreq   p15, 0, r0, c1, c0, 0
+       isb
+       mrc     p15, 0, r0, c1, c0, 1
+       tst     r0, #(1 << 6)                   @ Check SMP bit enabled?
+       orreq   r0, r0, #(1 << 6)
+       mcreq   p15, 0, r0, c1, c0, 1
+       isb
+       dsb
+       ldmfd   sp!, {r4-r12, pc}
+ENDPROC(omap5_finish_suspend)
+
+ENTRY(omap5_cpu_resume)
+#ifdef CONFIG_ARM_ERRATA_761171
+       /*
+        * Work around for errata for 761171. Streaming write that will not
+        * allocate in L2 could lead to data corruption.
+        */
+       mrc     p15, 0, r0, c0, c0, 0           @ read main ID register
+       and     r5, r0, #0x00f00000             @ variant
+       and     r6, r0, #0x0000000f             @ revision
+       orr     r6, r6, r5, lsr #20-4           @ combine variant and revision
+       cmp     r6, #0x03                       @ Present before r0p3
+       bgt     1f
+       mrc     p15, 0, r0, c1, c0, 1           @ Read Auxctrl
+       orr     r0, r0, #0x3 << 27              @ bits[28:27]-L1_mode3_threshold
+       ldr     r12, =OMAP5_MON_AUX_CTRL_INDEX
+       dsb
+       smc     #0
+       dsb
+1:
+#endif
+       mrc     p15, 1, r0, c15, c0, 0          @ Read L2 ACTLR
+       cmp     r0, #0x118                      @ Check if it is already set
+       beq     skip_sec_l2
+       ldr     r0, =0x118                      @ Setup L2 ACTLR = 0x118
+       ldr     r12, =OMAP5_MON_L2AUX_CTRL_INDEX
+       dsb
+       smc     #0
+       dsb
+skip_sec_l2:
+       b       cpu_resume                      @ Jump to generic resume
+ENDPROC(omap5_cpu_resume)
 #endif
 
 #ifndef CONFIG_OMAP4_ERRATA_I688
index f31d90774de0c61db145d0904ec3bcf071857d96..38c76cf9044ee5ddc15b23d2b3ccc05837b67c46 100644 (file)
 
 int omap_type(void);
 
+/*
+ * API to retrieve the OMAP sysboot value.
+ * NOTE: depending on the SoC the meaning of the bits
+ * OR bit-combinations might vary.
+ */
+u8 omap_get_sysboot_value(void);
+
 /*
  * omap_rev bits:
  * CPU id bits (0730, 1510, 1710, 2422...)     [31:16]
@@ -404,7 +411,9 @@ IS_OMAP_TYPE(3430, 0x3430)
 
 #define OMAP54XX_CLASS         0x54000054
 #define OMAP5430_REV_ES1_0     (OMAP54XX_CLASS | (0x30 << 16) | (0x10 << 8))
+#define OMAP5430_REV_ES2_0     (OMAP54XX_CLASS | (0x30 << 16) | (0x20 << 8))
 #define OMAP5432_REV_ES1_0     (OMAP54XX_CLASS | (0x32 << 16) | (0x10 << 8))
+#define OMAP5432_REV_ES2_0     (OMAP54XX_CLASS | (0x32 << 16) | (0x20 << 8))
 
 void omap2xxx_check_revision(void);
 void omap3xxx_check_revision(void);
index 0ff0f068bea85d0a671e00619e9f903069cc5cc0..6c50e4c7fca3b521a03af718c884663283a6b20e 100644 (file)
 
 #define OMAP2_SRAM_PUB_PA      (OMAP2_SRAM_PA + 0xf800)
 #define OMAP3_SRAM_PUB_PA       (OMAP3_SRAM_PA + 0x8000)
-#ifdef CONFIG_OMAP4_ERRATA_I688
-#define OMAP4_SRAM_PUB_PA      OMAP4_SRAM_PA
-#else
-#define OMAP4_SRAM_PUB_PA      (OMAP4_SRAM_PA + 0x4000)
-#endif
-#define OMAP5_SRAM_PA          0x40300000
-
 #define SRAM_BOOTLOADER_SZ     0x00
 
 #define OMAP24XX_VA_REQINFOPERM0       OMAP2_L3_IO_ADDRESS(0x68005048)
@@ -106,11 +99,15 @@ static void __init omap_detect_sram(void)
                                omap_sram_size = 0x8000; /* 32K */
                        }
                } else if (cpu_is_omap44xx()) {
-                       omap_sram_start = OMAP4_SRAM_PUB_PA;
-                       omap_sram_size = 0xa000; /* 40K */
+                       omap_sram_start = OMAP4_SRAM_START_PA;
+                       omap_sram_size = OMAP4_SRAM_SIZE; /* 56KB */
+                       omap_sram_size -= OMAP4_SRAM_HS_RESERVE;
+                       omap_sram_start += OMAP4_SRAM_HS_RESERVE;
                } else if (soc_is_omap54xx()) {
-                       omap_sram_start = OMAP5_SRAM_PA;
-                       omap_sram_size = SZ_128K; /* 128KB */
+                       omap_sram_start = OMAP4_SRAM_START_PA;
+                       omap_sram_size = OMAP5_SRAM_SIZE; /* 128KB */
+                       omap_sram_size -= OMAP5_SRAM_HS_RESERVE;
+                       omap_sram_start += OMAP5_SRAM_HS_RESERVE;
                } else {
                        omap_sram_start = OMAP2_SRAM_PUB_PA;
                        omap_sram_size = 0x800; /* 2K */
@@ -123,11 +120,15 @@ static void __init omap_detect_sram(void)
                        omap_sram_start = OMAP3_SRAM_PA;
                        omap_sram_size = 0x10000; /* 64K */
                } else if (cpu_is_omap44xx()) {
-                       omap_sram_start = OMAP4_SRAM_PA;
-                       omap_sram_size = 0xe000; /* 56K */
+                       omap_sram_start = OMAP4_SRAM_START_PA;
+                       omap_sram_size = OMAP4_SRAM_SIZE; /* 56K */
+                       omap_sram_size -= OMAP4_SRAM_GP_RESERVE;
+                       omap_sram_start += OMAP4_SRAM_GP_RESERVE;
                } else if (soc_is_omap54xx()) {
-                       omap_sram_start = OMAP5_SRAM_PA;
-                       omap_sram_size = SZ_128K; /* 128KB */
+                       omap_sram_start = OMAP4_SRAM_START_PA;
+                       omap_sram_size = OMAP5_SRAM_SIZE; /* 128KB */
+                       omap_sram_size -= OMAP5_SRAM_GP_RESERVE;
+                       omap_sram_start += OMAP5_SRAM_GP_RESERVE;
                } else {
                        omap_sram_start = OMAP2_SRAM_PA;
                        if (cpu_is_omap242x())
@@ -146,12 +147,10 @@ static void __init omap2_map_sram(void)
        int cached = 1;
 
 #ifdef CONFIG_OMAP4_ERRATA_I688
-       if (cpu_is_omap44xx()) {
-               omap_sram_start += PAGE_SIZE;
-               omap_sram_size -= SZ_16K;
-       }
+       if (cpu_is_omap44xx() || soc_is_omap54xx())
+               omap_sram_size -= OMAP4_ERRATA_I688_SIZE;
 #endif
-       if (cpu_is_omap34xx()) {
+       if (cpu_is_omap34xx() || soc_is_am33xx()) {
                /*
                 * SRAM must be marked as non-cached on OMAP3 since the
                 * CORE DPLL M2 divider change code (in SRAM) runs with the
@@ -282,10 +281,18 @@ static inline int omap34xx_sram_init(void)
 }
 #endif /* CONFIG_ARCH_OMAP3 */
 
+#ifdef CONFIG_SOC_AM33XX
 static inline int am33xx_sram_init(void)
 {
+       am33xx_push_sram_idle();
        return 0;
 }
+#else
+static inline int am33xx_sram_init(void)
+{
+       return 0;
+}
+#endif
 
 int __init omap_sram_init(void)
 {
index ca7277c2a9ee74541c7ed249662c438f740838c6..6476c38b3da93e792994d03ff9aa661e6aa75343 100644 (file)
@@ -62,8 +62,10 @@ extern unsigned long omap3_sram_configure_core_dpll_sz;
 
 #ifdef CONFIG_PM
 extern void omap_push_sram_idle(void);
+extern void am33xx_push_sram_idle(void);
 #else
 static inline void omap_push_sram_idle(void) {}
+static inline void am33xx_push_sram_idle(void) {}
 #endif /* CONFIG_PM */
 
 #endif /* __ASSEMBLY__ */
@@ -74,10 +76,48 @@ static inline void omap_push_sram_idle(void) {}
  */
 #define OMAP2_SRAM_PA          0x40200000
 #define OMAP3_SRAM_PA           0x40200000
+
+#define OMAP4_SRAM_START_PA    0x40300000
+#define OMAP4_SRAM_START_VA    0xfe400000
+/* OMAP4 has 56K */
+#define OMAP4_SRAM_SIZE                (SZ_64K - SZ_8K)
+/* OMAP5 has 128K */
+#define OMAP5_SRAM_SIZE                SZ_128K
+/* 16K GP, 52K HS(default) for secure world */
+#define OMAP4_SRAM_GP_RESERVE  SZ_16K
+#ifdef CONFIG_OMAP4_HS_SECURE_SRAM_SIZE
+#define OMAP4_SRAM_HS_RESERVE  (CONFIG_OMAP4_HS_SECURE_SRAM_SIZE * SZ_1K)
+#else
+#define OMAP4_SRAM_HS_RESERVE  (OMAP4_SRAM_SIZE - PAGE_SIZE)
+#endif
+#if OMAP4_SRAM_HS_RESERVE > OMAP4_SRAM_SIZE
+#error "SRAM HS secure size > sram!!"
+#endif
+
+/* 64K GP, 124K HS for secure world */
+#define OMAP5_SRAM_GP_RESERVE  SZ_64K
+#ifdef CONFIG_OMAP5_HS_SECURE_SRAM_SIZE
+#define OMAP5_SRAM_HS_RESERVE  (CONFIG_OMAP5_HS_SECURE_SRAM_SIZE * SZ_1K)
+#else
+#define OMAP5_SRAM_HS_RESERVE  (OMAP5_SRAM_SIZE - PAGE_SIZE)
+#endif
+#if OMAP5_SRAM_HS_RESERVE > OMAP5_SRAM_SIZE
+#error "SRAM HS secure size > sram!!"
+#endif
+
 #ifdef CONFIG_OMAP4_ERRATA_I688
-#define OMAP4_SRAM_PA          0x40304000
-#define OMAP4_SRAM_VA          0xfe404000
+#define        OMAP4_ERRATA_I688_SIZE          PAGE_SIZE
+#define OMAP4_I688_ADDR(pa, size)      \
+       (OMAP4_SRAM_START_##pa + size - OMAP4_ERRATA_I688_SIZE)
+#define OMAP4_ERRATA_I688_SRAM_PA      OMAP4_I688_ADDR(PA, OMAP4_SRAM_SIZE)
+#define OMAP4_ERRATA_I688_SRAM_VA      OMAP4_I688_ADDR(VA, OMAP4_SRAM_SIZE)
+#define OMAP5_ERRATA_I688_SRAM_PA      OMAP4_I688_ADDR(PA, OMAP5_SRAM_SIZE)
+#define OMAP5_ERRATA_I688_SRAM_VA      OMAP4_I688_ADDR(VA, OMAP4_SRAM_SIZE)
+#if OMAP5_SRAM_HS_RESERVE > (OMAP5_SRAM_SIZE - OMAP4_ERRATA_I688_SIZE)
+#error "SRAM HS secure size > sram with i688 WA!"
+#endif
 #else
-#define OMAP4_SRAM_PA          0x40300000
+#define OMAP4_ERRATA_I688_SIZE                 0
 #endif
+
 #define AM33XX_SRAM_PA         0x40300000
index b8ad6e632bb84d9ed3ede854cefeeebe6b4cc00c..30ad6f8e3d007841f78bb843da5f9b7f9a26bd5d 100644 (file)
@@ -62,6 +62,7 @@
 #define OMAP2_MPU_SOURCE       "sys_ck"
 #define OMAP3_MPU_SOURCE       OMAP2_MPU_SOURCE
 #define OMAP4_MPU_SOURCE       "sys_clkin_ck"
+#define OMAP5_MPU_SOURCE       "sys_clkin"
 #define OMAP2_32K_SOURCE       "func_32k_ck"
 #define OMAP3_32K_SOURCE       "omap_32k_fck"
 #define OMAP4_32K_SOURCE       "sys_32k_ck"
@@ -128,6 +129,36 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
        }
 }
 
+static void omap_clkevt_suspend(struct clock_event_device *unused)
+{
+       char name[10];
+       struct omap_hwmod *oh;
+
+       sprintf(name, "timer%d", clkev.id);
+       oh = omap_hwmod_lookup(name);
+       if (!oh)
+               return;
+
+       __omap_dm_timer_stop(&clkev, 1, clkev.rate);
+       omap_hwmod_idle(oh);
+}
+
+static void omap_clkevt_resume(struct clock_event_device *unused)
+{
+       char name[10];
+       struct omap_hwmod *oh;
+
+       sprintf(name, "timer%d", clkev.id);
+       oh = omap_hwmod_lookup(name);
+       if (!oh)
+               return;
+
+       omap_hwmod_enable(oh);
+       __omap_dm_timer_load_start(&clkev,
+                       OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
+       __omap_dm_timer_int_enable(&clkev, OMAP_TIMER_INT_OVERFLOW);
+}
+
 static struct clock_event_device clockevent_gpt = {
        .name           = "gp_timer",
        .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
@@ -135,6 +166,8 @@ static struct clock_event_device clockevent_gpt = {
        .rating         = 300,
        .set_next_event = omap2_gp_timer_set_next_event,
        .set_mode       = omap2_gp_timer_set_mode,
+       .suspend        = omap_clkevt_suspend,
+       .resume         = omap_clkevt_resume,
 };
 
 static struct property device_disabled = {
@@ -228,7 +261,7 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
        int r = 0;
 
        if (of_have_populated_dt()) {
-               np = omap_get_timer_dt(omap_timer_match, NULL);
+               np = omap_get_timer_dt(omap_timer_match, property);
                if (!np)
                        return -ENODEV;
 
@@ -319,6 +352,7 @@ static void __init omap2_gp_clockevent_init(int gptimer_id,
        int res;
 
        clkev.errata = omap_dm_timer_get_errata();
+       clkev.id = gptimer_id;
 
        /*
         * For clock-event timers we never read the timer counter and
@@ -494,7 +528,7 @@ static void __init realtime_counter_init(void)
                pr_err("%s: ioremap failed\n", __func__);
                return;
        }
-       sys_clk = clk_get(NULL, "sys_clkin_ck");
+       sys_clk = clk_get(NULL, OMAP5_MPU_SOURCE);
        if (IS_ERR(sys_clk)) {
                pr_err("%s: failed to get system clock handle\n", __func__);
                iounmap(base);
@@ -596,8 +630,8 @@ OMAP_SYS_TIMER(3_gp, gptimer);
 #endif /* CONFIG_ARCH_OMAP3 */
 
 #ifdef CONFIG_SOC_AM33XX
-OMAP_SYS_GP_TIMER_INIT(3_am33xx, 1, OMAP4_MPU_SOURCE, "ti,timer-alwon",
-                      2, OMAP4_MPU_SOURCE);
+OMAP_SYS_GP_TIMER_INIT(3_am33xx, 2, OMAP4_MPU_SOURCE, "ti,timer-non-wkup",
+                      1, OMAP4_MPU_SOURCE);
 OMAP_SYS_TIMER(3_am33xx, gptimer);
 #endif /* CONFIG_SOC_AM33XX */
 
@@ -634,7 +668,7 @@ OMAP_SYS_TIMER(4, local);
 
 #ifdef CONFIG_SOC_OMAP5
 OMAP_SYS_32K_TIMER_INIT(5, 1, OMAP4_32K_SOURCE, "ti,timer-alwon",
-                       2, OMAP4_MPU_SOURCE);
+                       2, OMAP5_MPU_SOURCE);
 static void __init omap5_realtime_timer_init(void)
 {
        int err;
index 2e44e8a228845284d8a6a8fc15b20653348785fe..940aad401279afa23681623c8ca2cd01818f149e 100644 (file)
 #define        USBHS_UHH_HWMODNAME     "usb_host_hs"
 #define USBHS_TLL_HWMODNAME    "usb_tll_hs"
 
-static struct usbhs_omap_platform_data         usbhs_data;
-static struct usbtll_omap_platform_data                usbtll_data;
-static struct ehci_hcd_omap_platform_data      ehci_data;
-static struct ohci_hcd_omap_platform_data      ohci_data;
-
 static struct omap_device_pm_latency omap_uhhtll_latency[] = {
          {
                .deactivate_func = omap_device_idle_hwmods,
@@ -485,32 +480,18 @@ void __init setup_4430ohci_io_mux(const enum usbhs_omap_port_mode *port_mode)
        }
 }
 
-void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
+void __init usbhs_init(struct usbhs_omap_platform_data *pdata)
 {
        struct omap_hwmod       *uhh_hwm, *tll_hwm;
        struct platform_device  *pdev;
        int                     bus_id = -1;
-       int                     i;
-
-       for (i = 0; i < OMAP3_HS_USB_PORTS; i++) {
-               usbhs_data.port_mode[i] = pdata->port_mode[i];
-               usbtll_data.port_mode[i] = pdata->port_mode[i];
-               ohci_data.port_mode[i] = pdata->port_mode[i];
-               ehci_data.port_mode[i] = pdata->port_mode[i];
-               ehci_data.reset_gpio_port[i] = pdata->reset_gpio_port[i];
-               ehci_data.regulator[i] = pdata->regulator[i];
-       }
-       ehci_data.phy_reset = pdata->phy_reset;
-       ohci_data.es2_compatibility = pdata->es2_compatibility;
-       usbhs_data.ehci_data = &ehci_data;
-       usbhs_data.ohci_data = &ohci_data;
 
        if (cpu_is_omap34xx()) {
                setup_ehci_io_mux(pdata->port_mode);
                setup_ohci_io_mux(pdata->port_mode);
 
                if (omap_rev() <= OMAP3430_REV_ES2_1)
-                       usbhs_data.single_ulpi_bypass = true;
+                       pdata->single_ulpi_bypass = true;
 
        } else if (cpu_is_omap44xx()) {
                setup_4430ehci_io_mux(pdata->port_mode);
@@ -530,7 +511,7 @@ void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
        }
 
        pdev = omap_device_build(OMAP_USBTLL_DEVICE, bus_id, tll_hwm,
-                               &usbtll_data, sizeof(usbtll_data),
+                               pdata, sizeof(*pdata),
                                omap_uhhtll_latency,
                                ARRAY_SIZE(omap_uhhtll_latency), false);
        if (IS_ERR(pdev)) {
@@ -540,7 +521,7 @@ void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
        }
 
        pdev = omap_device_build(OMAP_USBHS_DEVICE, bus_id, uhh_hwm,
-                               &usbhs_data, sizeof(usbhs_data),
+                               pdata, sizeof(*pdata),
                                omap_uhhtll_latency,
                                ARRAY_SIZE(omap_uhhtll_latency), false);
        if (IS_ERR(pdev)) {
@@ -552,7 +533,7 @@ void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
 
 #else
 
-void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
+void __init usbhs_init(struct usbhs_omap_platform_data *pdata)
 {
 }
 
index 7b33b375fe77d0f816268b0ce0e8f2f189203d3f..9d27e3f8a09c14c626035ae868e48e0a3ea7426f 100644 (file)
@@ -85,6 +85,9 @@ void __init usb_musb_init(struct omap_musb_board_data *musb_board_data)
        musb_plat.mode = board_data->mode;
        musb_plat.extvbus = board_data->extvbus;
 
+       if (cpu_is_omap44xx())
+               musb_plat.has_mailbox = true;
+
        if (soc_is_am35xx()) {
                oh_name = "am35x_otg_hs";
                name = "musb-am35x";
index 9b986ead7c45c64d4c34407f2ca80eacfc8a4861..3319f5cf47a3aade4bdf79ba9605e3cdc183ee6f 100644 (file)
 #define USBPHY_OTGSESSEND_EN   (1 << 20)
 #define USBPHY_DATA_POLARITY   (1 << 23)
 
-struct usbhs_omap_board_data {
-       enum usbhs_omap_port_mode       port_mode[OMAP3_HS_USB_PORTS];
-
-       /* have to be valid if phy_reset is true and portx is in phy mode */
-       int     reset_gpio_port[OMAP3_HS_USB_PORTS];
-
-       /* Set this to true for ES2.x silicon */
-       unsigned                        es2_compatibility:1;
-
-       unsigned                        phy_reset:1;
-
-       /*
-        * Regulators for USB PHYs.
-        * Each PHY can have a separate regulator.
-        */
-       struct regulator                *regulator[OMAP3_HS_USB_PORTS];
-};
-
 extern void usb_musb_init(struct omap_musb_board_data *board_data);
-extern void usbhs_init(const struct usbhs_omap_board_data *pdata);
+extern void usbhs_init(struct usbhs_omap_platform_data *pdata);
 
 extern void am35x_musb_reset(void);
 extern void am35x_musb_phy_power(u8 on);
index a0ce4f10ff13ea342de9ff713b8af5a15af652af..5998eed3a2d2d13943cda2059f5e54abc1ab4a76 100644 (file)
@@ -171,6 +171,7 @@ extern void omap2xxx_voltagedomains_init(void);
 extern void omap3xxx_voltagedomains_init(void);
 extern void am33xx_voltagedomains_init(void);
 extern void omap44xx_voltagedomains_init(void);
+extern void omap54xx_voltagedomains_init(void);
 
 struct voltagedomain *voltdm_lookup(const char *name);
 void voltdm_init(struct voltagedomain **voltdm_list);
diff --git a/arch/arm/mach-omap2/voltagedomains54xx_data.c b/arch/arm/mach-omap2/voltagedomains54xx_data.c
new file mode 100644 (file)
index 0000000..9384549
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * OMAP5 Voltage Management Routines
+ *
+ * Based on voltagedomains44xx_data.c
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/init.h>
+
+#include "common.h"
+
+#include "prm54xx.h"
+#include "voltage.h"
+#include "vc.h"
+#include "vp.h"
+
+static const struct omap_vfsm_instance omap5_vdd_mpu_vfsm = {
+       .voltsetup_reg = OMAP54XX_PRM_VOLTSETUP_MPU_RET_SLEEP_OFFSET,
+};
+
+static const struct omap_vfsm_instance omap5_vdd_mm_vfsm = {
+       .voltsetup_reg = OMAP54XX_PRM_VOLTSETUP_MM_RET_SLEEP_OFFSET,
+};
+
+static const struct omap_vfsm_instance omap5_vdd_core_vfsm = {
+       .voltsetup_reg = OMAP54XX_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET,
+};
+
+static struct voltagedomain omap5_voltdm_mpu = {
+       .name = "mpu",
+       .scalable = true,
+       .read = omap4_prm_vcvp_read,
+       .write = omap4_prm_vcvp_write,
+       .rmw = omap4_prm_vcvp_rmw,
+       .vc = &omap4_vc_mpu,
+       .vfsm = &omap5_vdd_mpu_vfsm,
+       .vp = &omap4_vp_mpu,
+};
+
+static struct voltagedomain omap5_voltdm_mm = {
+       .name = "mm",
+       .scalable = true,
+       .read = omap4_prm_vcvp_read,
+       .write = omap4_prm_vcvp_write,
+       .rmw = omap4_prm_vcvp_rmw,
+       .vc = &omap4_vc_iva,
+       .vfsm = &omap5_vdd_mm_vfsm,
+       .vp = &omap4_vp_iva,
+};
+
+static struct voltagedomain omap5_voltdm_core = {
+       .name = "core",
+       .scalable = true,
+       .read = omap4_prm_vcvp_read,
+       .write = omap4_prm_vcvp_write,
+       .rmw = omap4_prm_vcvp_rmw,
+       .vc = &omap4_vc_core,
+       .vfsm = &omap5_vdd_core_vfsm,
+       .vp = &omap4_vp_core,
+};
+
+static struct voltagedomain omap5_voltdm_wkup = {
+       .name = "wkup",
+};
+
+static struct voltagedomain *voltagedomains_omap5[] __initdata = {
+       &omap5_voltdm_mpu,
+       &omap5_voltdm_mm,
+       &omap5_voltdm_core,
+       &omap5_voltdm_wkup,
+       NULL,
+};
+
+static const char *sys_clk_name __initdata = "sys_clkin";
+
+void __init omap54xx_voltagedomains_init(void)
+{
+       struct voltagedomain *voltdm;
+       int i;
+
+       for (i = 0; voltdm = voltagedomains_omap5[i], voltdm; i++)
+               voltdm->sys_clk.name = sys_clk_name;
+
+       voltdm_init(voltagedomains_omap5);
+};
index 616cb87b61792544f470110cd6322b2d3ea9d914..69985b06c0dae83e5864bf76691599c6499e4e73 100644 (file)
@@ -53,17 +53,25 @@ static unsigned long ac97_reset_config[] = {
        GPIO95_AC97_nRESET,
 };
 
-void pxa27x_assert_ac97reset(int reset_gpio, int on)
+void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio)
 {
+       /*
+        * This helper function is used to work around a bug in the pxa27x's
+        * ac97 controller during a warm reset.  The configuration of the
+        * reset_gpio is changed as follows:
+        * to_gpio == true: configured to generic output gpio and driven high
+        * to_gpio == false: configured to ac97 controller alt fn AC97_nRESET
+        */
+
        if (reset_gpio == 113)
-               pxa2xx_mfp_config(on ? &ac97_reset_config[0] :
-                                      &ac97_reset_config[1], 1);
+               pxa2xx_mfp_config(to_gpio ? &ac97_reset_config[0] :
+                                 &ac97_reset_config[1], 1);
 
        if (reset_gpio == 95)
-               pxa2xx_mfp_config(on ? &ac97_reset_config[2] :
-                                      &ac97_reset_config[3], 1);
+               pxa2xx_mfp_config(to_gpio ? &ac97_reset_config[2] :
+                                 &ac97_reset_config[3], 1);
 }
-EXPORT_SYMBOL_GPL(pxa27x_assert_ac97reset);
+EXPORT_SYMBOL_GPL(pxa27x_configure_ac97reset);
 
 /* Crystal clock: 13MHz */
 #define BASE_CLK       13000000
index f2a7a1725596787bad96e191d6e096dce2110f37..a77f5214bbe82bbde9b0d9dbb70ddb4823480552 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/amba/pl080.h>
 
 #include <mach/dma.h>
 #include <mach/map.h>
@@ -30,7 +31,6 @@
 
 #include <mach/regs-sys.h>
 
-#include <asm/hardware/pl080.h>
 
 /* dma channel state information */
 
index 99ef190d0909d5ee5bf633efd0b660061244e71b..08294fa9e0d47a84b4f478298dba40b865dcf5ba 100644 (file)
@@ -657,14 +657,8 @@ static struct platform_device lcdc_device = {
 /* FSI */
 #define IRQ_FSI                evt2irq(0x1840)
 static struct sh_fsi_platform_info fsi_info = {
-       .port_a = {
-               .flags          = SH_FSI_BRS_INV,
-       },
        .port_b = {
-               .flags          = SH_FSI_BRS_INV |
-                                 SH_FSI_BRM_INV |
-                                 SH_FSI_LRS_INV |
-                                 SH_FSI_CLK_CPG |
+               .flags          = SH_FSI_CLK_CPG |
                                  SH_FSI_FMT_SPDIF,
        },
 };
@@ -692,21 +686,21 @@ static struct platform_device fsi_device = {
        },
 };
 
-static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = {
-       .fmt            = SND_SOC_DAIFMT_LEFT_J,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
-       .sysclk         = 11289600,
-};
-
 static struct asoc_simple_card_info fsi2_ak4643_info = {
        .name           = "AK4643",
        .card           = "FSI2A-AK4643",
-       .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0013",
        .platform       = "sh_fsi2",
-       .codec_dai      = "ak4642-hifi",
-       .init           = &fsi2_ak4643_init_info,
+       .daifmt         = SND_SOC_DAIFMT_LEFT_J,
+       .cpu_dai = {
+               .name   = "fsia-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS,
+       },
+       .codec_dai = {
+               .name   = "ak4642-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+               .sysclk = 11289600,
+       },
 };
 
 static struct platform_device fsi_ak4643_device = {
@@ -815,18 +809,18 @@ static struct platform_device lcdc1_device = {
        },
 };
 
-static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = {
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBM_CFM,
-};
-
 static struct asoc_simple_card_info fsi2_hdmi_info = {
        .name           = "HDMI",
        .card           = "FSI2B-HDMI",
-       .cpu_dai        = "fsib-dai",
        .codec          = "sh-mobile-hdmi",
        .platform       = "sh_fsi2",
-       .codec_dai      = "sh_mobile_hdmi-hifi",
-       .init           = &fsi2_hdmi_init_info,
+       .cpu_dai = {
+               .name   = "fsib-dai",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF,
+       },
+       .codec_dai = {
+               .name   = "sh_mobile_hdmi-hifi",
+       },
 };
 
 static struct platform_device fsi_hdmi_device = {
index 5353adf6b828a55446933e070968c9b4bec20a94..0679ca6bf1f67bfaf64c0d1d118a6c908b044655 100644 (file)
@@ -806,21 +806,21 @@ static struct platform_device fsi_device = {
 };
 
 /* FSI-WM8978 */
-static struct asoc_simple_dai_init_info fsi_wm8978_init_info = {
-       .fmt            = SND_SOC_DAIFMT_I2S,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_NB_NF,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
-       .sysclk         = 12288000,
-};
-
 static struct asoc_simple_card_info fsi_wm8978_info = {
        .name           = "wm8978",
        .card           = "FSI2A-WM8978",
-       .cpu_dai        = "fsia-dai",
        .codec          = "wm8978.0-001a",
        .platform       = "sh_fsi2",
-       .codec_dai      = "wm8978-hifi",
-       .init           = &fsi_wm8978_init_info,
+       .daifmt         = SND_SOC_DAIFMT_I2S,
+       .cpu_dai = {
+               .name   = "fsia-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF,
+       },
+       .codec_dai = {
+               .name   = "wm8978-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_NB_NF,
+               .sysclk = 12288000,
+       },
 };
 
 static struct platform_device fsi_wm8978_device = {
@@ -832,18 +832,18 @@ static struct platform_device fsi_wm8978_device = {
 };
 
 /* FSI-HDMI */
-static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = {
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBM_CFM,
-};
-
 static struct asoc_simple_card_info fsi2_hdmi_info = {
        .name           = "HDMI",
        .card           = "FSI2B-HDMI",
-       .cpu_dai        = "fsib-dai",
        .codec          = "sh-mobile-hdmi",
        .platform       = "sh_fsi2",
-       .codec_dai      = "sh_mobile_hdmi-hifi",
-       .init           = &fsi2_hdmi_init_info,
+       .cpu_dai = {
+               .name   = "fsib-dai",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+       },
+       .codec_dai = {
+               .name = "sh_mobile_hdmi-hifi",
+       },
 };
 
 static struct platform_device fsi_hdmi_device = {
index c02448d6847f40aba1d2906e9957e376aecd0df4..f41b71e8df3eb331eedd4816ea876cd3b10313c5 100644 (file)
@@ -525,21 +525,21 @@ static struct platform_device fsi_device = {
        },
 };
 
-static struct asoc_simple_dai_init_info fsi2_ak4648_init_info = {
-       .fmt            = SND_SOC_DAIFMT_LEFT_J,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
-       .sysclk         = 11289600,
-};
-
 static struct asoc_simple_card_info fsi2_ak4648_info = {
        .name           = "AK4648",
        .card           = "FSI2A-AK4648",
-       .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0012",
        .platform       = "sh_fsi2",
-       .codec_dai      = "ak4642-hifi",
-       .init           = &fsi2_ak4648_init_info,
+       .daifmt         = SND_SOC_DAIFMT_LEFT_J,
+       .cpu_dai = {
+               .name   = "fsia-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS,
+       },
+       .codec_dai = {
+               .name   = "ak4642-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+               .sysclk = 11289600,
+       },
 };
 
 static struct platform_device fsi_ak4648_device = {
index 2fed62f660459ccae4b08f8548543309587eb0fc..3fd716dae40544d33f11fe1f45556304446fe165 100644 (file)
@@ -502,18 +502,18 @@ static struct platform_device hdmi_lcdc_device = {
        },
 };
 
-static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = {
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBM_CFM,
-};
-
 static struct asoc_simple_card_info fsi2_hdmi_info = {
        .name           = "HDMI",
        .card           = "FSI2B-HDMI",
-       .cpu_dai        = "fsib-dai",
        .codec          = "sh-mobile-hdmi",
        .platform       = "sh_fsi2",
-       .codec_dai      = "sh_mobile_hdmi-hifi",
-       .init           = &fsi2_hdmi_init_info,
+       .cpu_dai = {
+               .name   = "fsib-dai",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF,
+       },
+       .codec_dai = {
+               .name   = "sh_mobile_hdmi-hifi",
+       },
 };
 
 static struct platform_device fsi_hdmi_device = {
@@ -858,16 +858,12 @@ static struct platform_device leds_device = {
 #define IRQ_FSI evt2irq(0x1840)
 static struct sh_fsi_platform_info fsi_info = {
        .port_a = {
-               .flags = SH_FSI_BRS_INV,
                .tx_id = SHDMA_SLAVE_FSIA_TX,
                .rx_id = SHDMA_SLAVE_FSIA_RX,
        },
        .port_b = {
-               .flags = SH_FSI_BRS_INV |
-                       SH_FSI_BRM_INV  |
-                       SH_FSI_LRS_INV  |
-                       SH_FSI_CLK_CPG  |
-                       SH_FSI_FMT_SPDIF,
+               .flags = SH_FSI_CLK_CPG |
+                        SH_FSI_FMT_SPDIF,
        }
 };
 
@@ -896,21 +892,21 @@ static struct platform_device fsi_device = {
        },
 };
 
-static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = {
-       .fmt            = SND_SOC_DAIFMT_LEFT_J,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
-       .sysclk         = 11289600,
-};
-
 static struct asoc_simple_card_info fsi2_ak4643_info = {
        .name           = "AK4643",
        .card           = "FSI2A-AK4643",
-       .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0013",
        .platform       = "sh_fsi2",
-       .codec_dai      = "ak4642-hifi",
-       .init           = &fsi2_ak4643_init_info,
+       .daifmt         = SND_SOC_DAIFMT_LEFT_J,
+       .cpu_dai = {
+               .name   = "fsia-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS,
+       },
+       .codec_dai = {
+               .name   = "ak4642-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+               .sysclk = 11289600,
+       },
 };
 
 static struct platform_device fsi_ak4643_device = {
index 7cfa6818865a144f2a8d8935eb88ebdb67a053d3..972a151df34cc2698133eb6abc13de98808b2ad1 100644 (file)
@@ -43,8 +43,6 @@
 #define VA_L2CC_BASE                           IOMEM(UL(0xFB000000))
 
 /* others */
-#define DMAC0_BASE                             UL(0xEA800000)
-#define DMAC1_BASE                             UL(0xEB000000)
 #define MCIF_CF_BASE                           UL(0xB2800000)
 
 /* Debug uart for linux, will be used for debug and uncompress messages */
index 02f4724bb0d487d4d6440e302ccd4cb0d0993301..ec72c47c0e085688edb9758b14fffcc0f4e31ddb 100644 (file)
@@ -36,7 +36,7 @@
 static struct arasan_cf_pdata cf_pdata = {
        .cf_if_clk = CF_IF_CLK_166M,
        .quirk = CF_BROKEN_UDMA,
-       .dma_priv = &cf_dma_priv,
+       .dma_priv = "cf",
 };
 
 /* ssp device registration */
@@ -47,10 +47,7 @@ static struct pl022_ssp_controller ssp1_plat_data = {
 /* Add SPEAr1310 auxdata to pass platform data */
 static struct of_dev_auxdata spear1310_auxdata_lookup[] __initdata = {
        OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_pdata),
-       OF_DEV_AUXDATA("snps,dma-spear1340", DMAC0_BASE, NULL, &dmac_plat_data),
-       OF_DEV_AUXDATA("snps,dma-spear1340", DMAC1_BASE, NULL, &dmac_plat_data),
        OF_DEV_AUXDATA("arm,pl022", SSP_BASE, NULL, &pl022_plat_data),
-
        OF_DEV_AUXDATA("arm,pl022", SPEAR1310_SSP1_BASE, NULL, &ssp1_plat_data),
        {}
 };
index 081014fb314a9e4b979a249c3271fdcd828bf9db..69c8f72a9ca29eaf5b74cc49539821ccfc2a39b9 100644 (file)
@@ -18,9 +18,9 @@
 #include <linux/delay.h>
 #include <linux/dw_dmac.h>
 #include <linux/of_platform.h>
+#include <linux/pata_arasan_cf_data.h>
 #include <asm/hardware/gic.h>
 #include <asm/mach/arch.h>
-#include <mach/dma.h>
 #include <mach/generic.h>
 #include <mach/spear.h>
 
                        (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
                        SPEAR1340_MIPHY_PLL_RATIO_TOP(25))
 
-static struct dw_dma_slave uart1_dma_param[] = {
-       {
-               /* Tx */
-               .cfg_hi = DWC_CFGH_DST_PER(SPEAR1340_DMA_REQ_UART1_TX),
-               .cfg_lo = 0,
-               .src_master = DMA_MASTER_MEMORY,
-               .dst_master = SPEAR1340_DMA_MASTER_UART1,
-       }, {
-               /* Rx */
-               .cfg_hi = DWC_CFGH_SRC_PER(SPEAR1340_DMA_REQ_UART1_RX),
-               .cfg_lo = 0,
-               .src_master = SPEAR1340_DMA_MASTER_UART1,
-               .dst_master = DMA_MASTER_MEMORY,
-       }
+static struct amba_pl011_data uart1_data = {
+       .dma_filter = dw_dma_generic_filter,
+       .dma_tx_param = "uart1_tx",
+       .dma_rx_param = "uart1_rx",
 };
 
-static struct amba_pl011_data uart1_data = {
-       .dma_filter = dw_dma_filter,
-       .dma_tx_param = &uart1_dma_param[0],
-       .dma_rx_param = &uart1_dma_param[1],
+static struct arasan_cf_pdata cf_pdata = {
+       .cf_if_clk = CF_IF_CLK_166M,
+       .quirk = CF_BROKEN_UDMA,
+       .dma_priv = "cf",
 };
 
 /* SATA device registration */
@@ -158,11 +148,8 @@ static struct ahci_platform_data sata_pdata = {
 
 /* Add SPEAr1340 auxdata to pass platform data */
 static struct of_dev_auxdata spear1340_auxdata_lookup[] __initdata = {
-       OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_dma_priv),
-       OF_DEV_AUXDATA("snps,dma-spear1340", DMAC0_BASE, NULL, &dmac_plat_data),
-       OF_DEV_AUXDATA("snps,dma-spear1340", DMAC1_BASE, NULL, &dmac_plat_data),
+       OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_pdata),
        OF_DEV_AUXDATA("arm,pl022", SSP_BASE, NULL, &pl022_plat_data),
-
        OF_DEV_AUXDATA("snps,spear-ahci", SPEAR1340_SATA_BASE, NULL,
                        &sata_pdata),
        OF_DEV_AUXDATA("arm,pl011", SPEAR1340_UART1_BASE, NULL, &uart1_data),
index c4af775a8451a6067f5c4c8ecb30ef1da2d902f8..b074db8b109c050fb311ec17241cdb5a74bc9e47 100644 (file)
 #include <asm/hardware/gic.h>
 #include <asm/mach/map.h>
 #include <asm/smp_twd.h>
-#include <mach/dma.h>
 #include <mach/generic.h>
 #include <mach/spear.h>
 
-/* common dw_dma filter routine to be used by peripherals */
-bool dw_dma_filter(struct dma_chan *chan, void *slave)
-{
-       struct dw_dma_slave *dws = (struct dw_dma_slave *)slave;
-
-       if (chan->device->dev == dws->dma_dev) {
-               chan->private = slave;
-               return true;
-       } else {
-               return false;
-       }
-}
-
 /* ssp device registration */
-static struct dw_dma_slave ssp_dma_param[] = {
-       {
-               /* Tx */
-               .cfg_hi = DWC_CFGH_DST_PER(DMA_REQ_SSP0_TX),
-               .cfg_lo = 0,
-               .src_master = DMA_MASTER_MEMORY,
-               .dst_master = DMA_MASTER_SSP0,
-       }, {
-               /* Rx */
-               .cfg_hi = DWC_CFGH_SRC_PER(DMA_REQ_SSP0_RX),
-               .cfg_lo = 0,
-               .src_master = DMA_MASTER_SSP0,
-               .dst_master = DMA_MASTER_MEMORY,
-       }
-};
-
 struct pl022_ssp_controller pl022_plat_data = {
        .enable_dma = 1,
-       .dma_filter = dw_dma_filter,
-       .dma_rx_param = &ssp_dma_param[1],
-       .dma_tx_param = &ssp_dma_param[0],
-};
-
-/* CF device registration */
-struct dw_dma_slave cf_dma_priv = {
-       .cfg_hi = 0,
-       .cfg_lo = 0,
-       .src_master = 0,
-       .dst_master = 0,
-};
-
-/* dmac device registeration */
-struct dw_dma_platform_data dmac_plat_data = {
-       .nr_channels = 8,
-       .chan_allocation_order = CHAN_ALLOCATION_DESCENDING,
-       .chan_priority = CHAN_PRIORITY_DESCENDING,
-       .block_size = 4095U,
-       .nr_masters = 2,
-       .data_width = { 3, 3, 0, 0 },
+       .dma_filter = dw_dma_generic_filter,
+       .dma_rx_param = "ssp0_rx",
+       .dma_tx_param = "ssp0_tx",
+       .num_chipselect = 3,
 };
 
 void __init spear13xx_l2x0_init(void)
index 38fe95db31a70a69c241699edf2b5022be8d698a..3d9b1b5e8ed9a2126e7130c24fd51b0e1fdf2e88 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/irqchip/spear-shirq.h>
 #include <linux/of_irq.h>
 #include <linux/io.h>
-#include <asm/hardware/pl080.h>
 #include <asm/hardware/vic.h>
 #include <plat/pl080.h>
 #include <mach/generic.h>
index 5a5a52db252bf79d6d776c4a2cbdbbf330ba9e43..8ce65a23b06e2d139d4d1f182fd47c197b8fe63f 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
-#include <asm/hardware/pl080.h>
+#include <linux/amba/pl080.h>
 #include <asm/hardware/vic.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
index ce328c7f5c94556cdda6021e8aef1b950aba5aa2..0c10c20bd241dd69c0c6e8d0ae778d6276d5aa53 100644 (file)
@@ -576,39 +576,60 @@ static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
        } while (pte++, addr += PAGE_SIZE, addr != end);
 }
 
-static void __init alloc_init_section(pud_t *pud, unsigned long addr,
-                                     unsigned long end, phys_addr_t phys,
-                                     const struct mem_type *type)
+static void __init map_init_section(pmd_t *pmd, unsigned long addr,
+                       unsigned long end, phys_addr_t phys,
+                       const struct mem_type *type)
 {
-       pmd_t *pmd = pmd_offset(pud, addr);
-
+#ifndef CONFIG_ARM_LPAE
        /*
-        * Try a section mapping - end, addr and phys must all be aligned
-        * to a section boundary.  Note that PMDs refer to the individual
-        * L1 entries, whereas PGDs refer to a group of L1 entries making
-        * up one logical pointer to an L2 table.
+        * In classic MMU format, puds and pmds are folded in to
+        * the pgds. pmd_offset gives the PGD entry. PGDs refer to a
+        * group of L1 entries making up one logical pointer to
+        * an L2 table (2MB), where as PMDs refer to the individual
+        * L1 entries (1MB). Hence increment to get the correct
+        * offset for odd 1MB sections.
+        * (See arch/arm/include/asm/pgtable-2level.h)
         */
-       if (type->prot_sect && ((addr | end | phys) & ~SECTION_MASK) == 0) {
-               pmd_t *p = pmd;
-
-#ifndef CONFIG_ARM_LPAE
-               if (addr & SECTION_SIZE)
-                       pmd++;
+       if (addr & SECTION_SIZE)
+               pmd++;
 #endif
+       do {
+               *pmd = __pmd(phys | type->prot_sect);
+               phys += SECTION_SIZE;
+       } while (pmd++, addr += SECTION_SIZE, addr != end);
 
-               do {
-                       *pmd = __pmd(phys | type->prot_sect);
-                       phys += SECTION_SIZE;
-               } while (pmd++, addr += SECTION_SIZE, addr != end);
+       flush_pmd_entry(pmd);
+}
 
-               flush_pmd_entry(p);
-       } else {
+static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
+                                     unsigned long end, phys_addr_t phys,
+                                     const struct mem_type *type)
+{
+       pmd_t *pmd = pmd_offset(pud, addr);
+       unsigned long next;
+
+       do {
                /*
-                * No need to loop; pte's aren't interested in the
-                * individual L1 entries.
+                * With LPAE, we must loop over to map
+                * all the pmds for the given range.
                 */
-               alloc_init_pte(pmd, addr, end, __phys_to_pfn(phys), type);
-       }
+               next = pmd_addr_end(addr, end);
+
+               /*
+                * Try a section mapping - addr, next and phys must all be
+                * aligned to a section boundary.
+                */
+               if (type->prot_sect &&
+                               ((addr | next | phys) & ~SECTION_MASK) == 0) {
+                       map_init_section(pmd, addr, next, phys, type);
+               } else {
+                       alloc_init_pte(pmd, addr, next,
+                                               __phys_to_pfn(phys), type);
+               }
+
+               phys += next - addr;
+
+       } while (pmd++, addr = next, addr != end);
 }
 
 static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
@@ -619,7 +640,7 @@ static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
 
        do {
                next = pud_addr_end(addr, end);
-               alloc_init_section(pud, addr, next, phys, type);
+               alloc_init_pmd(pud, addr, next, phys, type);
                phys += next - addr;
        } while (pud++, addr = next, addr != end);
 }
index 665870dce3c82bb09c1c10329f7bc876ce997ca3..23d429bbf73b708c083b12530ce52d2d64e8bc1c 100644 (file)
@@ -29,6 +29,7 @@ config ARCH_OMAP2PLUS
        select PINCTRL
        select PROC_DEVICETREE if PROC_FS
        select SPARSE_IRQ
+       select TI_PRIV_EDMA
        select USE_OF
        help
          "Systems based on OMAP2, OMAP3, OMAP4 or OMAP5"
@@ -116,22 +117,6 @@ config OMAP_MUX_WARNINGS
          to change the pin multiplexing setup.  When there are no warnings
          printed, it's safe to deselect OMAP_MUX for your product.
 
-config OMAP_MBOX_FWK
-       tristate "Mailbox framework support"
-       depends on ARCH_OMAP
-       help
-         Say Y here if you want to use OMAP Mailbox framework support for
-         DSP, IVA1.0 and IVA2 in OMAP1/2/3.
-
-config OMAP_MBOX_KFIFO_SIZE
-       int "Mailbox kfifo default buffer size (bytes)"
-       depends on OMAP_MBOX_FWK
-       default 256
-       help
-         Specify the default size of mailbox's kfifo buffers (bytes).
-         This can also be changed at runtime (via the mbox_kfifo_size
-         module parameter).
-
 config OMAP_IOMMU_IVA2
        bool
 
index a14a78a2f149fa76e0167af8ec420c8152fe744f..4077d23a8603af0525ce648940b71087ddfcfdd9 100644 (file)
@@ -15,6 +15,3 @@ obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o
 i2c-omap-$(CONFIG_I2C_OMAP) := i2c.o
 obj-y += $(i2c-omap-m) $(i2c-omap-y)
 
-# OMAP mailbox framework
-obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox.o
-
index 4136b20cba3cc257534900894a1a8767baab585e..3e6ede1c8e5a6d9883446ec1476237b1b5fe3ff1 100644 (file)
@@ -701,8 +701,8 @@ int omap_request_dma(int dev_id, const char *dev_name,
        for (ch = 0; ch < dma_chan_count; ch++) {
                if (free_ch == -1 && dma_chan[ch].dev_id == -1) {
                        free_ch = ch;
-                       if (dev_id == 0)
-                               break;
+                       /* Exit after first free channel found */
+                       break;
                }
        }
        if (free_ch == -1) {
@@ -894,11 +894,12 @@ void omap_start_dma(int lch)
                int next_lch, cur_lch;
                char dma_chan_link_map[MAX_LOGICAL_DMA_CH_COUNT];
 
-               dma_chan_link_map[lch] = 1;
                /* Set the link register of the first channel */
                enable_lnk(lch);
 
                memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map));
+               dma_chan_link_map[lch] = 1;
+
                cur_lch = dma_chan[lch].next_lch;
                do {
                        next_lch = dma_chan[cur_lch].next_lch;
diff --git a/arch/arm/plat-omap/include/plat/mailbox.h b/arch/arm/plat-omap/include/plat/mailbox.h
deleted file mode 100644 (file)
index cc3921e..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/* mailbox.h */
-
-#ifndef MAILBOX_H
-#define MAILBOX_H
-
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/kfifo.h>
-
-typedef u32 mbox_msg_t;
-struct omap_mbox;
-
-typedef int __bitwise omap_mbox_irq_t;
-#define IRQ_TX ((__force omap_mbox_irq_t) 1)
-#define IRQ_RX ((__force omap_mbox_irq_t) 2)
-
-typedef int __bitwise omap_mbox_type_t;
-#define OMAP_MBOX_TYPE1 ((__force omap_mbox_type_t) 1)
-#define OMAP_MBOX_TYPE2 ((__force omap_mbox_type_t) 2)
-
-struct omap_mbox_ops {
-       omap_mbox_type_t        type;
-       int             (*startup)(struct omap_mbox *mbox);
-       void            (*shutdown)(struct omap_mbox *mbox);
-       /* fifo */
-       mbox_msg_t      (*fifo_read)(struct omap_mbox *mbox);
-       void            (*fifo_write)(struct omap_mbox *mbox, mbox_msg_t msg);
-       int             (*fifo_empty)(struct omap_mbox *mbox);
-       int             (*fifo_full)(struct omap_mbox *mbox);
-       /* irq */
-       void            (*enable_irq)(struct omap_mbox *mbox,
-                                               omap_mbox_irq_t irq);
-       void            (*disable_irq)(struct omap_mbox *mbox,
-                                               omap_mbox_irq_t irq);
-       void            (*ack_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq);
-       int             (*is_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq);
-       /* ctx */
-       void            (*save_ctx)(struct omap_mbox *mbox);
-       void            (*restore_ctx)(struct omap_mbox *mbox);
-};
-
-struct omap_mbox_queue {
-       spinlock_t              lock;
-       struct kfifo            fifo;
-       struct work_struct      work;
-       struct tasklet_struct   tasklet;
-       struct omap_mbox        *mbox;
-       bool full;
-};
-
-struct omap_mbox {
-       char                    *name;
-       unsigned int            irq;
-       struct omap_mbox_queue  *txq, *rxq;
-       struct omap_mbox_ops    *ops;
-       struct device           *dev;
-       void                    *priv;
-       int                     use_count;
-       struct blocking_notifier_head   notifier;
-};
-
-int omap_mbox_msg_send(struct omap_mbox *, mbox_msg_t msg);
-void omap_mbox_init_seq(struct omap_mbox *);
-
-struct omap_mbox *omap_mbox_get(const char *, struct notifier_block *nb);
-void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb);
-
-int omap_mbox_register(struct device *parent, struct omap_mbox **);
-int omap_mbox_unregister(void);
-
-static inline void omap_mbox_save_ctx(struct omap_mbox *mbox)
-{
-       if (!mbox->ops->save_ctx) {
-               dev_err(mbox->dev, "%s:\tno save\n", __func__);
-               return;
-       }
-
-       mbox->ops->save_ctx(mbox);
-}
-
-static inline void omap_mbox_restore_ctx(struct omap_mbox *mbox)
-{
-       if (!mbox->ops->restore_ctx) {
-               dev_err(mbox->dev, "%s:\tno restore\n", __func__);
-               return;
-       }
-
-       mbox->ops->restore_ctx(mbox);
-}
-
-static inline void omap_mbox_enable_irq(struct omap_mbox *mbox,
-                                       omap_mbox_irq_t irq)
-{
-       mbox->ops->enable_irq(mbox, irq);
-}
-
-static inline void omap_mbox_disable_irq(struct omap_mbox *mbox,
-                                        omap_mbox_irq_t irq)
-{
-       mbox->ops->disable_irq(mbox, irq);
-}
-
-#endif /* MAILBOX_H */
diff --git a/arch/arm/plat-omap/include/plat/sata.h b/arch/arm/plat-omap/include/plat/sata.h
new file mode 100644 (file)
index 0000000..0c9ba83
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * sata.h - The ahci sata device init function prototype
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Keshava Munegowda <keshava_mgowda@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PLAT_ARCH_OMAP_SATA_H
+#define __PLAT_ARCH_OMAP_SATA_H
+
+#if IS_ENABLED(CONFIG_SATA_AHCI_PLATFORM)
+extern void omap_sata_init(void);
+#else
+static inline void omap_sata_init(void)
+{  }
+#endif
+
+#endif
diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c
deleted file mode 100644 (file)
index 42377ef..0000000
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * OMAP mailbox driver
- *
- * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved.
- *
- * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/kfifo.h>
-#include <linux/err.h>
-#include <linux/notifier.h>
-#include <linux/module.h>
-
-#include <plat/mailbox.h>
-
-static struct omap_mbox **mboxes;
-
-static int mbox_configured;
-static DEFINE_MUTEX(mbox_configured_lock);
-
-static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
-module_param(mbox_kfifo_size, uint, S_IRUGO);
-MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
-
-/* Mailbox FIFO handle functions */
-static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
-{
-       return mbox->ops->fifo_read(mbox);
-}
-static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
-{
-       mbox->ops->fifo_write(mbox, msg);
-}
-static inline int mbox_fifo_empty(struct omap_mbox *mbox)
-{
-       return mbox->ops->fifo_empty(mbox);
-}
-static inline int mbox_fifo_full(struct omap_mbox *mbox)
-{
-       return mbox->ops->fifo_full(mbox);
-}
-
-/* Mailbox IRQ handle functions */
-static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
-{
-       if (mbox->ops->ack_irq)
-               mbox->ops->ack_irq(mbox, irq);
-}
-static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
-{
-       return mbox->ops->is_irq(mbox, irq);
-}
-
-/*
- * message sender
- */
-static int __mbox_poll_for_space(struct omap_mbox *mbox)
-{
-       int ret = 0, i = 1000;
-
-       while (mbox_fifo_full(mbox)) {
-               if (mbox->ops->type == OMAP_MBOX_TYPE2)
-                       return -1;
-               if (--i == 0)
-                       return -1;
-               udelay(1);
-       }
-       return ret;
-}
-
-int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg)
-{
-       struct omap_mbox_queue *mq = mbox->txq;
-       int ret = 0, len;
-
-       spin_lock_bh(&mq->lock);
-
-       if (kfifo_avail(&mq->fifo) < sizeof(msg)) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       if (kfifo_is_empty(&mq->fifo) && !__mbox_poll_for_space(mbox)) {
-               mbox_fifo_write(mbox, msg);
-               goto out;
-       }
-
-       len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
-       WARN_ON(len != sizeof(msg));
-
-       tasklet_schedule(&mbox->txq->tasklet);
-
-out:
-       spin_unlock_bh(&mq->lock);
-       return ret;
-}
-EXPORT_SYMBOL(omap_mbox_msg_send);
-
-static void mbox_tx_tasklet(unsigned long tx_data)
-{
-       struct omap_mbox *mbox = (struct omap_mbox *)tx_data;
-       struct omap_mbox_queue *mq = mbox->txq;
-       mbox_msg_t msg;
-       int ret;
-
-       while (kfifo_len(&mq->fifo)) {
-               if (__mbox_poll_for_space(mbox)) {
-                       omap_mbox_enable_irq(mbox, IRQ_TX);
-                       break;
-               }
-
-               ret = kfifo_out(&mq->fifo, (unsigned char *)&msg,
-                                                               sizeof(msg));
-               WARN_ON(ret != sizeof(msg));
-
-               mbox_fifo_write(mbox, msg);
-       }
-}
-
-/*
- * Message receiver(workqueue)
- */
-static void mbox_rx_work(struct work_struct *work)
-{
-       struct omap_mbox_queue *mq =
-                       container_of(work, struct omap_mbox_queue, work);
-       mbox_msg_t msg;
-       int len;
-
-       while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
-               len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
-               WARN_ON(len != sizeof(msg));
-
-               blocking_notifier_call_chain(&mq->mbox->notifier, len,
-                                                               (void *)msg);
-               spin_lock_irq(&mq->lock);
-               if (mq->full) {
-                       mq->full = false;
-                       omap_mbox_enable_irq(mq->mbox, IRQ_RX);
-               }
-               spin_unlock_irq(&mq->lock);
-       }
-}
-
-/*
- * Mailbox interrupt handler
- */
-static void __mbox_tx_interrupt(struct omap_mbox *mbox)
-{
-       omap_mbox_disable_irq(mbox, IRQ_TX);
-       ack_mbox_irq(mbox, IRQ_TX);
-       tasklet_schedule(&mbox->txq->tasklet);
-}
-
-static void __mbox_rx_interrupt(struct omap_mbox *mbox)
-{
-       struct omap_mbox_queue *mq = mbox->rxq;
-       mbox_msg_t msg;
-       int len;
-
-       while (!mbox_fifo_empty(mbox)) {
-               if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) {
-                       omap_mbox_disable_irq(mbox, IRQ_RX);
-                       mq->full = true;
-                       goto nomem;
-               }
-
-               msg = mbox_fifo_read(mbox);
-
-               len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
-               WARN_ON(len != sizeof(msg));
-
-               if (mbox->ops->type == OMAP_MBOX_TYPE1)
-                       break;
-       }
-
-       /* no more messages in the fifo. clear IRQ source. */
-       ack_mbox_irq(mbox, IRQ_RX);
-nomem:
-       schedule_work(&mbox->rxq->work);
-}
-
-static irqreturn_t mbox_interrupt(int irq, void *p)
-{
-       struct omap_mbox *mbox = p;
-
-       if (is_mbox_irq(mbox, IRQ_TX))
-               __mbox_tx_interrupt(mbox);
-
-       if (is_mbox_irq(mbox, IRQ_RX))
-               __mbox_rx_interrupt(mbox);
-
-       return IRQ_HANDLED;
-}
-
-static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
-                                       void (*work) (struct work_struct *),
-                                       void (*tasklet)(unsigned long))
-{
-       struct omap_mbox_queue *mq;
-
-       mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL);
-       if (!mq)
-               return NULL;
-
-       spin_lock_init(&mq->lock);
-
-       if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL))
-               goto error;
-
-       if (work)
-               INIT_WORK(&mq->work, work);
-
-       if (tasklet)
-               tasklet_init(&mq->tasklet, tasklet, (unsigned long)mbox);
-       return mq;
-error:
-       kfree(mq);
-       return NULL;
-}
-
-static void mbox_queue_free(struct omap_mbox_queue *q)
-{
-       kfifo_free(&q->fifo);
-       kfree(q);
-}
-
-static int omap_mbox_startup(struct omap_mbox *mbox)
-{
-       int ret = 0;
-       struct omap_mbox_queue *mq;
-
-       mutex_lock(&mbox_configured_lock);
-       if (!mbox_configured++) {
-               if (likely(mbox->ops->startup)) {
-                       ret = mbox->ops->startup(mbox);
-                       if (unlikely(ret))
-                               goto fail_startup;
-               } else
-                       goto fail_startup;
-       }
-
-       if (!mbox->use_count++) {
-               ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
-                                                       mbox->name, mbox);
-               if (unlikely(ret)) {
-                       pr_err("failed to register mailbox interrupt:%d\n",
-                                                                       ret);
-                       goto fail_request_irq;
-               }
-               mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet);
-               if (!mq) {
-                       ret = -ENOMEM;
-                       goto fail_alloc_txq;
-               }
-               mbox->txq = mq;
-
-               mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL);
-               if (!mq) {
-                       ret = -ENOMEM;
-                       goto fail_alloc_rxq;
-               }
-               mbox->rxq = mq;
-               mq->mbox = mbox;
-
-               omap_mbox_enable_irq(mbox, IRQ_RX);
-       }
-       mutex_unlock(&mbox_configured_lock);
-       return 0;
-
-fail_alloc_rxq:
-       mbox_queue_free(mbox->txq);
-fail_alloc_txq:
-       free_irq(mbox->irq, mbox);
-fail_request_irq:
-       if (mbox->ops->shutdown)
-               mbox->ops->shutdown(mbox);
-       mbox->use_count--;
-fail_startup:
-       mbox_configured--;
-       mutex_unlock(&mbox_configured_lock);
-       return ret;
-}
-
-static void omap_mbox_fini(struct omap_mbox *mbox)
-{
-       mutex_lock(&mbox_configured_lock);
-
-       if (!--mbox->use_count) {
-               omap_mbox_disable_irq(mbox, IRQ_RX);
-               free_irq(mbox->irq, mbox);
-               tasklet_kill(&mbox->txq->tasklet);
-               flush_work(&mbox->rxq->work);
-               mbox_queue_free(mbox->txq);
-               mbox_queue_free(mbox->rxq);
-       }
-
-       if (likely(mbox->ops->shutdown)) {
-               if (!--mbox_configured)
-                       mbox->ops->shutdown(mbox);
-       }
-
-       mutex_unlock(&mbox_configured_lock);
-}
-
-struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb)
-{
-       struct omap_mbox *_mbox, *mbox = NULL;
-       int i, ret;
-
-       if (!mboxes)
-               return ERR_PTR(-EINVAL);
-
-       for (i = 0; (_mbox = mboxes[i]); i++) {
-               if (!strcmp(_mbox->name, name)) {
-                       mbox = _mbox;
-                       break;
-               }
-       }
-
-       if (!mbox)
-               return ERR_PTR(-ENOENT);
-
-       if (nb)
-               blocking_notifier_chain_register(&mbox->notifier, nb);
-
-       ret = omap_mbox_startup(mbox);
-       if (ret) {
-               blocking_notifier_chain_unregister(&mbox->notifier, nb);
-               return ERR_PTR(-ENODEV);
-       }
-
-       return mbox;
-}
-EXPORT_SYMBOL(omap_mbox_get);
-
-void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb)
-{
-       blocking_notifier_chain_unregister(&mbox->notifier, nb);
-       omap_mbox_fini(mbox);
-}
-EXPORT_SYMBOL(omap_mbox_put);
-
-static struct class omap_mbox_class = { .name = "mbox", };
-
-int omap_mbox_register(struct device *parent, struct omap_mbox **list)
-{
-       int ret;
-       int i;
-
-       mboxes = list;
-       if (!mboxes)
-               return -EINVAL;
-
-       for (i = 0; mboxes[i]; i++) {
-               struct omap_mbox *mbox = mboxes[i];
-               mbox->dev = device_create(&omap_mbox_class,
-                               parent, 0, mbox, "%s", mbox->name);
-               if (IS_ERR(mbox->dev)) {
-                       ret = PTR_ERR(mbox->dev);
-                       goto err_out;
-               }
-
-               BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier);
-       }
-       return 0;
-
-err_out:
-       while (i--)
-               device_unregister(mboxes[i]->dev);
-       return ret;
-}
-EXPORT_SYMBOL(omap_mbox_register);
-
-int omap_mbox_unregister(void)
-{
-       int i;
-
-       if (!mboxes)
-               return -EINVAL;
-
-       for (i = 0; mboxes[i]; i++)
-               device_unregister(mboxes[i]->dev);
-       mboxes = NULL;
-       return 0;
-}
-EXPORT_SYMBOL(omap_mbox_unregister);
-
-static int __init omap_mbox_init(void)
-{
-       int err;
-
-       err = class_register(&omap_mbox_class);
-       if (err)
-               return err;
-
-       /* kfifo size sanity check: alignment and minimal size */
-       mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t));
-       mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size,
-                                                       sizeof(mbox_msg_t));
-
-       return 0;
-}
-subsys_initcall(omap_mbox_init);
-
-static void __exit omap_mbox_exit(void)
-{
-       class_unregister(&omap_mbox_class);
-}
-module_exit(omap_mbox_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging");
-MODULE_AUTHOR("Toshihiro Kobayashi");
-MODULE_AUTHOR("Hiroshi DOYU");
index 57981e4fe2bc94c143070ee99ecf66d09ceae223..b8ef965705cf6ca4b7ceb890528d63e5236fb34e 100644 (file)
@@ -62,7 +62,7 @@ static int __init uart8250_init_bcma(void)
 
                p->mapbase = (unsigned int) bcma_port->regs;
                p->membase = (void *) bcma_port->regs;
-               p->irq = bcma_port->irq + 2;
+               p->irq = bcma_port->irq;
                p->uartclk = bcma_port->baud_base;
                p->regshift = bcma_port->reg_shift;
                p->iotype = UPIO_MEM;
index a0fa5791cd44abe9b29784e26142338c11768c1d..aaff7671101b301cedc1e30edf2055fc6aa008c8 100644 (file)
@@ -887,12 +887,6 @@ static struct platform_device camera_devices[] = {
 };
 
 /* FSI */
-static struct sh_fsi_platform_info fsi_info = {
-       .port_b = {
-               .flags = SH_FSI_BRS_INV,
-       },
-};
-
 static struct resource fsi_resources[] = {
        [0] = {
                .name   = "FSI",
@@ -911,25 +905,22 @@ static struct platform_device fsi_device = {
        .id             = 0,
        .num_resources  = ARRAY_SIZE(fsi_resources),
        .resource       = fsi_resources,
-       .dev    = {
-               .platform_data  = &fsi_info,
-       },
-};
-
-static struct asoc_simple_dai_init_info fsi_da7210_init_info = {
-       .fmt            = SND_SOC_DAIFMT_I2S,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
 };
 
 static struct asoc_simple_card_info fsi_da7210_info = {
        .name           = "DA7210",
        .card           = "FSIB-DA7210",
-       .cpu_dai        = "fsib-dai",
        .codec          = "da7210.0-001a",
        .platform       = "sh_fsi.0",
-       .codec_dai      = "da7210-hifi",
-       .init           = &fsi_da7210_init_info,
+       .daifmt         = SND_SOC_DAIFMT_I2S,
+       .cpu_dai = {
+               .name   = "fsib-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF,
+       },
+       .codec_dai = {
+               .name   = "da7210-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+       },
 };
 
 static struct platform_device fsi_da7210_device = {
index 35f6efa3ac0e60c8f1842ef36629e3356622d0c9..4010e63e82d8491422457d32459a8d216455d4ef 100644 (file)
@@ -279,12 +279,6 @@ static struct platform_device ceu1_device = {
 
 /* FSI */
 /* change J20, J21, J22 pin to 1-2 connection to use slave mode */
-static struct sh_fsi_platform_info fsi_info = {
-       .port_a = {
-               .flags = SH_FSI_BRS_INV,
-       },
-};
-
 static struct resource fsi_resources[] = {
        [0] = {
                .name   = "FSI",
@@ -303,26 +297,23 @@ static struct platform_device fsi_device = {
        .id             = 0,
        .num_resources  = ARRAY_SIZE(fsi_resources),
        .resource       = fsi_resources,
-       .dev    = {
-               .platform_data  = &fsi_info,
-       },
-};
-
-static struct asoc_simple_dai_init_info fsi2_ak4642_init_info = {
-       .fmt            = SND_SOC_DAIFMT_LEFT_J,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
-       .sysclk         = 11289600,
 };
 
 static struct asoc_simple_card_info fsi_ak4642_info = {
        .name           = "AK4642",
        .card           = "FSIA-AK4642",
-       .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0012",
        .platform       = "sh_fsi.0",
-       .codec_dai      = "ak4642-hifi",
-       .init           = &fsi2_ak4642_init_info,
+       .daifmt         = SND_SOC_DAIFMT_LEFT_J,
+       .cpu_dai = {
+               .name   = "fsia-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF,
+       },
+       .codec_dai = {
+               .name   = "ak4642-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+               .sysclk = 11289600,
+       },
 };
 
 static struct platform_device fsi_ak4642_device = {
index 361b5e8239bc307d29882907744423e0b846d85d..9e62feffb374536995e08357866df9787635b19e 100644 (file)
@@ -67,6 +67,12 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset,
 
                tx = device->device_prep_dma_memcpy(chan, dma_dest, dma_src,
                                                    len, dma_prep_flags);
+               if (!tx) {
+                       dma_unmap_page(device->dev, dma_dest, len,
+                                      DMA_FROM_DEVICE);
+                       dma_unmap_page(device->dev, dma_src, len,
+                                      DMA_TO_DEVICE);
+               }
        }
 
        if (tx) {
index 58e4a8752aee52c06681bac43055f53586e3b3e1..05a4d1e001487bb9941c561f001a3e90042fdbc5 100644 (file)
@@ -25,6 +25,7 @@
  */
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
+#include <linux/module.h>
 #include <linux/mm.h>
 #include <linux/dma-mapping.h>
 #include <linux/async_tx.h>
index 842120979374c9cfc0fd6810e577a3720d2c45ff..7be34248b450896cfc056d1709513df4f172f79a 100644 (file)
@@ -128,8 +128,8 @@ async_tx_channel_switch(struct dma_async_tx_descriptor *depend_tx,
                }
                device->device_issue_pending(chan);
        } else {
-               if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR)
-                       panic("%s: DMA_ERROR waiting for depend_tx\n",
+               if (dma_wait_for_async_tx(depend_tx) != DMA_SUCCESS)
+                       panic("%s: DMA error waiting for depend_tx\n",
                              __func__);
                tx->tx_submit(tx);
        }
@@ -280,8 +280,9 @@ void async_tx_quiesce(struct dma_async_tx_descriptor **tx)
                 * we are referring to the correct operation
                 */
                BUG_ON(async_tx_test_ack(*tx));
-               if (dma_wait_for_async_tx(*tx) == DMA_ERROR)
-                       panic("DMA_ERROR waiting for transaction\n");
+               if (dma_wait_for_async_tx(*tx) != DMA_SUCCESS)
+                       panic("%s: DMA error waiting for transaction\n",
+                             __func__);
                async_tx_ack(*tx);
                *tx = NULL;
        }
index 154cc84381c23d07374a4a411bd4f4a7146606f5..8ade0a0481c67149e72d7878deb306a0fd33e5c2 100644 (file)
@@ -230,9 +230,7 @@ EXPORT_SYMBOL_GPL(async_xor);
 
 static int page_is_zero(struct page *p, unsigned int offset, size_t len)
 {
-       char *a = page_address(p) + offset;
-       return ((*(u32 *) a) == 0 &&
-               memcmp(a, a + 4, len - 4) == 0);
+       return !memchr_inv(page_address(p) + offset, 0, len);
 }
 
 static inline struct dma_chan *
index f5fb0722a63a14c9155b58a86f69c82e6efa8d92..2b4e89ba15adafe1f6105c2dd06d5cfe0beaa8d1 100644 (file)
@@ -134,6 +134,8 @@ source "drivers/hwspinlock/Kconfig"
 
 source "drivers/clocksource/Kconfig"
 
+source "drivers/mailbox/Kconfig"
+
 source "drivers/iommu/Kconfig"
 
 source "drivers/remoteproc/Kconfig"
index 7863b9fee50bbc33793a2acb2f104c34c22b2bd0..7e14f8e2873670a33b9f49c62f6d3dfad644899e 100644 (file)
@@ -132,6 +132,7 @@ obj-y                               += clk/
 
 obj-$(CONFIG_HWSPINLOCK)       += hwspinlock/
 obj-$(CONFIG_NFC)              += nfc/
+obj-$(CONFIG_MAILBOX)          += mailbox/
 obj-$(CONFIG_IOMMU_SUPPORT)    += iommu/
 obj-$(CONFIG_REMOTEPROC)       += remoteproc/
 obj-$(CONFIG_RPMSG)            += rpmsg/
index 7a8a2841fe64a93effec85f1ff5fc6dc4c610d06..747b8c0b5011add977a9a1b9d232998831f87ed3 100644 (file)
@@ -287,7 +287,9 @@ static int ahci_resume(struct device *dev)
        struct ahci_platform_data *pdata = dev_get_platdata(dev);
        struct ata_host *host = dev_get_drvdata(dev);
        struct ahci_host_priv *hpriv = host->private_data;
-       int rc;
+       struct ata_link *link;
+       unsigned int class;
+       int rc, i;
 
        if (!IS_ERR(hpriv->clk)) {
                rc = clk_prepare_enable(hpriv->clk);
@@ -309,6 +311,37 @@ static int ahci_resume(struct device *dev)
                        goto disable_unprepare_clk;
 
                ahci_init_controller(host);
+
+               /*
+                * Do a reset for OMAP5
+                * This step is specific to OMAP5 ES1.0 as OMAPS00265590
+                * and the hw bug CDDS id: COBRA-1.0BUG00044
+                *
+                * Bug Description:
+                * ----------------
+                * If SATA dpll unlocked during suspend, then during resume,
+                * we may encounter 2 different problems:
+                * 1. OOB communication is taking place, but we have no D2H
+                * FIS returned by the device. Port 0 TFD register contains
+                * 0x80. In this case the device keeps sending X_RDY, but the
+                * host does not acknowledge with R_RDY and sends SYNC instead
+                * (relevant screenshot attached).
+                * 2. D2H FIS returned containing status 0x50, TFD contains
+                * 0x150, but when we send the IDENTIFY_DEVICE command we get
+                * 0x1D0 in TFD, without any FIS having been actually sent on
+                * the bus. In this case there is only SYNC continued (implied
+                * SYNC after CONT) on the bus from both sides.
+                *
+                * Workaround:
+                * Issue a software reset (two ATA commands in succession)
+                * to the device before IDENTIFY_DEVICE.
+                */
+               for (i = 0; i < host->n_ports; i++) {
+                       ahci_port_resume(host->ports[i]);
+                       link = &host->ports[i]->link;
+                       ahci_do_softreset(link, &class,  sata_srst_pmp(link),
+                                         1000, ahci_check_ready);
+               }
        }
 
        ata_host_resume(host);
index 61d3e1b4069487c700b18f210601f628917f1ec4..b59861520a12a7a25727b76c38701c30202e3db7 100644 (file)
@@ -135,7 +135,7 @@ static bool driver_deferred_probe_enable = false;
  * list and schedules the deferred probe workqueue to process them.  It
  * should be called anytime a driver is successfully bound to a device.
  */
-static void driver_deferred_probe_trigger(void)
+void driver_deferred_probe_trigger(void)
 {
        if (!driver_deferred_probe_enable)
                return;
index cb0c4548857282c3bc6b38cfa836ad9474370604..79595a001204b1b5ef19d2a3c7930516ede4ce08 100644 (file)
@@ -31,6 +31,8 @@ int __init bcma_bus_early_register(struct bcma_bus *bus,
 int bcma_bus_suspend(struct bcma_bus *bus);
 int bcma_bus_resume(struct bcma_bus *bus);
 #endif
+struct bcma_device *bcma_find_core_unit(struct bcma_bus *bus, u16 coreid,
+                                       u8 unit);
 
 /* scan.c */
 int bcma_bus_scan(struct bcma_bus *bus);
@@ -45,6 +47,7 @@ int bcma_sprom_get(struct bcma_bus *bus);
 /* driver_chipcommon.c */
 #ifdef CONFIG_BCMA_DRIVER_MIPS
 void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
+extern struct platform_device bcma_pflash_dev;
 #endif /* CONFIG_BCMA_DRIVER_MIPS */
 
 /* driver_chipcommon_pmu.c */
index e461ad25fda4825dbc36a632d5781015843eb268..28fa50ad87bee80630758b2c5ace883d02ce2c0d 100644 (file)
@@ -329,7 +329,7 @@ void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
                return;
        }
 
-       irq = bcma_core_mips_irq(cc->core);
+       irq = bcma_core_irq(cc->core);
 
        /* Determine the registers of the UARTs */
        cc->nr_serial_ports = (cc->capabilities & BCMA_CC_CAP_NRUART);
index 1f0b83e18f6827f5003669b4015c2a89b2d369c5..d4f699aef8c440081d0f5847ab67766b6e128f48 100644 (file)
@@ -5,11 +5,11 @@
  * Licensed under the GNU/GPL. See COPYING for details.
  */
 
+#include "bcma_private.h"
+
 #include <linux/platform_device.h>
 #include <linux/bcma/bcma.h>
 
-#include "bcma_private.h"
-
 struct platform_device bcma_nflash_dev = {
        .name           = "bcma_nflash",
        .num_resources  = 0,
index 1e694db4532dd7aa41f95fc207b5bb0adea1556b..e6ed4fe5dcedc50a758db62a5ca8a1d0c30d3b38 100644 (file)
@@ -5,11 +5,11 @@
  * Licensed under the GNU/GPL. See COPYING for details.
  */
 
+#include "bcma_private.h"
+
 #include <linux/platform_device.h>
 #include <linux/bcma/bcma.h>
 
-#include "bcma_private.h"
-
 static struct resource bcma_sflash_resource = {
        .name   = "bcma_sflash",
        .start  = BCMA_SOC_FLASH2,
index 71f755c06fc6637497665f31d15f6432d49c3075..45f0996a375231be24109df39f89d1ee707e1f21 100644 (file)
@@ -73,6 +73,16 @@ static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio)
        bcma_chipco_gpio_pullup(cc, 1 << gpio, 0);
 }
 
+static int bcma_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+       struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+       if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
+               return bcma_core_irq(cc->core);
+       else
+               return -EINVAL;
+}
+
 int bcma_gpio_init(struct bcma_drv_cc *cc)
 {
        struct gpio_chip *chip = &cc->gpio;
@@ -85,6 +95,7 @@ int bcma_gpio_init(struct bcma_drv_cc *cc)
        chip->set               = bcma_gpio_set_value;
        chip->direction_input   = bcma_gpio_direction_input;
        chip->direction_output  = bcma_gpio_direction_output;
+       chip->to_irq            = bcma_gpio_to_irq;
        chip->ngpio             = 16;
        /* There is just one SoC in one device and its GPIO addresses should be
         * deterministic to address them more easily. The other buses could get
index 792daad28cbc6fa883a2e20911b33dbe840a49ba..9a7f0e3ab5a33fbb012a5c2f74145cc47c346dcd 100644 (file)
 
 #include <linux/bcma/bcma.h>
 
+#include <linux/mtd/physmap.h>
+#include <linux/platform_device.h>
 #include <linux/serial.h>
 #include <linux/serial_core.h>
 #include <linux/serial_reg.h>
 #include <linux/time.h>
 
+static const char *part_probes[] = { "bcm47xxpart", NULL };
+
+static struct physmap_flash_data bcma_pflash_data = {
+       .part_probe_types       = part_probes,
+};
+
+static struct resource bcma_pflash_resource = {
+       .name   = "bcma_pflash",
+       .flags  = IORESOURCE_MEM,
+};
+
+struct platform_device bcma_pflash_dev = {
+       .name           = "physmap-flash",
+       .dev            = {
+               .platform_data  = &bcma_pflash_data,
+       },
+       .resource       = &bcma_pflash_resource,
+       .num_resources  = 1,
+};
+
 /* The 47162a0 hangs when reading MIPS DMP registers registers */
 static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev)
 {
@@ -74,28 +96,41 @@ static u32 bcma_core_mips_irqflag(struct bcma_device *dev)
                return dev->core_index;
        flag = bcma_aread32(dev, BCMA_MIPS_OOBSELOUTA30);
 
-       return flag & 0x1F;
+       if (flag)
+               return flag & 0x1F;
+       else
+               return 0x3f;
 }
 
 /* Get the MIPS IRQ assignment for a specified device.
  * If unassigned, 0 is returned.
+ * If disabled, 5 is returned.
+ * If not supported, 6 is returned.
  */
-unsigned int bcma_core_mips_irq(struct bcma_device *dev)
+static unsigned int bcma_core_mips_irq(struct bcma_device *dev)
 {
        struct bcma_device *mdev = dev->bus->drv_mips.core;
        u32 irqflag;
        unsigned int irq;
 
        irqflag = bcma_core_mips_irqflag(dev);
+       if (irqflag == 0x3f)
+               return 6;
 
-       for (irq = 1; irq <= 4; irq++)
+       for (irq = 0; irq <= 4; irq++)
                if (bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq)) &
                    (1 << irqflag))
                        return irq;
 
-       return 0;
+       return 5;
+}
+
+unsigned int bcma_core_irq(struct bcma_device *dev)
+{
+       unsigned int mips_irq = bcma_core_mips_irq(dev);
+       return mips_irq <= 4 ? mips_irq + 2 : 0;
 }
-EXPORT_SYMBOL(bcma_core_mips_irq);
+EXPORT_SYMBOL(bcma_core_irq);
 
 static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
 {
@@ -114,7 +149,7 @@ static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
                bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0),
                            bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) &
                            ~(1 << irqflag));
-       else
+       else if (oldirq != 5)
                bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(oldirq), 0);
 
        /* assign the new one */
@@ -123,9 +158,9 @@ static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
                            bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) |
                            (1 << irqflag));
        } else {
-               u32 oldirqflag = bcma_read32(mdev,
-                                            BCMA_MIPS_MIPS74K_INTMASK(irq));
-               if (oldirqflag) {
+               u32 irqinitmask = bcma_read32(mdev,
+                                             BCMA_MIPS_MIPS74K_INTMASK(irq));
+               if (irqinitmask) {
                        struct bcma_device *core;
 
                        /* backplane irq line is in use, find out who uses
@@ -133,7 +168,7 @@ static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
                         */
                        list_for_each_entry(core, &bus->cores, list) {
                                if ((1 << bcma_core_mips_irqflag(core)) ==
-                                   oldirqflag) {
+                                   irqinitmask) {
                                        bcma_core_mips_set_irq(core, 0);
                                        break;
                                }
@@ -143,15 +178,31 @@ static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
                             1 << irqflag);
        }
 
-       bcma_info(bus, "set_irq: core 0x%04x, irq %d => %d\n",
-                 dev->id.id, oldirq + 2, irq + 2);
+       bcma_debug(bus, "set_irq: core 0x%04x, irq %d => %d\n",
+                  dev->id.id, oldirq <= 4 ? oldirq + 2 : 0, irq + 2);
+}
+
+static void bcma_core_mips_set_irq_name(struct bcma_bus *bus, unsigned int irq,
+                                       u16 coreid, u8 unit)
+{
+       struct bcma_device *core;
+
+       core = bcma_find_core_unit(bus, coreid, unit);
+       if (!core) {
+               bcma_warn(bus,
+                         "Can not find core (id: 0x%x, unit %i) for IRQ configuration.\n",
+                         coreid, unit);
+               return;
+       }
+
+       bcma_core_mips_set_irq(core, irq);
 }
 
 static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq)
 {
        int i;
        static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"};
-       printk(KERN_INFO KBUILD_MODNAME ": core 0x%04x, irq :", dev->id.id);
+       printk(KERN_DEBUG KBUILD_MODNAME ": core 0x%04x, irq :", dev->id.id);
        for (i = 0; i <= 6; i++)
                printk(" %s%s", irq_name[i], i == irq ? "*" : " ");
        printk("\n");
@@ -182,6 +233,7 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
 {
        struct bcma_bus *bus = mcore->core->bus;
        struct bcma_drv_cc *cc = &bus->drv_cc;
+       struct bcma_pflash *pflash = &cc->pflash;
 
        switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
        case BCMA_CC_FLASHT_STSER:
@@ -191,15 +243,20 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
                break;
        case BCMA_CC_FLASHT_PARA:
                bcma_debug(bus, "Found parallel flash\n");
-               cc->pflash.present = true;
-               cc->pflash.window = BCMA_SOC_FLASH2;
-               cc->pflash.window_size = BCMA_SOC_FLASH2_SZ;
+               pflash->present = true;
+               pflash->window = BCMA_SOC_FLASH2;
+               pflash->window_size = BCMA_SOC_FLASH2_SZ;
 
                if ((bcma_read32(cc->core, BCMA_CC_FLASH_CFG) &
                     BCMA_CC_FLASH_CFG_DS) == 0)
-                       cc->pflash.buswidth = 1;
+                       pflash->buswidth = 1;
                else
-                       cc->pflash.buswidth = 2;
+                       pflash->buswidth = 2;
+
+               bcma_pflash_data.width = pflash->buswidth;
+               bcma_pflash_resource.start = pflash->window;
+               bcma_pflash_resource.end = pflash->window + pflash->window_size;
+
                break;
        default:
                bcma_err(bus, "Flash type not supported\n");
@@ -227,6 +284,32 @@ void bcma_core_mips_early_init(struct bcma_drv_mips *mcore)
        mcore->early_setup_done = true;
 }
 
+static void bcma_fix_i2s_irqflag(struct bcma_bus *bus)
+{
+       struct bcma_device *cpu, *pcie, *i2s;
+
+       /* Fixup the interrupts in 4716/4748 for i2s core (2010 Broadcom SDK)
+        * (IRQ flags > 7 are ignored when setting the interrupt masks)
+        */
+       if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4716 &&
+           bus->chipinfo.id != BCMA_CHIP_ID_BCM4748)
+               return;
+
+       cpu = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
+       pcie = bcma_find_core(bus, BCMA_CORE_PCIE);
+       i2s = bcma_find_core(bus, BCMA_CORE_I2S);
+       if (cpu && pcie && i2s &&
+           bcma_aread32(cpu, BCMA_MIPS_OOBSELINA74) == 0x08060504 &&
+           bcma_aread32(pcie, BCMA_MIPS_OOBSELINA74) == 0x08060504 &&
+           bcma_aread32(i2s, BCMA_MIPS_OOBSELOUTA30) == 0x88) {
+               bcma_awrite32(cpu, BCMA_MIPS_OOBSELINA74, 0x07060504);
+               bcma_awrite32(pcie, BCMA_MIPS_OOBSELINA74, 0x07060504);
+               bcma_awrite32(i2s, BCMA_MIPS_OOBSELOUTA30, 0x87);
+               bcma_debug(bus,
+                          "Moved i2s interrupt to oob line 7 instead of 8\n");
+       }
+}
+
 void bcma_core_mips_init(struct bcma_drv_mips *mcore)
 {
        struct bcma_bus *bus;
@@ -236,43 +319,55 @@ void bcma_core_mips_init(struct bcma_drv_mips *mcore)
        if (mcore->setup_done)
                return;
 
-       bcma_info(bus, "Initializing MIPS core...\n");
+       bcma_debug(bus, "Initializing MIPS core...\n");
 
        bcma_core_mips_early_init(mcore);
 
-       mcore->assigned_irqs = 1;
-
-       /* Assign IRQs to all cores on the bus */
-       list_for_each_entry(core, &bus->cores, list) {
-               int mips_irq;
-               if (core->irq)
-                       continue;
-
-               mips_irq = bcma_core_mips_irq(core);
-               if (mips_irq > 4)
-                       core->irq = 0;
-               else
-                       core->irq = mips_irq + 2;
-               if (core->irq > 5)
-                       continue;
-               switch (core->id.id) {
-               case BCMA_CORE_PCI:
-               case BCMA_CORE_PCIE:
-               case BCMA_CORE_ETHERNET:
-               case BCMA_CORE_ETHERNET_GBIT:
-               case BCMA_CORE_MAC_GBIT:
-               case BCMA_CORE_80211:
-               case BCMA_CORE_USB20_HOST:
-                       /* These devices get their own IRQ line if available,
-                        * the rest goes on IRQ0
-                        */
-                       if (mcore->assigned_irqs <= 4)
-                               bcma_core_mips_set_irq(core,
-                                                      mcore->assigned_irqs++);
-                       break;
+       bcma_fix_i2s_irqflag(bus);
+
+       switch (bus->chipinfo.id) {
+       case BCMA_CHIP_ID_BCM4716:
+       case BCMA_CHIP_ID_BCM4748:
+               bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0);
+               bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0);
+               bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_USB20_HOST, 0);
+               bcma_core_mips_set_irq_name(bus, 4, BCMA_CORE_PCIE, 0);
+               bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0);
+               bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_I2S, 0);
+               break;
+       case BCMA_CHIP_ID_BCM5356:
+       case BCMA_CHIP_ID_BCM47162:
+       case BCMA_CHIP_ID_BCM53572:
+               bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0);
+               bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0);
+               bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0);
+               break;
+       case BCMA_CHIP_ID_BCM5357:
+       case BCMA_CHIP_ID_BCM4749:
+               bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0);
+               bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0);
+               bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_USB20_HOST, 0);
+               bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0);
+               bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_I2S, 0);
+               break;
+       case BCMA_CHIP_ID_BCM4706:
+               bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_PCIE, 0);
+               bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_4706_MAC_GBIT,
+                                           0);
+               bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_PCIE, 1);
+               bcma_core_mips_set_irq_name(bus, 4, BCMA_CORE_USB20_HOST, 0);
+               bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_4706_CHIPCOMMON,
+                                           0);
+               break;
+       default:
+               list_for_each_entry(core, &bus->cores, list) {
+                       core->irq = bcma_core_irq(core);
                }
+               bcma_err(bus,
+                        "Unknown device (0x%x) found, can not configure IRQs\n",
+                        bus->chipinfo.id);
        }
-       bcma_info(bus, "IRQ reconfiguration done\n");
+       bcma_debug(bus, "IRQ reconfiguration done\n");
        bcma_core_mips_dump_irq(bus);
 
        mcore->setup_done = true;
index af0c9fabee54f212833809ecbfab0007bc9105fb..d3bde6cec927643bdf02445e387e97d4d5f29d69 100644 (file)
@@ -94,19 +94,19 @@ static int bcma_extpci_read_config(struct bcma_drv_pci *pc, unsigned int dev,
        if (dev == 0) {
                /* we support only two functions on device 0 */
                if (func > 1)
-                       return -EINVAL;
+                       goto out;
 
                /* accesses to config registers with offsets >= 256
                 * requires indirect access.
                 */
                if (off >= PCI_CONFIG_SPACE_SIZE) {
                        addr = (func << 12);
-                       addr |= (off & 0x0FFF);
+                       addr |= (off & 0x0FFC);
                        val = bcma_pcie_read_config(pc, addr);
                } else {
                        addr = BCMA_CORE_PCI_PCICFG0;
                        addr |= (func << 8);
-                       addr |= (off & 0xfc);
+                       addr |= (off & 0xFC);
                        val = pcicore_read32(pc, addr);
                }
        } else {
@@ -119,11 +119,9 @@ static int bcma_extpci_read_config(struct bcma_drv_pci *pc, unsigned int dev,
                        goto out;
 
                if (mips_busprobe32(val, mmio)) {
-                       val = 0xffffffff;
+                       val = 0xFFFFFFFF;
                        goto unmap;
                }
-
-               val = readl(mmio);
        }
        val >>= (8 * (off & 3));
 
@@ -151,7 +149,7 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev,
                                   const void *buf, int len)
 {
        int err = -EINVAL;
-       u32 addr = 0, val = 0;
+       u32 addr, val;
        void __iomem *mmio = 0;
        u16 chipid = pc->core->bus->chipinfo.id;
 
@@ -159,16 +157,22 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev,
        if (unlikely(len != 1 && len != 2 && len != 4))
                goto out;
        if (dev == 0) {
+               /* we support only two functions on device 0 */
+               if (func > 1)
+                       goto out;
+
                /* accesses to config registers with offsets >= 256
                 * requires indirect access.
                 */
-               if (off < PCI_CONFIG_SPACE_SIZE) {
-                       addr = pc->core->addr + BCMA_CORE_PCI_PCICFG0;
+               if (off >= PCI_CONFIG_SPACE_SIZE) {
+                       addr = (func << 12);
+                       addr |= (off & 0x0FFC);
+                       val = bcma_pcie_read_config(pc, addr);
+               } else {
+                       addr = BCMA_CORE_PCI_PCICFG0;
                        addr |= (func << 8);
-                       addr |= (off & 0xfc);
-                       mmio = ioremap_nocache(addr, sizeof(val));
-                       if (!mmio)
-                               goto out;
+                       addr |= (off & 0xFC);
+                       val = pcicore_read32(pc, addr);
                }
        } else {
                addr = bcma_get_cfgspace_addr(pc, dev, func, off);
@@ -180,19 +184,17 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev,
                        goto out;
 
                if (mips_busprobe32(val, mmio)) {
-                       val = 0xffffffff;
+                       val = 0xFFFFFFFF;
                        goto unmap;
                }
        }
 
        switch (len) {
        case 1:
-               val = readl(mmio);
                val &= ~(0xFF << (8 * (off & 3)));
                val |= *((const u8 *)buf) << (8 * (off & 3));
                break;
        case 2:
-               val = readl(mmio);
                val &= ~(0xFFFF << (8 * (off & 3)));
                val |= *((const u16 *)buf) << (8 * (off & 3));
                break;
@@ -200,13 +202,14 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev,
                val = *((const u32 *)buf);
                break;
        }
-       if (dev == 0 && !addr) {
+       if (dev == 0) {
                /* accesses to config registers with offsets >= 256
                 * requires indirect access.
                 */
-               addr = (func << 12);
-               addr |= (off & 0x0FFF);
-               bcma_pcie_write_config(pc, addr, val);
+               if (off >= PCI_CONFIG_SPACE_SIZE)
+                       bcma_pcie_write_config(pc, addr, val);
+               else
+                       pcicore_write32(pc, addr, val);
        } else {
                writel(val, mmio);
 
@@ -276,7 +279,7 @@ static u8 bcma_find_pci_capability(struct bcma_drv_pci *pc, unsigned int dev,
        /* check for Header type 0 */
        bcma_extpci_read_config(pc, dev, func, PCI_HEADER_TYPE, &byte_val,
                                sizeof(u8));
-       if ((byte_val & 0x7f) != PCI_HEADER_TYPE_NORMAL)
+       if ((byte_val & 0x7F) != PCI_HEADER_TYPE_NORMAL)
                return cap_ptr;
 
        /* check if the capability pointer field exists */
@@ -426,7 +429,7 @@ void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
        /* Reset RC */
        usleep_range(3000, 5000);
        pcicore_write32(pc, BCMA_CORE_PCI_CTL, BCMA_CORE_PCI_CTL_RST_OE);
-       usleep_range(1000, 2000);
+       msleep(50);
        pcicore_write32(pc, BCMA_CORE_PCI_CTL, BCMA_CORE_PCI_CTL_RST |
                        BCMA_CORE_PCI_CTL_RST_OE);
 
@@ -488,6 +491,17 @@ void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
 
        bcma_core_pci_enable_crs(pc);
 
+       if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706 ||
+           bus->chipinfo.id == BCMA_CHIP_ID_BCM4716) {
+               u16 val16;
+               bcma_extpci_read_config(pc, 0, 0, BCMA_CORE_PCI_CFG_DEVCTRL,
+                                       &val16, sizeof(val16));
+               val16 |= (2 << 5);      /* Max payload size of 512 */
+               val16 |= (2 << 12);     /* MRRS 512 */
+               bcma_extpci_write_config(pc, 0, 0, BCMA_CORE_PCI_CFG_DEVCTRL,
+                                        &val16, sizeof(val16));
+       }
+
        /* Enable PCI bridge BAR0 memory & master access */
        tmp = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
        bcma_extpci_write_config(pc, 0, 0, PCI_COMMAND, &tmp, sizeof(tmp));
@@ -576,7 +590,7 @@ int bcma_core_pci_plat_dev_init(struct pci_dev *dev)
        pr_info("PCI: Fixing up device %s\n", pci_name(dev));
 
        /* Fix up interrupt lines */
-       dev->irq = bcma_core_mips_irq(pc_host->pdev->core) + 2;
+       dev->irq = bcma_core_irq(pc_host->pdev->core);
        pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
 
        return 0;
@@ -595,6 +609,6 @@ int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev)
 
        pc_host = container_of(dev->bus->ops, struct bcma_drv_pci_host,
                               pci_ops);
-       return bcma_core_mips_irq(pc_host->pdev->core) + 2;
+       return bcma_core_irq(pc_host->pdev->core);
 }
 EXPORT_SYMBOL(bcma_core_pci_pcibios_map_irq);
index 324f9debda88007ac096320dae35340db99e3fe5..9a6188add5908904887339df04182eda5a6bb358 100644 (file)
@@ -81,8 +81,8 @@ struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid)
 }
 EXPORT_SYMBOL_GPL(bcma_find_core);
 
-static struct bcma_device *bcma_find_core_unit(struct bcma_bus *bus, u16 coreid,
-                                              u8 unit)
+struct bcma_device *bcma_find_core_unit(struct bcma_bus *bus, u16 coreid,
+                                       u8 unit)
 {
        struct bcma_device *core;
 
@@ -149,6 +149,14 @@ static int bcma_register_cores(struct bcma_bus *bus)
                dev_id++;
        }
 
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+       if (bus->drv_cc.pflash.present) {
+               err = platform_device_register(&bcma_pflash_dev);
+               if (err)
+                       bcma_err(bus, "Error registering parallel flash\n");
+       }
+#endif
+
 #ifdef CONFIG_BCMA_SFLASH
        if (bus->drv_cc.sflash.present) {
                err = platform_device_register(&bcma_sflash_dev);
index a47e6ee98b8c9798da8ffad462dbc052ef875053..f2fbf103d90e55ac4ea626f1729cd11983f81bda 100644 (file)
@@ -63,6 +63,12 @@ config CLK_TWL6040
          McPDM. McPDM module is using the external bit clock on the McPDM bus
          as functional clock.
 
+config CLK_PALMAS
+       tristate "Palmas PMIC Clock Support"
+       depends on MFD_PALMAS
+       help
+          Enable clock support for the two clocks on Palmas PMIC chips
+
 endmenu
 
 source "drivers/clk/mvebu/Kconfig"
index ee90e87e7675859e4ae80cf0587191732e8b6541..c92d381adcf3e144cce8e285122f68f7a31536ef 100644 (file)
@@ -1,5 +1,6 @@
 # common clock types
 obj-$(CONFIG_HAVE_CLK)         += clk-devres.o
+
 obj-$(CONFIG_CLKDEV_LOOKUP)    += clkdev.o
 obj-$(CONFIG_COMMON_CLK)       += clk.o clk-fixed-rate.o clk-gate.o \
                                   clk-mux.o clk-divider.o clk-fixed-factor.o
@@ -27,3 +28,4 @@ obj-$(CONFIG_ARCH_ZYNQ)               += clk-zynq.o
 obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
 obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
 obj-$(CONFIG_CLK_TWL6040)      += clk-twl6040.o
+obj-$(CONFIG_CLK_PALMAS) += clk-palmas.o
diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c
new file mode 100644 (file)
index 0000000..3760a93
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * PALMAS resource clock module driver
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ * Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/palmas.h>
+#include <linux/clk-provider.h>
+#include <linux/of_platform.h>
+
+struct palmas_clk {
+       struct palmas *palmas;
+       struct device *dev;
+       struct clk_hw clk32kg;
+       struct clk_hw clk32kgaudio;
+       int clk32kgaudio_mode_sleep;
+       int clk32kg_mode_sleep;
+};
+
+static int palmas_clock_setbits(struct palmas *palmas, unsigned int reg,
+               unsigned int data)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_RESOURCE_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_RESOURCE_BASE, reg);
+
+       return regmap_update_bits(palmas->regmap[slave], addr, data, data);
+}
+
+static int palmas_clock_clrbits(struct palmas *palmas, unsigned int reg,
+               unsigned int data)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_RESOURCE_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_RESOURCE_BASE, reg);
+
+       return regmap_update_bits(palmas->regmap[slave], addr, data, 0);
+}
+
+static int palmas_prepare_clk32kg(struct clk_hw *hw)
+{
+       struct palmas_clk *palmas_clk = container_of(hw, struct palmas_clk,
+                       clk32kg);
+       int ret;
+
+       ret = palmas_clock_setbits(palmas_clk->palmas,
+                       PALMAS_CLK32KG_CTRL, PALMAS_CLK32KG_CTRL_MODE_ACTIVE);
+       if (ret)
+               dev_err(palmas_clk->dev, "Failed to enable clk32kg: %d\n", ret);
+
+       return ret;
+}
+
+static void palmas_unprepare_clk32kg(struct clk_hw *hw)
+{
+       struct palmas_clk *palmas_clk = container_of(hw, struct palmas_clk,
+                       clk32kg);
+       int ret;
+
+       ret = palmas_clock_clrbits(palmas_clk->palmas,
+                       PALMAS_CLK32KG_CTRL, PALMAS_CLK32KG_CTRL_MODE_ACTIVE);
+       if (ret)
+               dev_err(palmas_clk->dev, "Failed to enable clk32kg: %d\n", ret);
+
+       return;
+}
+
+static const struct clk_ops palmas_clk32kg_ops = {
+       .prepare = palmas_prepare_clk32kg,
+       .unprepare = palmas_unprepare_clk32kg,
+};
+
+static int palmas_prepare_clk32kgaudio(struct clk_hw *hw)
+{
+       struct palmas_clk *palmas_clk = container_of(hw, struct palmas_clk,
+                       clk32kgaudio);
+       int ret;
+
+       ret = palmas_clock_setbits(palmas_clk->palmas,
+               PALMAS_CLK32KGAUDIO_CTRL, PALMAS_CLK32KGAUDIO_CTRL_MODE_ACTIVE);
+       if (ret)
+               dev_err(palmas_clk->dev,
+                               "Failed to enable clk32kgaudio: %d\n", ret);
+
+       return ret;
+}
+
+static void palmas_unprepare_clk32kgaudio(struct clk_hw *hw)
+{
+       struct palmas_clk *palmas_clk = container_of(hw, struct palmas_clk,
+                       clk32kgaudio);
+       int ret;
+
+       ret = palmas_clock_clrbits(palmas_clk->palmas,
+               PALMAS_CLK32KGAUDIO_CTRL, PALMAS_CLK32KGAUDIO_CTRL_MODE_ACTIVE);
+       if (ret)
+               dev_err(palmas_clk->dev,
+                               "Failed to enable clk32kgaudio: %d\n", ret);
+
+       return;
+}
+
+static const struct clk_ops palmas_clk32kgaudio_ops = {
+       .prepare = palmas_prepare_clk32kgaudio,
+       .unprepare = palmas_unprepare_clk32kgaudio,
+};
+
+static int palmas_initialise_clk(struct palmas_clk *palmas_clk)
+{
+       int ret;
+
+       if (palmas_clk->clk32kg_mode_sleep) {
+               ret = palmas_clock_setbits(palmas_clk->palmas,
+                       PALMAS_CLK32KG_CTRL, PALMAS_CLK32KG_CTRL_MODE_SLEEP);
+               if (ret)
+                       return ret;
+       }
+
+       if (palmas_clk->clk32kgaudio_mode_sleep) {
+               ret = palmas_clock_setbits(palmas_clk->palmas,
+                       PALMAS_CLK32KGAUDIO_CTRL,
+                       PALMAS_CLK32KGAUDIO_CTRL_MODE_SLEEP);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int palmas_clk_probe(struct platform_device *pdev)
+{
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+       struct palmas_clk_platform_data *pdata = pdev->dev.platform_data;
+       struct device_node *node = pdev->dev.of_node;
+       struct palmas_clk *palmas_clk;
+       struct clk *clk;
+       struct clk_init_data init_clk32g, init_clk32gaudio;
+       int ret;
+       u32 prop;
+
+       palmas_clk = devm_kzalloc(&pdev->dev, sizeof(*palmas_clk), GFP_KERNEL);
+       if (!palmas_clk)
+               return -ENOMEM;
+
+       if (node && !pdata) {
+               ret = of_property_read_u32(node, "ti,clk32kg_mode_sleep",
+                                               &prop);
+               if (!ret)
+                       palmas_clk->clk32kg_mode_sleep = prop;
+               ret = of_property_read_u32(node, "ti,clk32kgaudio_mode_sleep",
+                                               &prop);
+               if (!ret)
+                       palmas_clk->clk32kgaudio_mode_sleep = prop;
+       }
+
+       palmas_clk->palmas = palmas;
+       palmas_clk->dev = &pdev->dev;
+
+       init_clk32g.name = "clk32kg";
+       init_clk32g.ops = &palmas_clk32kg_ops;
+       init_clk32g.parent_names = NULL;
+       init_clk32g.num_parents = 0;
+       palmas_clk->clk32kg.init = &init_clk32g;
+
+       clk = clk_register(palmas_clk->dev, &palmas_clk->clk32kg);
+       if (IS_ERR(clk)) {
+               dev_dbg(&pdev->dev, "clk32kg clock register failed %ld\n",
+                               PTR_ERR(clk));
+               ret = PTR_ERR(clk);
+               goto err_clk32kg;
+       }
+
+       init_clk32gaudio.name = "clk32kgaudio";
+       init_clk32gaudio.ops = &palmas_clk32kgaudio_ops;
+       init_clk32gaudio.parent_names = NULL;
+       init_clk32gaudio.num_parents = 0;
+       palmas_clk->clk32kgaudio.init = &init_clk32gaudio;
+
+       clk = clk_register(palmas_clk->dev, &palmas_clk->clk32kgaudio);
+       if (IS_ERR(clk)) {
+               dev_dbg(&pdev->dev, "clk32kgaudio clock register failed %ld\n",
+               PTR_ERR(clk));
+               ret = PTR_ERR(clk);
+               goto err_audio;
+       }
+
+       ret = palmas_initialise_clk(palmas_clk);
+       if (ret)
+               goto err;
+
+       dev_set_drvdata(&pdev->dev, palmas_clk);
+
+       return 0;
+
+err:
+       clk_unregister(palmas_clk->clk32kgaudio.clk);
+err_audio:
+       clk_unregister(palmas_clk->clk32kg.clk);
+err_clk32kg:
+
+       return ret;
+}
+
+static int palmas_clk_remove(struct platform_device *pdev)
+{
+       struct palmas_clk *palmas_clk = dev_get_drvdata(&pdev->dev);
+
+       clk_unregister(palmas_clk->clk32kgaudio.clk);
+       clk_unregister(palmas_clk->clk32kg.clk);
+
+       return 0;
+}
+
+static struct of_device_id palmas_clk_of_match[] = {
+       { .compatible = "ti,palmas-clk", },
+       { /* end */ }
+};
+MODULE_DEVICE_TABLE(of, palmas_clk_of_match);
+
+static struct platform_driver palmas_clk_driver = {
+       .probe = palmas_clk_probe,
+       .remove = palmas_clk_remove,
+       .driver = {
+               .name = "palmas-clk",
+               .of_match_table = palmas_clk_of_match,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init palmas_clk_init(void)
+{
+       return platform_driver_register(&palmas_clk_driver);
+}
+module_init(palmas_clk_init);
+
+static void __exit palmas_clk_exit(void)
+{
+       platform_driver_unregister(&palmas_clk_driver);
+}
+module_exit(palmas_clk_exit);
+module_platform_driver(palmas_clk_driver);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("PALMAS clock driver");
+MODULE_ALIAS("platform:palmas-clk");
+MODULE_LICENSE("GPL");
index e66e8ee5a9af0e59e6599bc3d637807ee99a0466..6aa425fe0ed57a26f0fa796f1930a63f27108311 100644 (file)
@@ -5,6 +5,7 @@
  *
  * Copyright (c) 2010 Nokia Corporation
  * Author: Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
+ * Copyright (c) 2011 Texas Instruments Incorporated
  *
  * 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
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
-#include <linux/clk.h>
 #include <linux/platform_device.h>
 #include <linux/scatterlist.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/omap-dma.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
 #include <linux/io.h>
 #include <linux/crypto.h>
 #include <linux/interrupt.h>
 #include <crypto/scatterwalk.h>
 #include <crypto/aes.h>
 
-#include <linux/omap-dma.h>
+#define DST_MAXBURST                   4
+#define DMA_MIN                                (DST_MAXBURST * sizeof(u32))
 
 /* OMAP TRM gives bitfields as start:end, where start is the higher bit
    number. For example 7:0 */
 #define FLD_MASK(start, end)   (((1 << ((start) - (end) + 1)) - 1) << (end))
 #define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
 
-#define AES_REG_KEY(x)                 (0x1C - ((x ^ 0x01) * 0x04))
-#define AES_REG_IV(x)                  (0x20 + ((x) * 0x04))
+#define AES_REG_KEY(dd, x)             ((dd)->pdata->key_ofs - \
+                                               ((x ^ 0x01) * 0x04))
+#define AES_REG_IV(dd, x)              ((dd)->pdata->iv_ofs + ((x) * 0x04))
 
-#define AES_REG_CTRL                   0x30
-#define AES_REG_CTRL_CTR_WIDTH         (1 << 7)
+#define AES_REG_CTRL(dd)               ((dd)->pdata->ctrl_ofs)
+#define AES_REG_CTRL_CTR_WIDTH_MASK    (3 << 7)
+#define AES_REG_CTRL_CTR_WIDTH_32              (0 << 7)
+#define AES_REG_CTRL_CTR_WIDTH_64              (1 << 7)
+#define AES_REG_CTRL_CTR_WIDTH_96              (2 << 7)
+#define AES_REG_CTRL_CTR_WIDTH_128             (3 << 7)
 #define AES_REG_CTRL_CTR               (1 << 6)
 #define AES_REG_CTRL_CBC               (1 << 5)
 #define AES_REG_CTRL_KEY_SIZE          (3 << 3)
 #define AES_REG_CTRL_INPUT_READY       (1 << 1)
 #define AES_REG_CTRL_OUTPUT_READY      (1 << 0)
 
-#define AES_REG_DATA                   0x34
-#define AES_REG_DATA_N(x)              (0x34 + ((x) * 0x04))
+#define AES_REG_DATA_N(dd, x)          ((dd)->pdata->data_ofs + ((x) * 0x04))
 
-#define AES_REG_REV                    0x44
-#define AES_REG_REV_MAJOR              0xF0
-#define AES_REG_REV_MINOR              0x0F
+#define AES_REG_REV(dd)                        ((dd)->pdata->rev_ofs)
 
-#define AES_REG_MASK                   0x48
+#define AES_REG_MASK(dd)               ((dd)->pdata->mask_ofs)
 #define AES_REG_MASK_SIDLE             (1 << 6)
 #define AES_REG_MASK_START             (1 << 5)
 #define AES_REG_MASK_DMA_OUT_EN                (1 << 3)
@@ -63,8 +72,7 @@
 #define AES_REG_MASK_SOFTRESET         (1 << 1)
 #define AES_REG_AUTOIDLE               (1 << 0)
 
-#define AES_REG_SYSSTATUS              0x4C
-#define AES_REG_SYSSTATUS_RESETDONE    (1 << 0)
+#define AES_REG_LENGTH_N(x)            (0x54 + ((x) * 0x04))
 
 #define DEFAULT_TIMEOUT                (5*HZ)
 
@@ -72,6 +80,7 @@
 #define FLAGS_ENCRYPT          BIT(0)
 #define FLAGS_CBC              BIT(1)
 #define FLAGS_GIV              BIT(2)
+#define FLAGS_CTR              BIT(3)
 
 #define FLAGS_INIT             BIT(4)
 #define FLAGS_FAST             BIT(5)
@@ -92,11 +101,39 @@ struct omap_aes_reqctx {
 #define OMAP_AES_QUEUE_LENGTH  1
 #define OMAP_AES_CACHE_SIZE    0
 
+struct omap_aes_algs_info {
+       struct crypto_alg       *algs_list;
+       unsigned int            size;
+       unsigned int            registered;
+};
+
+struct omap_aes_pdata {
+       struct omap_aes_algs_info       *algs_info;
+       unsigned int    algs_info_size;
+
+       void            (*trigger)(struct omap_aes_dev *dd, int length);
+
+       u32             key_ofs;
+       u32             iv_ofs;
+       u32             ctrl_ofs;
+       u32             data_ofs;
+       u32             rev_ofs;
+       u32             mask_ofs;
+
+       u32             dma_enable_in;
+       u32             dma_enable_out;
+       u32             dma_start;
+
+       u32             major_mask;
+       u32             major_shift;
+       u32             minor_mask;
+       u32             minor_shift;
+};
+
 struct omap_aes_dev {
        struct list_head        list;
        unsigned long           phys_base;
        void __iomem            *io_base;
-       struct clk              *iclk;
        struct omap_aes_ctx     *ctx;
        struct device           *dev;
        unsigned long           flags;
@@ -111,20 +148,24 @@ struct omap_aes_dev {
        struct ablkcipher_request       *req;
        size_t                          total;
        struct scatterlist              *in_sg;
+       struct scatterlist              in_sgl;
        size_t                          in_offset;
        struct scatterlist              *out_sg;
+       struct scatterlist              out_sgl;
        size_t                          out_offset;
 
        size_t                  buflen;
        void                    *buf_in;
        size_t                  dma_size;
        int                     dma_in;
-       int                     dma_lch_in;
+       struct dma_chan         *dma_lch_in;
        dma_addr_t              dma_addr_in;
        void                    *buf_out;
        int                     dma_out;
-       int                     dma_lch_out;
+       struct dma_chan         *dma_lch_out;
        dma_addr_t              dma_addr_out;
+
+       const struct omap_aes_pdata     *pdata;
 };
 
 /* keep registered devices data here */
@@ -160,19 +201,6 @@ static void omap_aes_write_n(struct omap_aes_dev *dd, u32 offset,
                omap_aes_write(dd, offset, *value);
 }
 
-static int omap_aes_wait(struct omap_aes_dev *dd, u32 offset, u32 bit)
-{
-       unsigned long timeout = jiffies + DEFAULT_TIMEOUT;
-
-       while (!(omap_aes_read(dd, offset) & bit)) {
-               if (time_is_before_jiffies(timeout)) {
-                       dev_err(dd->dev, "omap-aes timeout\n");
-                       return -ETIMEDOUT;
-               }
-       }
-       return 0;
-}
-
 static int omap_aes_hw_init(struct omap_aes_dev *dd)
 {
        /*
@@ -180,23 +208,9 @@ static int omap_aes_hw_init(struct omap_aes_dev *dd)
         * It may be long delays between requests.
         * Device might go to off mode to save power.
         */
-       clk_enable(dd->iclk);
+       pm_runtime_get_sync(dd->dev);
 
        if (!(dd->flags & FLAGS_INIT)) {
-               /* is it necessary to reset before every operation? */
-               omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_SOFTRESET,
-                                       AES_REG_MASK_SOFTRESET);
-               /*
-                * prevent OCP bus error (SRESP) in case an access to the module
-                * is performed while the module is coming out of soft reset
-                */
-               __asm__ __volatile__("nop");
-               __asm__ __volatile__("nop");
-
-               if (omap_aes_wait(dd, AES_REG_SYSSTATUS,
-                               AES_REG_SYSSTATUS_RESETDONE))
-                       return -ETIMEDOUT;
-
                dd->flags |= FLAGS_INIT;
                dd->err = 0;
        }
@@ -208,59 +222,75 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd)
 {
        unsigned int key32;
        int i, err;
-       u32 val, mask;
+       u32 val, mask = 0;
 
        err = omap_aes_hw_init(dd);
        if (err)
                return err;
 
-       val = 0;
-       if (dd->dma_lch_out >= 0)
-               val |= AES_REG_MASK_DMA_OUT_EN;
-       if (dd->dma_lch_in >= 0)
-               val |= AES_REG_MASK_DMA_IN_EN;
-
-       mask = AES_REG_MASK_DMA_IN_EN | AES_REG_MASK_DMA_OUT_EN;
-
-       omap_aes_write_mask(dd, AES_REG_MASK, val, mask);
-
        key32 = dd->ctx->keylen / sizeof(u32);
 
        /* it seems a key should always be set even if it has not changed */
        for (i = 0; i < key32; i++) {
-               omap_aes_write(dd, AES_REG_KEY(i),
+               omap_aes_write(dd, AES_REG_KEY(dd, i),
                        __le32_to_cpu(dd->ctx->key[i]));
        }
 
-       if ((dd->flags & FLAGS_CBC) && dd->req->info)
-               omap_aes_write_n(dd, AES_REG_IV(0), dd->req->info, 4);
+       if ((dd->flags & (FLAGS_CBC | FLAGS_CTR)) && dd->req->info)
+               omap_aes_write_n(dd, AES_REG_IV(dd, 0), dd->req->info, 4);
 
        val = FLD_VAL(((dd->ctx->keylen >> 3) - 1), 4, 3);
        if (dd->flags & FLAGS_CBC)
                val |= AES_REG_CTRL_CBC;
+       if (dd->flags & FLAGS_CTR) {
+               val |= AES_REG_CTRL_CTR | AES_REG_CTRL_CTR_WIDTH_32;
+               mask = AES_REG_CTRL_CTR | AES_REG_CTRL_CTR_WIDTH_MASK;
+       }
        if (dd->flags & FLAGS_ENCRYPT)
                val |= AES_REG_CTRL_DIRECTION;
 
-       mask = AES_REG_CTRL_CBC | AES_REG_CTRL_DIRECTION |
+       mask |= AES_REG_CTRL_CBC | AES_REG_CTRL_DIRECTION |
                        AES_REG_CTRL_KEY_SIZE;
 
-       omap_aes_write_mask(dd, AES_REG_CTRL, val, mask);
+       omap_aes_write_mask(dd, AES_REG_CTRL(dd), val, mask);
 
-       /* IN */
-       omap_set_dma_dest_params(dd->dma_lch_in, 0, OMAP_DMA_AMODE_CONSTANT,
-                                dd->phys_base + AES_REG_DATA, 0, 4);
+       return 0;
+}
 
-       omap_set_dma_dest_burst_mode(dd->dma_lch_in, OMAP_DMA_DATA_BURST_4);
-       omap_set_dma_src_burst_mode(dd->dma_lch_in, OMAP_DMA_DATA_BURST_4);
+static void omap_aes_dma_trigger_omap2(struct omap_aes_dev *dd, int length)
+{
+       u32 mask, val;
 
-       /* OUT */
-       omap_set_dma_src_params(dd->dma_lch_out, 0, OMAP_DMA_AMODE_CONSTANT,
-                               dd->phys_base + AES_REG_DATA, 0, 4);
+       val = dd->pdata->dma_start;
 
-       omap_set_dma_src_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4);
-       omap_set_dma_dest_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4);
+       if (dd->dma_lch_out != NULL)
+               val |= dd->pdata->dma_enable_out;
+       if (dd->dma_lch_in != NULL)
+               val |= dd->pdata->dma_enable_in;
+
+       mask = dd->pdata->dma_enable_out | dd->pdata->dma_enable_in |
+              dd->pdata->dma_start;
+
+       omap_aes_write_mask(dd, AES_REG_MASK(dd), val, mask);
 
-       return 0;
+}
+
+static void omap_aes_dma_trigger_omap4(struct omap_aes_dev *dd, int length)
+{
+       omap_aes_write(dd, AES_REG_LENGTH_N(0), length);
+       omap_aes_write(dd, AES_REG_LENGTH_N(1), 0);
+
+       omap_aes_dma_trigger_omap2(dd, length);
+}
+
+static void omap_aes_dma_stop(struct omap_aes_dev *dd)
+{
+       u32 mask;
+
+       mask = dd->pdata->dma_enable_out | dd->pdata->dma_enable_in |
+              dd->pdata->dma_start;
+
+       omap_aes_write_mask(dd, AES_REG_MASK(dd), 0, mask);
 }
 
 static struct omap_aes_dev *omap_aes_find_dev(struct omap_aes_ctx *ctx)
@@ -284,18 +314,10 @@ static struct omap_aes_dev *omap_aes_find_dev(struct omap_aes_ctx *ctx)
        return dd;
 }
 
-static void omap_aes_dma_callback(int lch, u16 ch_status, void *data)
+static void omap_aes_dma_out_callback(void *data)
 {
        struct omap_aes_dev *dd = data;
 
-       if (ch_status != OMAP_DMA_BLOCK_IRQ) {
-               pr_err("omap-aes DMA error status: 0x%hx\n", ch_status);
-               dd->err = -EIO;
-               dd->flags &= ~FLAGS_INIT; /* request to re-initialize */
-       } else if (lch == dd->dma_lch_in) {
-               return;
-       }
-
        /* dma_lch_out - completed */
        tasklet_schedule(&dd->done_task);
 }
@@ -303,9 +325,10 @@ static void omap_aes_dma_callback(int lch, u16 ch_status, void *data)
 static int omap_aes_dma_init(struct omap_aes_dev *dd)
 {
        int err = -ENOMEM;
+       dma_cap_mask_t mask;
 
-       dd->dma_lch_out = -1;
-       dd->dma_lch_in = -1;
+       dd->dma_lch_out = NULL;
+       dd->dma_lch_in = NULL;
 
        dd->buf_in = (void *)__get_free_pages(GFP_KERNEL, OMAP_AES_CACHE_SIZE);
        dd->buf_out = (void *)__get_free_pages(GFP_KERNEL, OMAP_AES_CACHE_SIZE);
@@ -334,23 +357,31 @@ static int omap_aes_dma_init(struct omap_aes_dev *dd)
                goto err_map_out;
        }
 
-       err = omap_request_dma(dd->dma_in, "omap-aes-rx",
-                              omap_aes_dma_callback, dd, &dd->dma_lch_in);
-       if (err) {
-               dev_err(dd->dev, "Unable to request DMA channel\n");
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+
+       dd->dma_lch_in = dma_request_slave_channel_compat(mask,
+                                                         omap_dma_filter_fn,
+                                                         &dd->dma_in,
+                                                         dd->dev, "rx");
+       if (!dd->dma_lch_in) {
+               dev_err(dd->dev, "Unable to request in DMA channel\n");
                goto err_dma_in;
        }
-       err = omap_request_dma(dd->dma_out, "omap-aes-tx",
-                              omap_aes_dma_callback, dd, &dd->dma_lch_out);
-       if (err) {
-               dev_err(dd->dev, "Unable to request DMA channel\n");
+
+       dd->dma_lch_out = dma_request_slave_channel_compat(mask,
+                                                          omap_dma_filter_fn,
+                                                          &dd->dma_out,
+                                                          dd->dev, "tx");
+       if (!dd->dma_lch_out) {
+               dev_err(dd->dev, "Unable to request out DMA channel\n");
                goto err_dma_out;
        }
 
        return 0;
 
 err_dma_out:
-       omap_free_dma(dd->dma_lch_in);
+       dma_release_channel(dd->dma_lch_in);
 err_dma_in:
        dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen,
                         DMA_FROM_DEVICE);
@@ -367,8 +398,8 @@ err_alloc:
 
 static void omap_aes_dma_cleanup(struct omap_aes_dev *dd)
 {
-       omap_free_dma(dd->dma_lch_out);
-       omap_free_dma(dd->dma_lch_in);
+       dma_release_channel(dd->dma_lch_out);
+       dma_release_channel(dd->dma_lch_in);
        dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen,
                         DMA_FROM_DEVICE);
        dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen, DMA_TO_DEVICE);
@@ -426,12 +457,15 @@ static int sg_copy(struct scatterlist **sg, size_t *offset, void *buf,
        return off;
 }
 
-static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in,
-                              dma_addr_t dma_addr_out, int length)
+static int omap_aes_crypt_dma(struct crypto_tfm *tfm,
+               struct scatterlist *in_sg, struct scatterlist *out_sg)
 {
        struct omap_aes_ctx *ctx = crypto_tfm_ctx(tfm);
        struct omap_aes_dev *dd = ctx->dd;
-       int len32;
+       struct dma_async_tx_descriptor *tx_in, *tx_out;
+       struct dma_slave_config cfg;
+       dma_addr_t dma_addr_in = sg_dma_address(in_sg);
+       int ret, length = sg_dma_len(in_sg);
 
        pr_debug("len: %d\n", length);
 
@@ -441,30 +475,61 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in,
                dma_sync_single_for_device(dd->dev, dma_addr_in, length,
                                           DMA_TO_DEVICE);
 
-       len32 = DIV_ROUND_UP(length, sizeof(u32));
+       memset(&cfg, 0, sizeof(cfg));
+
+       cfg.src_addr = dd->phys_base + AES_REG_DATA_N(dd, 0);
+       cfg.dst_addr = dd->phys_base + AES_REG_DATA_N(dd, 0);
+       cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       cfg.src_maxburst = DST_MAXBURST;
+       cfg.dst_maxburst = DST_MAXBURST;
 
        /* IN */
-       omap_set_dma_transfer_params(dd->dma_lch_in, OMAP_DMA_DATA_TYPE_S32,
-                                    len32, 1, OMAP_DMA_SYNC_PACKET, dd->dma_in,
-                                       OMAP_DMA_DST_SYNC);
+       ret = dmaengine_slave_config(dd->dma_lch_in, &cfg);
+       if (ret) {
+               dev_err(dd->dev, "can't configure IN dmaengine slave: %d\n",
+                       ret);
+               return ret;
+       }
+
+       tx_in = dmaengine_prep_slave_sg(dd->dma_lch_in, in_sg, 1,
+                                       DMA_MEM_TO_DEV,
+                                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!tx_in) {
+               dev_err(dd->dev, "IN prep_slave_sg() failed\n");
+               return -EINVAL;
+       }
 
-       omap_set_dma_src_params(dd->dma_lch_in, 0, OMAP_DMA_AMODE_POST_INC,
-                               dma_addr_in, 0, 0);
+       /* No callback necessary */
+       tx_in->callback_param = dd;
 
        /* OUT */
-       omap_set_dma_transfer_params(dd->dma_lch_out, OMAP_DMA_DATA_TYPE_S32,
-                                    len32, 1, OMAP_DMA_SYNC_PACKET,
-                                       dd->dma_out, OMAP_DMA_SRC_SYNC);
+       ret = dmaengine_slave_config(dd->dma_lch_out, &cfg);
+       if (ret) {
+               dev_err(dd->dev, "can't configure OUT dmaengine slave: %d\n",
+                       ret);
+               return ret;
+       }
+
+       tx_out = dmaengine_prep_slave_sg(dd->dma_lch_out, out_sg, 1,
+                                       DMA_DEV_TO_MEM,
+                                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!tx_out) {
+               dev_err(dd->dev, "OUT prep_slave_sg() failed\n");
+               return -EINVAL;
+       }
 
-       omap_set_dma_dest_params(dd->dma_lch_out, 0, OMAP_DMA_AMODE_POST_INC,
-                                dma_addr_out, 0, 0);
+       tx_out->callback = omap_aes_dma_out_callback;
+       tx_out->callback_param = dd;
 
-       omap_start_dma(dd->dma_lch_in);
-       omap_start_dma(dd->dma_lch_out);
+       dmaengine_submit(tx_in);
+       dmaengine_submit(tx_out);
 
-       /* start DMA or disable idle mode */
-       omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_START,
-                           AES_REG_MASK_START);
+       dma_async_issue_pending(dd->dma_lch_in);
+       dma_async_issue_pending(dd->dma_lch_out);
+
+       /* start DMA */
+       dd->pdata->trigger(dd, length);
 
        return 0;
 }
@@ -476,6 +541,8 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd)
        int err, fast = 0, in, out;
        size_t count;
        dma_addr_t addr_in, addr_out;
+       struct scatterlist *in_sg, *out_sg;
+       int len32;
 
        pr_debug("total: %d\n", dd->total);
 
@@ -514,6 +581,9 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd)
                addr_in = sg_dma_address(dd->in_sg);
                addr_out = sg_dma_address(dd->out_sg);
 
+               in_sg = dd->in_sg;
+               out_sg = dd->out_sg;
+
                dd->flags |= FLAGS_FAST;
 
        } else {
@@ -521,6 +591,27 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd)
                count = sg_copy(&dd->in_sg, &dd->in_offset, dd->buf_in,
                                 dd->buflen, dd->total, 0);
 
+               len32 = DIV_ROUND_UP(count, DMA_MIN) * DMA_MIN;
+
+               /*
+                * The data going into the AES module has been copied
+                * to a local buffer and the data coming out will go
+                * into a local buffer so set up local SG entries for
+                * both.
+                */
+               sg_init_table(&dd->in_sgl, 1);
+               dd->in_sgl.offset = dd->in_offset;
+               sg_dma_len(&dd->in_sgl) = len32;
+               sg_dma_address(&dd->in_sgl) = dd->dma_addr_in;
+
+               sg_init_table(&dd->out_sgl, 1);
+               dd->out_sgl.offset = dd->out_offset;
+               sg_dma_len(&dd->out_sgl) = len32;
+               sg_dma_address(&dd->out_sgl) = dd->dma_addr_out;
+
+               in_sg = &dd->in_sgl;
+               out_sg = &dd->out_sgl;
+
                addr_in = dd->dma_addr_in;
                addr_out = dd->dma_addr_out;
 
@@ -530,7 +621,7 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd)
 
        dd->total -= count;
 
-       err = omap_aes_crypt_dma(tfm, addr_in, addr_out, count);
+       err = omap_aes_crypt_dma(tfm, in_sg, out_sg);
        if (err) {
                dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
                dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_TO_DEVICE);
@@ -545,7 +636,7 @@ static void omap_aes_finish_req(struct omap_aes_dev *dd, int err)
 
        pr_debug("err: %d\n", err);
 
-       clk_disable(dd->iclk);
+       pm_runtime_put_sync(dd->dev);
        dd->flags &= ~FLAGS_BUSY;
 
        req->base.complete(&req->base, err);
@@ -558,10 +649,10 @@ static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd)
 
        pr_debug("total: %d\n", dd->total);
 
-       omap_aes_write_mask(dd, AES_REG_MASK, 0, AES_REG_MASK_START);
+       omap_aes_dma_stop(dd);
 
-       omap_stop_dma(dd->dma_lch_in);
-       omap_stop_dma(dd->dma_lch_out);
+       dmaengine_terminate_all(dd->dma_lch_in);
+       dmaengine_terminate_all(dd->dma_lch_out);
 
        if (dd->flags & FLAGS_FAST) {
                dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_FROM_DEVICE);
@@ -734,6 +825,16 @@ static int omap_aes_cbc_decrypt(struct ablkcipher_request *req)
        return omap_aes_crypt(req, FLAGS_CBC);
 }
 
+static int omap_aes_ctr_encrypt(struct ablkcipher_request *req)
+{
+       return omap_aes_crypt(req, FLAGS_ENCRYPT | FLAGS_CTR);
+}
+
+static int omap_aes_ctr_decrypt(struct ablkcipher_request *req)
+{
+       return omap_aes_crypt(req, FLAGS_CTR);
+}
+
 static int omap_aes_cra_init(struct crypto_tfm *tfm)
 {
        pr_debug("enter\n");
@@ -750,7 +851,7 @@ static void omap_aes_cra_exit(struct crypto_tfm *tfm)
 
 /* ********************** ALGS ************************************ */
 
-static struct crypto_alg algs[] = {
+static struct crypto_alg algs_ecb_cbc[] = {
 {
        .cra_name               = "ecb(aes)",
        .cra_driver_name        = "ecb-aes-omap",
@@ -798,11 +899,213 @@ static struct crypto_alg algs[] = {
 }
 };
 
+static struct crypto_alg algs_ctr[] = {
+{
+       .cra_name               = "ctr(aes)",
+       .cra_driver_name        = "ctr-aes-omap",
+       .cra_priority           = 100,
+       .cra_flags              = CRYPTO_ALG_TYPE_ABLKCIPHER |
+                                 CRYPTO_ALG_KERN_DRIVER_ONLY |
+                                 CRYPTO_ALG_ASYNC,
+       .cra_blocksize          = AES_BLOCK_SIZE,
+       .cra_ctxsize            = sizeof(struct omap_aes_ctx),
+       .cra_alignmask          = 0,
+       .cra_type               = &crypto_ablkcipher_type,
+       .cra_module             = THIS_MODULE,
+       .cra_init               = omap_aes_cra_init,
+       .cra_exit               = omap_aes_cra_exit,
+       .cra_u.ablkcipher = {
+               .min_keysize    = AES_MIN_KEY_SIZE,
+               .max_keysize    = AES_MAX_KEY_SIZE,
+               .geniv          = "eseqiv",
+               .ivsize         = AES_BLOCK_SIZE,
+               .setkey         = omap_aes_setkey,
+               .encrypt        = omap_aes_ctr_encrypt,
+               .decrypt        = omap_aes_ctr_decrypt,
+       }
+} ,
+};
+
+static struct omap_aes_algs_info omap_aes_algs_info_ecb_cbc[] = {
+       {
+               .algs_list      = algs_ecb_cbc,
+               .size           = ARRAY_SIZE(algs_ecb_cbc),
+       },
+};
+
+static const struct omap_aes_pdata omap_aes_pdata_omap2 = {
+       .algs_info      = omap_aes_algs_info_ecb_cbc,
+       .algs_info_size = ARRAY_SIZE(omap_aes_algs_info_ecb_cbc),
+       .trigger        = omap_aes_dma_trigger_omap2,
+       .key_ofs        = 0x1c,
+       .iv_ofs         = 0x20,
+       .ctrl_ofs       = 0x30,
+       .data_ofs       = 0x34,
+       .rev_ofs        = 0x44,
+       .mask_ofs       = 0x48,
+       .dma_enable_in  = BIT(2),
+       .dma_enable_out = BIT(3),
+       .dma_start      = BIT(5),
+       .major_mask     = 0xf0,
+       .major_shift    = 4,
+       .minor_mask     = 0x0f,
+       .minor_shift    = 0,
+};
+
+#ifdef CONFIG_OF
+static struct omap_aes_algs_info omap_aes_algs_info_ecb_cbc_ctr[] = {
+       {
+               .algs_list      = algs_ecb_cbc,
+               .size           = ARRAY_SIZE(algs_ecb_cbc),
+       },
+       {
+               .algs_list      = algs_ctr,
+               .size           = ARRAY_SIZE(algs_ctr),
+       },
+};
+
+static const struct omap_aes_pdata omap_aes_pdata_omap3 = {
+       .algs_info      = omap_aes_algs_info_ecb_cbc_ctr,
+       .algs_info_size = ARRAY_SIZE(omap_aes_algs_info_ecb_cbc_ctr),
+       .trigger        = omap_aes_dma_trigger_omap2,
+       .key_ofs        = 0x1c,
+       .iv_ofs         = 0x20,
+       .ctrl_ofs       = 0x30,
+       .data_ofs       = 0x34,
+       .rev_ofs        = 0x44,
+       .mask_ofs       = 0x48,
+       .dma_enable_in  = BIT(2),
+       .dma_enable_out = BIT(3),
+       .dma_start      = BIT(5),
+       .major_mask     = 0xf0,
+       .major_shift    = 4,
+       .minor_mask     = 0x0f,
+       .minor_shift    = 0,
+};
+
+static const struct omap_aes_pdata omap_aes_pdata_omap4 = {
+       .algs_info      = omap_aes_algs_info_ecb_cbc_ctr,
+       .algs_info_size = ARRAY_SIZE(omap_aes_algs_info_ecb_cbc_ctr),
+       .trigger        = omap_aes_dma_trigger_omap4,
+       .key_ofs        = 0x3c,
+       .iv_ofs         = 0x40,
+       .ctrl_ofs       = 0x50,
+       .data_ofs       = 0x60,
+       .rev_ofs        = 0x80,
+       .mask_ofs       = 0x84,
+       .dma_enable_in  = BIT(5),
+       .dma_enable_out = BIT(6),
+       .major_mask     = 0x0700,
+       .major_shift    = 8,
+       .minor_mask     = 0x003f,
+       .minor_shift    = 0,
+};
+
+static const struct of_device_id omap_aes_of_match[] = {
+       {
+               .compatible     = "ti,omap2-aes",
+               .data           = &omap_aes_pdata_omap2,
+       },
+       {
+               .compatible     = "ti,omap3-aes",
+               .data           = &omap_aes_pdata_omap3,
+       },
+       {
+               .compatible     = "ti,omap4-aes",
+               .data           = &omap_aes_pdata_omap4,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, omap_aes_of_match);
+
+static int omap_aes_get_res_of(struct omap_aes_dev *dd,
+               struct device *dev, struct resource *res)
+{
+       struct device_node *node = dev->of_node;
+       const struct of_device_id *match;
+       int err = 0;
+
+       match = of_match_device(of_match_ptr(omap_aes_of_match), dev);
+       if (!match) {
+               dev_err(dev, "no compatible OF match\n");
+               err = -EINVAL;
+               goto err;
+       }
+
+       err = of_address_to_resource(node, 0, res);
+       if (err < 0) {
+               dev_err(dev, "can't translate OF node address\n");
+               err = -EINVAL;
+               goto err;
+       }
+
+       dd->dma_out = -1; /* Dummy value that's unused */
+       dd->dma_in = -1; /* Dummy value that's unused */
+
+       dd->pdata = match->data;
+
+err:
+       return err;
+}
+#else
+static const struct of_device_id omap_aes_of_match[] = {
+       {},
+};
+
+static int omap_aes_get_res_of(struct omap_aes_dev *dd,
+               struct device *dev, struct resource *res)
+{
+       return -EINVAL;
+}
+#endif
+
+static int omap_aes_get_res_pdev(struct omap_aes_dev *dd,
+               struct platform_device *pdev, struct resource *res)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *r;
+       int err = 0;
+
+       /* Get the base address */
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(dev, "no MEM resource info\n");
+               err = -ENODEV;
+               goto err;
+       }
+       memcpy(res, r, sizeof(*res));
+
+       /* Get the DMA out channel */
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!r) {
+               dev_err(dev, "no DMA out resource info\n");
+               err = -ENODEV;
+               goto err;
+       }
+       dd->dma_out = r->start;
+
+       /* Get the DMA in channel */
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (!r) {
+               dev_err(dev, "no DMA in resource info\n");
+               err = -ENODEV;
+               goto err;
+       }
+       dd->dma_in = r->start;
+
+       /* Only OMAP2/3 can be non-DT */
+       dd->pdata = &omap_aes_pdata_omap2;
+
+err:
+       return err;
+}
+
 static int omap_aes_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct omap_aes_dev *dd;
-       struct resource *res;
+       struct crypto_alg *algp;
+       struct resource res;
        int err = -ENOMEM, i, j;
        u32 reg;
 
@@ -817,49 +1120,31 @@ static int omap_aes_probe(struct platform_device *pdev)
        spin_lock_init(&dd->lock);
        crypto_init_queue(&dd->queue, OMAP_AES_QUEUE_LENGTH);
 
-       /* Get the base address */
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "invalid resource type\n");
-               err = -ENODEV;
-               goto err_res;
-       }
-       dd->phys_base = res->start;
-
-       /* Get the DMA */
-       res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-       if (!res)
-               dev_info(dev, "no DMA info\n");
-       else
-               dd->dma_out = res->start;
-
-       /* Get the DMA */
-       res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
-       if (!res)
-               dev_info(dev, "no DMA info\n");
-       else
-               dd->dma_in = res->start;
-
-       /* Initializing the clock */
-       dd->iclk = clk_get(dev, "ick");
-       if (IS_ERR(dd->iclk)) {
-               dev_err(dev, "clock intialization failed.\n");
-               err = PTR_ERR(dd->iclk);
+       err = (dev->of_node) ? omap_aes_get_res_of(dd, dev, &res) :
+                              omap_aes_get_res_pdev(dd, pdev, &res);
+       if (err)
                goto err_res;
-       }
 
-       dd->io_base = ioremap(dd->phys_base, SZ_4K);
+       dd->io_base = devm_request_and_ioremap(dev, &res);
        if (!dd->io_base) {
                dev_err(dev, "can't ioremap\n");
                err = -ENOMEM;
-               goto err_io;
+               goto err_res;
        }
+       dd->phys_base = res.start;
+
+       pm_runtime_enable(dev);
+       pm_runtime_get_sync(dev);
+
+       omap_aes_dma_stop(dd);
+
+       reg = omap_aes_read(dd, AES_REG_REV(dd));
+
+       pm_runtime_put_sync(dev);
 
-       clk_enable(dd->iclk);
-       reg = omap_aes_read(dd, AES_REG_REV);
        dev_info(dev, "OMAP AES hw accel rev: %u.%u\n",
-                (reg & AES_REG_REV_MAJOR) >> 4, reg & AES_REG_REV_MINOR);
-       clk_disable(dd->iclk);
+                (reg & dd->pdata->major_mask) >> dd->pdata->major_shift,
+                (reg & dd->pdata->minor_mask) >> dd->pdata->minor_shift);
 
        tasklet_init(&dd->done_task, omap_aes_done_task, (unsigned long)dd);
        tasklet_init(&dd->queue_task, omap_aes_queue_task, (unsigned long)dd);
@@ -873,26 +1158,32 @@ static int omap_aes_probe(struct platform_device *pdev)
        list_add_tail(&dd->list, &dev_list);
        spin_unlock(&list_lock);
 
-       for (i = 0; i < ARRAY_SIZE(algs); i++) {
-               pr_debug("i: %d\n", i);
-               err = crypto_register_alg(&algs[i]);
-               if (err)
-                       goto err_algs;
-       }
+       for (i = 0; i < dd->pdata->algs_info_size; i++) {
+               for (j = 0; j < dd->pdata->algs_info[i].size; j++) {
+                       algp = &dd->pdata->algs_info[i].algs_list[j];
+
+                       pr_debug("reg alg: %s\n", algp->cra_name);
+                       INIT_LIST_HEAD(&algp->cra_list);
+
+                       err = crypto_register_alg(algp);
+                       if (err)
+                               goto err_algs;
 
-       pr_info("probe() done\n");
+                       dd->pdata->algs_info[i].registered++;
+               }
+       }
 
        return 0;
 err_algs:
-       for (j = 0; j < i; j++)
-               crypto_unregister_alg(&algs[j]);
+       for (i = dd->pdata->algs_info_size - 1; i >= 0; i--)
+               for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--)
+                       crypto_unregister_alg(
+                                       &dd->pdata->algs_info[i].algs_list[j]);
        omap_aes_dma_cleanup(dd);
 err_dma:
        tasklet_kill(&dd->done_task);
        tasklet_kill(&dd->queue_task);
-       iounmap(dd->io_base);
-err_io:
-       clk_put(dd->iclk);
+       pm_runtime_disable(dev);
 err_res:
        kfree(dd);
        dd = NULL;
@@ -904,7 +1195,7 @@ err_data:
 static int omap_aes_remove(struct platform_device *pdev)
 {
        struct omap_aes_dev *dd = platform_get_drvdata(pdev);
-       int i;
+       int i, j;
 
        if (!dd)
                return -ENODEV;
@@ -913,33 +1204,52 @@ static int omap_aes_remove(struct platform_device *pdev)
        list_del(&dd->list);
        spin_unlock(&list_lock);
 
-       for (i = 0; i < ARRAY_SIZE(algs); i++)
-               crypto_unregister_alg(&algs[i]);
+       for (i = dd->pdata->algs_info_size - 1; i >= 0; i--)
+               for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--)
+                       crypto_unregister_alg(
+                                       &dd->pdata->algs_info[i].algs_list[j]);
 
        tasklet_kill(&dd->done_task);
        tasklet_kill(&dd->queue_task);
        omap_aes_dma_cleanup(dd);
-       iounmap(dd->io_base);
-       clk_put(dd->iclk);
+       pm_runtime_disable(dd->dev);
        kfree(dd);
        dd = NULL;
 
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int omap_aes_suspend(struct device *dev)
+{
+       pm_runtime_put_sync(dev);
+       return 0;
+}
+
+static int omap_aes_resume(struct device *dev)
+{
+       pm_runtime_get_sync(dev);
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops omap_aes_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(omap_aes_suspend, omap_aes_resume)
+};
+
 static struct platform_driver omap_aes_driver = {
        .probe  = omap_aes_probe,
        .remove = omap_aes_remove,
        .driver = {
                .name   = "omap-aes",
                .owner  = THIS_MODULE,
+               .pm     = &omap_aes_pm_ops,
+               .of_match_table = omap_aes_of_match,
        },
 };
 
 static int __init omap_aes_mod_init(void)
 {
-       pr_info("loading %s driver\n", "omap-aes");
-
        return  platform_driver_register(&omap_aes_driver);
 }
 
index 90d34adc2a66ba36ccfce82142e154db04e87d83..696a7d5271e3ec5198e115067320802d26252968 100644 (file)
@@ -5,6 +5,7 @@
  *
  * Copyright (c) 2010 Nokia Corporation
  * Author: Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
+ * Copyright (c) 2011 Texas Instruments Incorporated
  *
  * 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
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
-#include <linux/clk.h>
 #include <linux/irq.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/scatterlist.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/omap-dma.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
 #include <linux/delay.h>
 #include <linux/crypto.h>
 #include <linux/cryptohash.h>
 #include <crypto/hash.h>
 #include <crypto/internal/hash.h>
 
-#include <linux/omap-dma.h>
-#include <mach/irqs.h>
-
-#define SHA_REG_DIGEST(x)              (0x00 + ((x) * 0x04))
-#define SHA_REG_DIN(x)                 (0x1C + ((x) * 0x04))
-
 #define SHA1_MD5_BLOCK_SIZE            SHA1_BLOCK_SIZE
 #define MD5_DIGEST_SIZE                        16
 
-#define SHA_REG_DIGCNT                 0x14
+#define DST_MAXBURST                   16
+#define DMA_MIN                                (DST_MAXBURST * sizeof(u32))
+
+#define SHA_REG_IDIGEST(dd, x)         ((dd)->pdata->idigest_ofs + ((x)*0x04))
+#define SHA_REG_DIN(dd, x)             ((dd)->pdata->din_ofs + ((x) * 0x04))
+#define SHA_REG_DIGCNT(dd)             ((dd)->pdata->digcnt_ofs)
+
+#define SHA_REG_ODIGEST(x)             (0x00 + ((x) * 0x04))
 
 #define SHA_REG_CTRL                   0x18
 #define SHA_REG_CTRL_LENGTH            (0xFFFFFFFF << 5)
 #define SHA_REG_CTRL_INPUT_READY       (1 << 1)
 #define SHA_REG_CTRL_OUTPUT_READY      (1 << 0)
 
-#define SHA_REG_REV                    0x5C
-#define SHA_REG_REV_MAJOR              0xF0
-#define SHA_REG_REV_MINOR              0x0F
+#define SHA_REG_REV(dd)                        ((dd)->pdata->rev_ofs)
 
-#define SHA_REG_MASK                   0x60
+#define SHA_REG_MASK(dd)               ((dd)->pdata->mask_ofs)
 #define SHA_REG_MASK_DMA_EN            (1 << 3)
 #define SHA_REG_MASK_IT_EN             (1 << 2)
 #define SHA_REG_MASK_SOFTRESET         (1 << 1)
 #define SHA_REG_AUTOIDLE               (1 << 0)
 
-#define SHA_REG_SYSSTATUS              0x64
+#define SHA_REG_SYSSTATUS(dd)          ((dd)->pdata->sysstatus_ofs)
 #define SHA_REG_SYSSTATUS_RESETDONE    (1 << 0)
 
+#define SHA_REG_MODE                   0x44
+#define SHA_REG_MODE_HMAC_OUTER_HASH   (1 << 7)
+#define SHA_REG_MODE_HMAC_KEY_PROC     (1 << 5)
+#define SHA_REG_MODE_CLOSE_HASH                (1 << 4)
+#define SHA_REG_MODE_ALGO_CONSTANT     (1 << 3)
+#define SHA_REG_MODE_ALGO_MASK         (3 << 1)
+#define                SHA_REG_MODE_ALGO_MD5_128       (0 << 1)
+#define                SHA_REG_MODE_ALGO_SHA1_160      (1 << 1)
+#define                SHA_REG_MODE_ALGO_SHA2_224      (2 << 1)
+#define                SHA_REG_MODE_ALGO_SHA2_256      (3 << 1)
+
+#define SHA_REG_LENGTH                 0x48
+
+#define SHA_REG_IRQSTATUS              0x118
+#define SHA_REG_IRQSTATUS_CTX_RDY      (1 << 3)
+#define SHA_REG_IRQSTATUS_PARTHASH_RDY (1 << 2)
+#define SHA_REG_IRQSTATUS_INPUT_RDY    (1 << 1)
+#define SHA_REG_IRQSTATUS_OUTPUT_RDY   (1 << 0)
+
+#define SHA_REG_IRQENA                 0x11C
+#define SHA_REG_IRQENA_CTX_RDY         (1 << 3)
+#define SHA_REG_IRQENA_PARTHASH_RDY    (1 << 2)
+#define SHA_REG_IRQENA_INPUT_RDY       (1 << 1)
+#define SHA_REG_IRQENA_OUTPUT_RDY      (1 << 0)
+
 #define DEFAULT_TIMEOUT_INTERVAL       HZ
 
 /* mostly device flags */
 #define FLAGS_INIT             4
 #define FLAGS_CPU              5
 #define FLAGS_DMA_READY                6
+#define FLAGS_AUTO_XOR         7
+#define FLAGS_BE32_SHA1                8
 /* context flags */
 #define FLAGS_FINUP            16
 #define FLAGS_SG               17
-#define FLAGS_SHA1             18
-#define FLAGS_HMAC             19
-#define FLAGS_ERROR            20
 
-#define OP_UPDATE      1
-#define OP_FINAL       2
+#define FLAGS_MODE_SHIFT       18
+#define FLAGS_MODE_MASK                (SHA_REG_MODE_ALGO_MASK                 \
+                                       << (FLAGS_MODE_SHIFT - 1))
+#define                FLAGS_MODE_MD5          (SHA_REG_MODE_ALGO_MD5_128      \
+                                               << (FLAGS_MODE_SHIFT - 1))
+#define                FLAGS_MODE_SHA1         (SHA_REG_MODE_ALGO_SHA1_160     \
+                                               << (FLAGS_MODE_SHIFT - 1))
+#define                FLAGS_MODE_SHA224       (SHA_REG_MODE_ALGO_SHA2_224     \
+                                               << (FLAGS_MODE_SHIFT - 1))
+#define                FLAGS_MODE_SHA256       (SHA_REG_MODE_ALGO_SHA2_256     \
+                                               << (FLAGS_MODE_SHIFT - 1))
+#define FLAGS_HMAC             20
+#define FLAGS_ERROR            21
+
+#define OP_UPDATE              1
+#define OP_FINAL               2
 
 #define OMAP_ALIGN_MASK                (sizeof(u32)-1)
 #define OMAP_ALIGNED           __attribute__((aligned(sizeof(u32))))
 
-#define BUFLEN         PAGE_SIZE
+#define BUFLEN                 PAGE_SIZE
 
 struct omap_sham_dev;
 
@@ -101,7 +145,7 @@ struct omap_sham_reqctx {
        unsigned long           flags;
        unsigned long           op;
 
-       u8                      digest[SHA1_DIGEST_SIZE] OMAP_ALIGNED;
+       u8                      digest[SHA256_DIGEST_SIZE] OMAP_ALIGNED;
        size_t                  digcnt;
        size_t                  bufcnt;
        size_t                  buflen;
@@ -109,6 +153,7 @@ struct omap_sham_reqctx {
 
        /* walk state */
        struct scatterlist      *sg;
+       struct scatterlist      sgl;
        unsigned int            offset; /* offset in current sg */
        unsigned int            total;  /* total request */
 
@@ -117,8 +162,8 @@ struct omap_sham_reqctx {
 
 struct omap_sham_hmac_ctx {
        struct crypto_shash     *shash;
-       u8                      ipad[SHA1_MD5_BLOCK_SIZE];
-       u8                      opad[SHA1_MD5_BLOCK_SIZE];
+       u8                      ipad[SHA1_MD5_BLOCK_SIZE] OMAP_ALIGNED;
+       u8                      opad[SHA1_MD5_BLOCK_SIZE] OMAP_ALIGNED;
 };
 
 struct omap_sham_ctx {
@@ -134,22 +179,56 @@ struct omap_sham_ctx {
 
 #define OMAP_SHAM_QUEUE_LENGTH 1
 
+struct omap_sham_algs_info {
+       struct ahash_alg        *algs_list;
+       unsigned int            size;
+       unsigned int            registered;
+};
+
+struct omap_sham_pdata {
+       struct omap_sham_algs_info      *algs_info;
+       unsigned int    algs_info_size;
+       unsigned long   flags;
+       int             digest_size;
+
+       void            (*copy_hash)(struct ahash_request *req, int out);
+       void            (*write_ctrl)(struct omap_sham_dev *dd, size_t length,
+                                     int final, int dma);
+       void            (*trigger)(struct omap_sham_dev *dd, size_t length);
+       int             (*poll_irq)(struct omap_sham_dev *dd);
+       irqreturn_t     (*intr_hdlr)(int irq, void *dev_id);
+
+       u32             odigest_ofs;
+       u32             idigest_ofs;
+       u32             din_ofs;
+       u32             digcnt_ofs;
+       u32             rev_ofs;
+       u32             mask_ofs;
+       u32             sysstatus_ofs;
+
+       u32             major_mask;
+       u32             major_shift;
+       u32             minor_mask;
+       u32             minor_shift;
+};
+
 struct omap_sham_dev {
        struct list_head        list;
        unsigned long           phys_base;
        struct device           *dev;
        void __iomem            *io_base;
        int                     irq;
-       struct clk              *iclk;
        spinlock_t              lock;
        int                     err;
-       int                     dma;
-       int                     dma_lch;
+       unsigned int            dma;
+       struct dma_chan         *dma_lch;
        struct tasklet_struct   done_task;
 
        unsigned long           flags;
        struct crypto_queue     queue;
        struct ahash_request    *req;
+
+       const struct omap_sham_pdata    *pdata;
 };
 
 struct omap_sham_drv {
@@ -197,21 +276,44 @@ static inline int omap_sham_wait(struct omap_sham_dev *dd, u32 offset, u32 bit)
        return 0;
 }
 
-static void omap_sham_copy_hash(struct ahash_request *req, int out)
+static void omap_sham_copy_hash_omap2(struct ahash_request *req, int out)
 {
        struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
+       struct omap_sham_dev *dd = ctx->dd;
        u32 *hash = (u32 *)ctx->digest;
        int i;
 
-       /* MD5 is almost unused. So copy sha1 size to reduce code */
-       for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++) {
+       for (i = 0; i < dd->pdata->digest_size / sizeof(u32); i++) {
                if (out)
-                       hash[i] = omap_sham_read(ctx->dd,
-                                               SHA_REG_DIGEST(i));
+                       hash[i] = omap_sham_read(dd, SHA_REG_IDIGEST(dd, i));
                else
-                       omap_sham_write(ctx->dd,
-                                       SHA_REG_DIGEST(i), hash[i]);
+                       omap_sham_write(dd, SHA_REG_IDIGEST(dd, i), hash[i]);
+       }
+}
+
+static void omap_sham_copy_hash_omap4(struct ahash_request *req, int out)
+{
+       struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
+       struct omap_sham_dev *dd = ctx->dd;
+       int i;
+
+       if (ctx->flags & BIT(FLAGS_HMAC)) {
+               struct crypto_ahash *tfm = crypto_ahash_reqtfm(dd->req);
+               struct omap_sham_ctx *tctx = crypto_ahash_ctx(tfm);
+               struct omap_sham_hmac_ctx *bctx = tctx->base;
+               u32 *opad = (u32 *)bctx->opad;
+
+               for (i = 0; i < dd->pdata->digest_size / sizeof(u32); i++) {
+                       if (out)
+                               opad[i] = omap_sham_read(dd,
+                                               SHA_REG_ODIGEST(i));
+                       else
+                               omap_sham_write(dd, SHA_REG_ODIGEST(i),
+                                               opad[i]);
+               }
        }
+
+       omap_sham_copy_hash_omap2(req, out);
 }
 
 static void omap_sham_copy_ready_hash(struct ahash_request *req)
@@ -219,34 +321,44 @@ static void omap_sham_copy_ready_hash(struct ahash_request *req)
        struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
        u32 *in = (u32 *)ctx->digest;
        u32 *hash = (u32 *)req->result;
-       int i;
+       int i, d, big_endian = 0;
 
        if (!hash)
                return;
 
-       if (likely(ctx->flags & BIT(FLAGS_SHA1))) {
-               /* SHA1 results are in big endian */
-               for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++)
+       switch (ctx->flags & FLAGS_MODE_MASK) {
+       case FLAGS_MODE_MD5:
+               d = MD5_DIGEST_SIZE / sizeof(u32);
+               break;
+       case FLAGS_MODE_SHA1:
+               /* OMAP2 SHA1 is big endian */
+               if (test_bit(FLAGS_BE32_SHA1, &ctx->dd->flags))
+                       big_endian = 1;
+               d = SHA1_DIGEST_SIZE / sizeof(u32);
+               break;
+       case FLAGS_MODE_SHA224:
+               d = SHA224_DIGEST_SIZE / sizeof(u32);
+               break;
+       case FLAGS_MODE_SHA256:
+               d = SHA256_DIGEST_SIZE / sizeof(u32);
+               break;
+       default:
+               d = 0;
+       }
+
+       if (big_endian)
+               for (i = 0; i < d; i++)
                        hash[i] = be32_to_cpu(in[i]);
-       } else {
-               /* MD5 results are in little endian */
-               for (i = 0; i < MD5_DIGEST_SIZE / sizeof(u32); i++)
+       else
+               for (i = 0; i < d; i++)
                        hash[i] = le32_to_cpu(in[i]);
-       }
 }
 
 static int omap_sham_hw_init(struct omap_sham_dev *dd)
 {
-       clk_enable(dd->iclk);
+       pm_runtime_get_sync(dd->dev);
 
        if (!test_bit(FLAGS_INIT, &dd->flags)) {
-               omap_sham_write_mask(dd, SHA_REG_MASK,
-                       SHA_REG_MASK_SOFTRESET, SHA_REG_MASK_SOFTRESET);
-
-               if (omap_sham_wait(dd, SHA_REG_SYSSTATUS,
-                                       SHA_REG_SYSSTATUS_RESETDONE))
-                       return -ETIMEDOUT;
-
                set_bit(FLAGS_INIT, &dd->flags);
                dd->err = 0;
        }
@@ -254,23 +366,23 @@ static int omap_sham_hw_init(struct omap_sham_dev *dd)
        return 0;
 }
 
-static void omap_sham_write_ctrl(struct omap_sham_dev *dd, size_t length,
+static void omap_sham_write_ctrl_omap2(struct omap_sham_dev *dd, size_t length,
                                 int final, int dma)
 {
        struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
        u32 val = length << 5, mask;
 
        if (likely(ctx->digcnt))
-               omap_sham_write(dd, SHA_REG_DIGCNT, ctx->digcnt);
+               omap_sham_write(dd, SHA_REG_DIGCNT(dd), ctx->digcnt);
 
-       omap_sham_write_mask(dd, SHA_REG_MASK,
+       omap_sham_write_mask(dd, SHA_REG_MASK(dd),
                SHA_REG_MASK_IT_EN | (dma ? SHA_REG_MASK_DMA_EN : 0),
                SHA_REG_MASK_IT_EN | SHA_REG_MASK_DMA_EN);
        /*
         * Setting ALGO_CONST only for the first iteration
         * and CLOSE_HASH only for the last one.
         */
-       if (ctx->flags & BIT(FLAGS_SHA1))
+       if ((ctx->flags & FLAGS_MODE_MASK) == FLAGS_MODE_SHA1)
                val |= SHA_REG_CTRL_ALGO;
        if (!ctx->digcnt)
                val |= SHA_REG_CTRL_ALGO_CONST;
@@ -283,6 +395,81 @@ static void omap_sham_write_ctrl(struct omap_sham_dev *dd, size_t length,
        omap_sham_write_mask(dd, SHA_REG_CTRL, val, mask);
 }
 
+static void omap_sham_trigger_omap2(struct omap_sham_dev *dd, size_t length)
+{
+}
+
+static int omap_sham_poll_irq_omap2(struct omap_sham_dev *dd)
+{
+       return omap_sham_wait(dd, SHA_REG_CTRL, SHA_REG_CTRL_INPUT_READY);
+}
+
+static void omap_sham_write_n(struct omap_sham_dev *dd, u32 offset,
+                                   u32 *value, int count)
+{
+       for (; count--; value++, offset += 4)
+               omap_sham_write(dd, offset, *value);
+}
+
+static void omap_sham_write_ctrl_omap4(struct omap_sham_dev *dd, size_t length,
+                                int final, int dma)
+{
+       struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
+       u32 val, mask;
+
+       /*
+        * Setting ALGO_CONST only for the first iteration and
+        * CLOSE_HASH only for the last one. Note that flags mode bits
+        * correspond to algorithm encoding in mode register.
+        */
+       val = (ctx->flags & FLAGS_MODE_MASK) >> (FLAGS_MODE_SHIFT - 1);
+       if (!ctx->digcnt) {
+               struct crypto_ahash *tfm = crypto_ahash_reqtfm(dd->req);
+               struct omap_sham_ctx *tctx = crypto_ahash_ctx(tfm);
+               struct omap_sham_hmac_ctx *bctx = tctx->base;
+
+               val |= SHA_REG_MODE_ALGO_CONSTANT;
+
+               if (ctx->flags & BIT(FLAGS_HMAC)) {
+                       val |= SHA_REG_MODE_HMAC_KEY_PROC;
+                       omap_sham_write_n(dd, SHA_REG_ODIGEST(0),
+                                         (u32 *)bctx->ipad,
+                                         SHA1_BLOCK_SIZE / sizeof(u32));
+                       ctx->digcnt += SHA1_BLOCK_SIZE;
+               }
+       }
+
+       if (final) {
+               val |= SHA_REG_MODE_CLOSE_HASH;
+
+               if (ctx->flags & BIT(FLAGS_HMAC))
+                       val |= SHA_REG_MODE_HMAC_OUTER_HASH;
+       }
+
+       mask = SHA_REG_MODE_ALGO_CONSTANT | SHA_REG_MODE_CLOSE_HASH |
+              SHA_REG_MODE_ALGO_MASK | SHA_REG_MODE_HMAC_OUTER_HASH |
+              SHA_REG_MODE_HMAC_KEY_PROC;
+
+       dev_dbg(dd->dev, "ctrl: %08x, flags: %08lx\n", val, ctx->flags);
+       omap_sham_write_mask(dd, SHA_REG_MODE, val, mask);
+       omap_sham_write(dd, SHA_REG_IRQENA, SHA_REG_IRQENA_OUTPUT_RDY);
+       omap_sham_write_mask(dd, SHA_REG_MASK(dd),
+                            SHA_REG_MASK_IT_EN |
+                                    (dma ? SHA_REG_MASK_DMA_EN : 0),
+                            SHA_REG_MASK_IT_EN | SHA_REG_MASK_DMA_EN);
+}
+
+static void omap_sham_trigger_omap4(struct omap_sham_dev *dd, size_t length)
+{
+       omap_sham_write(dd, SHA_REG_LENGTH, length);
+}
+
+static int omap_sham_poll_irq_omap4(struct omap_sham_dev *dd)
+{
+       return omap_sham_wait(dd, SHA_REG_IRQSTATUS,
+                             SHA_REG_IRQSTATUS_INPUT_RDY);
+}
+
 static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf,
                              size_t length, int final)
 {
@@ -293,12 +480,13 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf,
        dev_dbg(dd->dev, "xmit_cpu: digcnt: %d, length: %d, final: %d\n",
                                                ctx->digcnt, length, final);
 
-       omap_sham_write_ctrl(dd, length, final, 0);
+       dd->pdata->write_ctrl(dd, length, final, 0);
+       dd->pdata->trigger(dd, length);
 
        /* should be non-zero before next lines to disable clocks later */
        ctx->digcnt += length;
 
-       if (omap_sham_wait(dd, SHA_REG_CTRL, SHA_REG_CTRL_INPUT_READY))
+       if (dd->pdata->poll_irq(dd))
                return -ETIMEDOUT;
 
        if (final)
@@ -309,30 +497,73 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf,
        len32 = DIV_ROUND_UP(length, sizeof(u32));
 
        for (count = 0; count < len32; count++)
-               omap_sham_write(dd, SHA_REG_DIN(count), buffer[count]);
+               omap_sham_write(dd, SHA_REG_DIN(dd, count), buffer[count]);
 
        return -EINPROGRESS;
 }
 
+static void omap_sham_dma_callback(void *param)
+{
+       struct omap_sham_dev *dd = param;
+
+       set_bit(FLAGS_DMA_READY, &dd->flags);
+       tasklet_schedule(&dd->done_task);
+}
+
 static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr,
-                             size_t length, int final)
+                             size_t length, int final, int is_sg)
 {
        struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
-       int len32;
+       struct dma_async_tx_descriptor *tx;
+       struct dma_slave_config cfg;
+       int len32, ret;
 
        dev_dbg(dd->dev, "xmit_dma: digcnt: %d, length: %d, final: %d\n",
                                                ctx->digcnt, length, final);
 
-       len32 = DIV_ROUND_UP(length, sizeof(u32));
+       memset(&cfg, 0, sizeof(cfg));
+
+       cfg.dst_addr = dd->phys_base + SHA_REG_DIN(dd, 0);
+       cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       cfg.dst_maxburst = DST_MAXBURST;
+
+       ret = dmaengine_slave_config(dd->dma_lch, &cfg);
+       if (ret) {
+               pr_err("omap-sham: can't configure dmaengine slave: %d\n", ret);
+               return ret;
+       }
 
-       omap_set_dma_transfer_params(dd->dma_lch, OMAP_DMA_DATA_TYPE_S32, len32,
-                       1, OMAP_DMA_SYNC_PACKET, dd->dma,
-                               OMAP_DMA_DST_SYNC_PREFETCH);
+       len32 = DIV_ROUND_UP(length, DMA_MIN) * DMA_MIN;
+
+       if (is_sg) {
+               /*
+                * The SG entry passed in may not have the 'length' member
+                * set correctly so use a local SG entry (sgl) with the
+                * proper value for 'length' instead.  If this is not done,
+                * the dmaengine may try to DMA the incorrect amount of data.
+                */
+               sg_init_table(&ctx->sgl, 1);
+               ctx->sgl.page_link = ctx->sg->page_link;
+               ctx->sgl.offset = ctx->sg->offset;
+               sg_dma_len(&ctx->sgl) = len32;
+               sg_dma_address(&ctx->sgl) = sg_dma_address(ctx->sg);
+
+               tx = dmaengine_prep_slave_sg(dd->dma_lch, &ctx->sgl, 1,
+                       DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       } else {
+               tx = dmaengine_prep_slave_single(dd->dma_lch, dma_addr, len32,
+                       DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       }
+
+       if (!tx) {
+               dev_err(dd->dev, "prep_slave_sg/single() failed\n");
+               return -EINVAL;
+       }
 
-       omap_set_dma_src_params(dd->dma_lch, 0, OMAP_DMA_AMODE_POST_INC,
-                               dma_addr, 0, 0);
+       tx->callback = omap_sham_dma_callback;
+       tx->callback_param = dd;
 
-       omap_sham_write_ctrl(dd, length, final, 1);
+       dd->pdata->write_ctrl(dd, length, final, 1);
 
        ctx->digcnt += length;
 
@@ -341,7 +572,10 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr,
 
        set_bit(FLAGS_DMA_ACTIVE, &dd->flags);
 
-       omap_start_dma(dd->dma_lch);
+       dmaengine_submit(tx);
+       dma_async_issue_pending(dd->dma_lch);
+
+       dd->pdata->trigger(dd, length);
 
        return -EINPROGRESS;
 }
@@ -388,6 +622,8 @@ static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd,
                                        struct omap_sham_reqctx *ctx,
                                        size_t length, int final)
 {
+       int ret;
+
        ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer, ctx->buflen,
                                       DMA_TO_DEVICE);
        if (dma_mapping_error(dd->dev, ctx->dma_addr)) {
@@ -397,8 +633,12 @@ static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd,
 
        ctx->flags &= ~BIT(FLAGS_SG);
 
-       /* next call does not fail... so no unmap in the case of error */
-       return omap_sham_xmit_dma(dd, ctx->dma_addr, length, final);
+       ret = omap_sham_xmit_dma(dd, ctx->dma_addr, length, final, 0);
+       if (ret != -EINPROGRESS)
+               dma_unmap_single(dd->dev, ctx->dma_addr, ctx->buflen,
+                                DMA_TO_DEVICE);
+
+       return ret;
 }
 
 static int omap_sham_update_dma_slow(struct omap_sham_dev *dd)
@@ -433,6 +673,7 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd)
        struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
        unsigned int length, final, tail;
        struct scatterlist *sg;
+       int ret;
 
        if (!ctx->total)
                return 0;
@@ -440,6 +681,15 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd)
        if (ctx->bufcnt || ctx->offset)
                return omap_sham_update_dma_slow(dd);
 
+       /*
+        * Don't use the sg interface when the transfer size is less
+        * than the number of elements in a DMA frame.  Otherwise,
+        * the dmaengine infrastructure will calculate that it needs
+        * to transfer 0 frames which ultimately fails.
+        */
+       if (ctx->total < (DST_MAXBURST * sizeof(u32)))
+               return omap_sham_update_dma_slow(dd);
+
        dev_dbg(dd->dev, "fast: digcnt: %d, bufcnt: %u, total: %u\n",
                        ctx->digcnt, ctx->bufcnt, ctx->total);
 
@@ -477,8 +727,11 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd)
 
        final = (ctx->flags & BIT(FLAGS_FINUP)) && !ctx->total;
 
-       /* next call does not fail... so no unmap in the case of error */
-       return omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final);
+       ret = omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final, 1);
+       if (ret != -EINPROGRESS)
+               dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE);
+
+       return ret;
 }
 
 static int omap_sham_update_cpu(struct omap_sham_dev *dd)
@@ -497,7 +750,8 @@ static int omap_sham_update_dma_stop(struct omap_sham_dev *dd)
 {
        struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
 
-       omap_stop_dma(dd->dma_lch);
+       dmaengine_terminate_all(dd->dma_lch);
+
        if (ctx->flags & BIT(FLAGS_SG)) {
                dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE);
                if (ctx->sg->length == ctx->offset) {
@@ -539,18 +793,33 @@ static int omap_sham_init(struct ahash_request *req)
        dev_dbg(dd->dev, "init: digest size: %d\n",
                crypto_ahash_digestsize(tfm));
 
-       if (crypto_ahash_digestsize(tfm) == SHA1_DIGEST_SIZE)
-               ctx->flags |= BIT(FLAGS_SHA1);
+       switch (crypto_ahash_digestsize(tfm)) {
+       case MD5_DIGEST_SIZE:
+               ctx->flags |= FLAGS_MODE_MD5;
+               break;
+       case SHA1_DIGEST_SIZE:
+               ctx->flags |= FLAGS_MODE_SHA1;
+               break;
+       case SHA224_DIGEST_SIZE:
+               ctx->flags |= FLAGS_MODE_SHA224;
+               break;
+       case SHA256_DIGEST_SIZE:
+               ctx->flags |= FLAGS_MODE_SHA256;
+               break;
+       }
 
        ctx->bufcnt = 0;
        ctx->digcnt = 0;
        ctx->buflen = BUFLEN;
 
        if (tctx->flags & BIT(FLAGS_HMAC)) {
-               struct omap_sham_hmac_ctx *bctx = tctx->base;
+               if (!test_bit(FLAGS_AUTO_XOR, &dd->flags)) {
+                       struct omap_sham_hmac_ctx *bctx = tctx->base;
+
+                       memcpy(ctx->buffer, bctx->ipad, SHA1_MD5_BLOCK_SIZE);
+                       ctx->bufcnt = SHA1_MD5_BLOCK_SIZE;
+               }
 
-               memcpy(ctx->buffer, bctx->ipad, SHA1_MD5_BLOCK_SIZE);
-               ctx->bufcnt = SHA1_MD5_BLOCK_SIZE;
                ctx->flags |= BIT(FLAGS_HMAC);
        }
 
@@ -584,7 +853,7 @@ static int omap_sham_final_req(struct omap_sham_dev *dd)
        struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
        int err = 0, use_dma = 1;
 
-       if (ctx->bufcnt <= 64)
+       if (ctx->bufcnt <= DMA_MIN)
                /* faster to handle last block with cpu */
                use_dma = 0;
 
@@ -627,7 +896,8 @@ static int omap_sham_finish(struct ahash_request *req)
 
        if (ctx->digcnt) {
                omap_sham_copy_ready_hash(req);
-               if (ctx->flags & BIT(FLAGS_HMAC))
+               if ((ctx->flags & BIT(FLAGS_HMAC)) &&
+                               !test_bit(FLAGS_AUTO_XOR, &dd->flags))
                        err = omap_sham_finish_hmac(req);
        }
 
@@ -642,7 +912,7 @@ static void omap_sham_finish_req(struct ahash_request *req, int err)
        struct omap_sham_dev *dd = ctx->dd;
 
        if (!err) {
-               omap_sham_copy_hash(req, 1);
+               dd->pdata->copy_hash(req, 1);
                if (test_bit(FLAGS_FINAL, &dd->flags))
                        err = omap_sham_finish(req);
        } else {
@@ -652,7 +922,8 @@ static void omap_sham_finish_req(struct ahash_request *req, int err)
        /* atomic operation is not needed here */
        dd->flags &= ~(BIT(FLAGS_BUSY) | BIT(FLAGS_FINAL) | BIT(FLAGS_CPU) |
                        BIT(FLAGS_DMA_READY) | BIT(FLAGS_OUTPUT_READY));
-       clk_disable(dd->iclk);
+
+       pm_runtime_put_sync(dd->dev);
 
        if (req->base.complete)
                req->base.complete(&req->base, err);
@@ -699,19 +970,9 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd,
        if (err)
                goto err1;
 
-       omap_set_dma_dest_params(dd->dma_lch, 0,
-                       OMAP_DMA_AMODE_CONSTANT,
-                       dd->phys_base + SHA_REG_DIN(0), 0, 16);
-
-       omap_set_dma_dest_burst_mode(dd->dma_lch,
-                       OMAP_DMA_DATA_BURST_16);
-
-       omap_set_dma_src_burst_mode(dd->dma_lch,
-                       OMAP_DMA_DATA_BURST_4);
-
        if (ctx->digcnt)
                /* request has changed - restore hash */
-               omap_sham_copy_hash(req, 0);
+               dd->pdata->copy_hash(req, 0);
 
        if (ctx->op == OP_UPDATE) {
                err = omap_sham_update_req(dd);
@@ -850,7 +1111,21 @@ static int omap_sham_setkey(struct crypto_ahash *tfm, const u8 *key,
        struct omap_sham_hmac_ctx *bctx = tctx->base;
        int bs = crypto_shash_blocksize(bctx->shash);
        int ds = crypto_shash_digestsize(bctx->shash);
+       struct omap_sham_dev *dd = NULL, *tmp;
        int err, i;
+
+       spin_lock_bh(&sham.lock);
+       if (!tctx->dd) {
+               list_for_each_entry(tmp, &sham.dev_list, list) {
+                       dd = tmp;
+                       break;
+               }
+               tctx->dd = dd;
+       } else {
+               dd = tctx->dd;
+       }
+       spin_unlock_bh(&sham.lock);
+
        err = crypto_shash_setkey(tctx->fallback, key, keylen);
        if (err)
                return err;
@@ -867,11 +1142,14 @@ static int omap_sham_setkey(struct crypto_ahash *tfm, const u8 *key,
        }
 
        memset(bctx->ipad + keylen, 0, bs - keylen);
-       memcpy(bctx->opad, bctx->ipad, bs);
 
-       for (i = 0; i < bs; i++) {
-               bctx->ipad[i] ^= 0x36;
-               bctx->opad[i] ^= 0x5c;
+       if (!test_bit(FLAGS_AUTO_XOR, &dd->flags)) {
+               memcpy(bctx->opad, bctx->ipad, bs);
+
+               for (i = 0; i < bs; i++) {
+                       bctx->ipad[i] ^= 0x36;
+                       bctx->opad[i] ^= 0x5c;
+               }
        }
 
        return err;
@@ -921,6 +1199,16 @@ static int omap_sham_cra_sha1_init(struct crypto_tfm *tfm)
        return omap_sham_cra_init_alg(tfm, "sha1");
 }
 
+static int omap_sham_cra_sha224_init(struct crypto_tfm *tfm)
+{
+       return omap_sham_cra_init_alg(tfm, "sha224");
+}
+
+static int omap_sham_cra_sha256_init(struct crypto_tfm *tfm)
+{
+       return omap_sham_cra_init_alg(tfm, "sha256");
+}
+
 static int omap_sham_cra_md5_init(struct crypto_tfm *tfm)
 {
        return omap_sham_cra_init_alg(tfm, "md5");
@@ -939,7 +1227,7 @@ static void omap_sham_cra_exit(struct crypto_tfm *tfm)
        }
 }
 
-static struct ahash_alg algs[] = {
+static struct ahash_alg algs_sha1_md5[] = {
 {
        .init           = omap_sham_init,
        .update         = omap_sham_update,
@@ -1038,6 +1326,102 @@ static struct ahash_alg algs[] = {
 }
 };
 
+/* OMAP4 has some algs in addition to what OMAP2 has */
+static struct ahash_alg algs_sha224_sha256[] = {
+{
+       .init           = omap_sham_init,
+       .update         = omap_sham_update,
+       .final          = omap_sham_final,
+       .finup          = omap_sham_finup,
+       .digest         = omap_sham_digest,
+       .halg.digestsize        = SHA224_DIGEST_SIZE,
+       .halg.base      = {
+               .cra_name               = "sha224",
+               .cra_driver_name        = "omap-sha224",
+               .cra_priority           = 100,
+               .cra_flags              = CRYPTO_ALG_TYPE_AHASH |
+                                               CRYPTO_ALG_ASYNC |
+                                               CRYPTO_ALG_NEED_FALLBACK,
+               .cra_blocksize          = SHA224_BLOCK_SIZE,
+               .cra_ctxsize            = sizeof(struct omap_sham_ctx),
+               .cra_alignmask          = 0,
+               .cra_module             = THIS_MODULE,
+               .cra_init               = omap_sham_cra_init,
+               .cra_exit               = omap_sham_cra_exit,
+       }
+},
+{
+       .init           = omap_sham_init,
+       .update         = omap_sham_update,
+       .final          = omap_sham_final,
+       .finup          = omap_sham_finup,
+       .digest         = omap_sham_digest,
+       .halg.digestsize        = SHA256_DIGEST_SIZE,
+       .halg.base      = {
+               .cra_name               = "sha256",
+               .cra_driver_name        = "omap-sha256",
+               .cra_priority           = 100,
+               .cra_flags              = CRYPTO_ALG_TYPE_AHASH |
+                                               CRYPTO_ALG_ASYNC |
+                                               CRYPTO_ALG_NEED_FALLBACK,
+               .cra_blocksize          = SHA256_BLOCK_SIZE,
+               .cra_ctxsize            = sizeof(struct omap_sham_ctx),
+               .cra_alignmask          = 0,
+               .cra_module             = THIS_MODULE,
+               .cra_init               = omap_sham_cra_init,
+               .cra_exit               = omap_sham_cra_exit,
+       }
+},
+{
+       .init           = omap_sham_init,
+       .update         = omap_sham_update,
+       .final          = omap_sham_final,
+       .finup          = omap_sham_finup,
+       .digest         = omap_sham_digest,
+       .setkey         = omap_sham_setkey,
+       .halg.digestsize        = SHA224_DIGEST_SIZE,
+       .halg.base      = {
+               .cra_name               = "hmac(sha224)",
+               .cra_driver_name        = "omap-hmac-sha224",
+               .cra_priority           = 100,
+               .cra_flags              = CRYPTO_ALG_TYPE_AHASH |
+                                               CRYPTO_ALG_ASYNC |
+                                               CRYPTO_ALG_NEED_FALLBACK,
+               .cra_blocksize          = SHA224_BLOCK_SIZE,
+               .cra_ctxsize            = sizeof(struct omap_sham_ctx) +
+                                       sizeof(struct omap_sham_hmac_ctx),
+               .cra_alignmask          = OMAP_ALIGN_MASK,
+               .cra_module             = THIS_MODULE,
+               .cra_init               = omap_sham_cra_sha224_init,
+               .cra_exit               = omap_sham_cra_exit,
+       }
+},
+{
+       .init           = omap_sham_init,
+       .update         = omap_sham_update,
+       .final          = omap_sham_final,
+       .finup          = omap_sham_finup,
+       .digest         = omap_sham_digest,
+       .setkey         = omap_sham_setkey,
+       .halg.digestsize        = SHA256_DIGEST_SIZE,
+       .halg.base      = {
+               .cra_name               = "hmac(sha256)",
+               .cra_driver_name        = "omap-hmac-sha256",
+               .cra_priority           = 100,
+               .cra_flags              = CRYPTO_ALG_TYPE_AHASH |
+                                               CRYPTO_ALG_ASYNC |
+                                               CRYPTO_ALG_NEED_FALLBACK,
+               .cra_blocksize          = SHA256_BLOCK_SIZE,
+               .cra_ctxsize            = sizeof(struct omap_sham_ctx) +
+                                       sizeof(struct omap_sham_hmac_ctx),
+               .cra_alignmask          = OMAP_ALIGN_MASK,
+               .cra_module             = THIS_MODULE,
+               .cra_init               = omap_sham_cra_sha256_init,
+               .cra_exit               = omap_sham_cra_exit,
+       }
+},
+};
+
 static void omap_sham_done_task(unsigned long data)
 {
        struct omap_sham_dev *dd = (struct omap_sham_dev *)data;
@@ -1076,7 +1460,19 @@ finish:
        omap_sham_finish_req(dd->req, err);
 }
 
-static irqreturn_t omap_sham_irq(int irq, void *dev_id)
+static irqreturn_t omap_sham_irq_common(struct omap_sham_dev *dd)
+{
+       if (!test_bit(FLAGS_BUSY, &dd->flags)) {
+               dev_warn(dd->dev, "Interrupt when no active requests.\n");
+       } else {
+               set_bit(FLAGS_OUTPUT_READY, &dd->flags);
+               tasklet_schedule(&dd->done_task);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t omap_sham_irq_omap2(int irq, void *dev_id)
 {
        struct omap_sham_dev *dd = dev_id;
 
@@ -1088,61 +1484,184 @@ static irqreturn_t omap_sham_irq(int irq, void *dev_id)
                                 SHA_REG_CTRL_OUTPUT_READY);
        omap_sham_read(dd, SHA_REG_CTRL);
 
-       if (!test_bit(FLAGS_BUSY, &dd->flags)) {
-               dev_warn(dd->dev, "Interrupt when no active requests.\n");
-               return IRQ_HANDLED;
-       }
-
-       set_bit(FLAGS_OUTPUT_READY, &dd->flags);
-       tasklet_schedule(&dd->done_task);
-
-       return IRQ_HANDLED;
+       return omap_sham_irq_common(dd);
 }
 
-static void omap_sham_dma_callback(int lch, u16 ch_status, void *data)
+static irqreturn_t omap_sham_irq_omap4(int irq, void *dev_id)
 {
-       struct omap_sham_dev *dd = data;
+       struct omap_sham_dev *dd = dev_id;
 
-       if (ch_status != OMAP_DMA_BLOCK_IRQ) {
-               pr_err("omap-sham DMA error status: 0x%hx\n", ch_status);
-               dd->err = -EIO;
-               clear_bit(FLAGS_INIT, &dd->flags);/* request to re-initialize */
-       }
+       omap_sham_write_mask(dd, SHA_REG_MASK(dd), 0, SHA_REG_MASK_IT_EN);
 
-       set_bit(FLAGS_DMA_READY, &dd->flags);
-       tasklet_schedule(&dd->done_task);
+       return omap_sham_irq_common(dd);
 }
 
-static int omap_sham_dma_init(struct omap_sham_dev *dd)
+static struct omap_sham_algs_info omap_sham_algs_info_omap2[] = {
+       {
+               .algs_list      = algs_sha1_md5,
+               .size           = ARRAY_SIZE(algs_sha1_md5),
+       },
+};
+
+static const struct omap_sham_pdata omap_sham_pdata_omap2 = {
+       .algs_info      = omap_sham_algs_info_omap2,
+       .algs_info_size = ARRAY_SIZE(omap_sham_algs_info_omap2),
+       .flags          = BIT(FLAGS_BE32_SHA1),
+       .digest_size    = SHA1_DIGEST_SIZE,
+       .copy_hash      = omap_sham_copy_hash_omap2,
+       .write_ctrl     = omap_sham_write_ctrl_omap2,
+       .trigger        = omap_sham_trigger_omap2,
+       .poll_irq       = omap_sham_poll_irq_omap2,
+       .intr_hdlr      = omap_sham_irq_omap2,
+       .idigest_ofs    = 0x00,
+       .din_ofs        = 0x1c,
+       .digcnt_ofs     = 0x14,
+       .rev_ofs        = 0x5c,
+       .mask_ofs       = 0x60,
+       .sysstatus_ofs  = 0x64,
+       .major_mask     = 0xf0,
+       .major_shift    = 4,
+       .minor_mask     = 0x0f,
+       .minor_shift    = 0,
+};
+
+#ifdef CONFIG_OF
+static struct omap_sham_algs_info omap_sham_algs_info_omap4[] = {
+       {
+               .algs_list      = algs_sha1_md5,
+               .size           = ARRAY_SIZE(algs_sha1_md5),
+       },
+       {
+               .algs_list      = algs_sha224_sha256,
+               .size           = ARRAY_SIZE(algs_sha224_sha256),
+       },
+};
+
+static const struct omap_sham_pdata omap_sham_pdata_omap4 = {
+       .algs_info      = omap_sham_algs_info_omap4,
+       .algs_info_size = ARRAY_SIZE(omap_sham_algs_info_omap4),
+       .flags          = BIT(FLAGS_AUTO_XOR),
+       .digest_size    = SHA256_DIGEST_SIZE,
+       .copy_hash      = omap_sham_copy_hash_omap4,
+       .write_ctrl     = omap_sham_write_ctrl_omap4,
+       .trigger        = omap_sham_trigger_omap4,
+       .poll_irq       = omap_sham_poll_irq_omap4,
+       .intr_hdlr      = omap_sham_irq_omap4,
+       .idigest_ofs    = 0x020,
+       .din_ofs        = 0x080,
+       .digcnt_ofs     = 0x040,
+       .rev_ofs        = 0x100,
+       .mask_ofs       = 0x110,
+       .sysstatus_ofs  = 0x114,
+       .major_mask     = 0x0700,
+       .major_shift    = 8,
+       .minor_mask     = 0x003f,
+       .minor_shift    = 0,
+};
+
+static const struct of_device_id omap_sham_of_match[] = {
+       {
+               .compatible     = "ti,omap2-sham",
+               .data           = &omap_sham_pdata_omap2,
+       },
+       {
+               .compatible     = "ti,omap4-sham",
+               .data           = &omap_sham_pdata_omap4,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, omap_sham_of_match);
+
+static int omap_sham_get_res_of(struct omap_sham_dev *dd,
+               struct device *dev, struct resource *res)
 {
-       int err;
+       struct device_node *node = dev->of_node;
+       const struct of_device_id *match;
+       int err = 0;
 
-       dd->dma_lch = -1;
+       match = of_match_device(of_match_ptr(omap_sham_of_match), dev);
+       if (!match) {
+               dev_err(dev, "no compatible OF match\n");
+               err = -EINVAL;
+               goto err;
+       }
 
-       err = omap_request_dma(dd->dma, dev_name(dd->dev),
-                       omap_sham_dma_callback, dd, &dd->dma_lch);
-       if (err) {
-               dev_err(dd->dev, "Unable to request DMA channel\n");
-               return err;
+       err = of_address_to_resource(node, 0, res);
+       if (err < 0) {
+               dev_err(dev, "can't translate OF node address\n");
+               err = -EINVAL;
+               goto err;
        }
 
-       return 0;
+       dd->irq = of_irq_to_resource(node, 0, NULL);
+       if (!dd->irq) {
+               dev_err(dev, "can't translate OF irq value\n");
+               err = -EINVAL;
+               goto err;
+       }
+
+       dd->dma = -1; /* Dummy value that's unused */
+       dd->pdata = match->data;
+
+err:
+       return err;
+}
+#else
+static int omap_sham_get_res_dev(struct omap_sham_dev *dd,
+               struct device *dev, struct resource *res)
+{
+       return -EINVAL;
 }
+#endif
 
-static void omap_sham_dma_cleanup(struct omap_sham_dev *dd)
+static int omap_sham_get_res_pdev(struct omap_sham_dev *dd,
+               struct platform_device *pdev, struct resource *res)
 {
-       if (dd->dma_lch >= 0) {
-               omap_free_dma(dd->dma_lch);
-               dd->dma_lch = -1;
+       struct device *dev = &pdev->dev;
+       struct resource *r;
+       int err = 0;
+
+       /* Get the base address */
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(dev, "no MEM resource info\n");
+               err = -ENODEV;
+               goto err;
+       }
+       memcpy(res, r, sizeof(*res));
+
+       /* Get the IRQ */
+       dd->irq = platform_get_irq(pdev, 0);
+       if (dd->irq < 0) {
+               dev_err(dev, "no IRQ resource info\n");
+               err = dd->irq;
+               goto err;
        }
+
+       /* Get the DMA */
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!r) {
+               dev_err(dev, "no DMA resource info\n");
+               err = -ENODEV;
+               goto err;
+       }
+       dd->dma = r->start;
+
+       /* Only OMAP2/3 can be non-DT */
+       dd->pdata = &omap_sham_pdata_omap2;
+
+err:
+       return err;
 }
 
 static int omap_sham_probe(struct platform_device *pdev)
 {
        struct omap_sham_dev *dd;
        struct device *dev = &pdev->dev;
-       struct resource *res;
+       struct resource res;
+       dma_cap_mask_t mask;
        int err, i, j;
+       u32 rev;
 
        dd = kzalloc(sizeof(struct omap_sham_dev), GFP_KERNEL);
        if (dd == NULL) {
@@ -1158,89 +1677,75 @@ static int omap_sham_probe(struct platform_device *pdev)
        tasklet_init(&dd->done_task, omap_sham_done_task, (unsigned long)dd);
        crypto_init_queue(&dd->queue, OMAP_SHAM_QUEUE_LENGTH);
 
-       dd->irq = -1;
-
-       /* Get the base address */
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "no MEM resource info\n");
-               err = -ENODEV;
-               goto res_err;
-       }
-       dd->phys_base = res->start;
-
-       /* Get the DMA */
-       res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-       if (!res) {
-               dev_err(dev, "no DMA resource info\n");
-               err = -ENODEV;
+       err = (dev->of_node) ? omap_sham_get_res_of(dd, dev, &res) :
+                              omap_sham_get_res_pdev(dd, pdev, &res);
+       if (err)
                goto res_err;
-       }
-       dd->dma = res->start;
 
-       /* Get the IRQ */
-       dd->irq = platform_get_irq(pdev,  0);
-       if (dd->irq < 0) {
-               dev_err(dev, "no IRQ resource info\n");
-               err = dd->irq;
+       dd->io_base = devm_request_and_ioremap(dev, &res);
+       if (!dd->io_base) {
+               dev_err(dev, "can't ioremap\n");
+               err = -ENOMEM;
                goto res_err;
        }
+       dd->phys_base = res.start;
 
-       err = request_irq(dd->irq, omap_sham_irq,
-                       IRQF_TRIGGER_LOW, dev_name(dev), dd);
+       err = request_irq(dd->irq, dd->pdata->intr_hdlr, IRQF_TRIGGER_LOW,
+                         dev_name(dev), dd);
        if (err) {
                dev_err(dev, "unable to request irq.\n");
                goto res_err;
        }
 
-       err = omap_sham_dma_init(dd);
-       if (err)
-               goto dma_err;
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
 
-       /* Initializing the clock */
-       dd->iclk = clk_get(dev, "ick");
-       if (IS_ERR(dd->iclk)) {
-               dev_err(dev, "clock intialization failed.\n");
-               err = PTR_ERR(dd->iclk);
-               goto clk_err;
+       dd->dma_lch = dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
+                                                      &dd->dma, dev, "rx");
+       if (!dd->dma_lch) {
+               dev_err(dev, "unable to obtain RX DMA engine channel %u\n",
+                       dd->dma);
+               err = -ENXIO;
+               goto dma_err;
        }
 
-       dd->io_base = ioremap(dd->phys_base, SZ_4K);
-       if (!dd->io_base) {
-               dev_err(dev, "can't ioremap\n");
-               err = -ENOMEM;
-               goto io_err;
-       }
+       dd->flags |= dd->pdata->flags;
+
+       pm_runtime_enable(dev);
+       pm_runtime_get_sync(dev);
+       rev = omap_sham_read(dd, SHA_REG_REV(dd));
+       pm_runtime_put_sync(&pdev->dev);
 
-       clk_enable(dd->iclk);
        dev_info(dev, "hw accel on OMAP rev %u.%u\n",
-               (omap_sham_read(dd, SHA_REG_REV) & SHA_REG_REV_MAJOR) >> 4,
-               omap_sham_read(dd, SHA_REG_REV) & SHA_REG_REV_MINOR);
-       clk_disable(dd->iclk);
+               (rev & dd->pdata->major_mask) >> dd->pdata->major_shift,
+               (rev & dd->pdata->minor_mask) >> dd->pdata->minor_shift);
 
        spin_lock(&sham.lock);
        list_add_tail(&dd->list, &sham.dev_list);
        spin_unlock(&sham.lock);
 
-       for (i = 0; i < ARRAY_SIZE(algs); i++) {
-               err = crypto_register_ahash(&algs[i]);
-               if (err)
-                       goto err_algs;
+       for (i = 0; i < dd->pdata->algs_info_size; i++) {
+               for (j = 0; j < dd->pdata->algs_info[i].size; j++) {
+                       err = crypto_register_ahash(
+                                       &dd->pdata->algs_info[i].algs_list[j]);
+                       if (err)
+                               goto err_algs;
+
+                       dd->pdata->algs_info[i].registered++;
+               }
        }
 
        return 0;
 
 err_algs:
-       for (j = 0; j < i; j++)
-               crypto_unregister_ahash(&algs[j]);
-       iounmap(dd->io_base);
-io_err:
-       clk_put(dd->iclk);
-clk_err:
-       omap_sham_dma_cleanup(dd);
+       for (i = dd->pdata->algs_info_size - 1; i >= 0; i--)
+               for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--)
+                       crypto_unregister_ahash(
+                                       &dd->pdata->algs_info[i].algs_list[j]);
+       pm_runtime_disable(dev);
+       dma_release_channel(dd->dma_lch);
 dma_err:
-       if (dd->irq >= 0)
-               free_irq(dd->irq, dd);
+       free_irq(dd->irq, dd);
 res_err:
        kfree(dd);
        dd = NULL;
@@ -1253,7 +1758,7 @@ data_err:
 static int omap_sham_remove(struct platform_device *pdev)
 {
        static struct omap_sham_dev *dd;
-       int i;
+       int i, j;
 
        dd = platform_get_drvdata(pdev);
        if (!dd)
@@ -1261,33 +1766,51 @@ static int omap_sham_remove(struct platform_device *pdev)
        spin_lock(&sham.lock);
        list_del(&dd->list);
        spin_unlock(&sham.lock);
-       for (i = 0; i < ARRAY_SIZE(algs); i++)
-               crypto_unregister_ahash(&algs[i]);
+       for (i = dd->pdata->algs_info_size - 1; i >= 0; i--)
+               for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--)
+                       crypto_unregister_ahash(
+                                       &dd->pdata->algs_info[i].algs_list[j]);
        tasklet_kill(&dd->done_task);
-       iounmap(dd->io_base);
-       clk_put(dd->iclk);
-       omap_sham_dma_cleanup(dd);
-       if (dd->irq >= 0)
-               free_irq(dd->irq, dd);
+       pm_runtime_disable(&pdev->dev);
+       dma_release_channel(dd->dma_lch);
+       free_irq(dd->irq, dd);
        kfree(dd);
        dd = NULL;
 
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int omap_sham_suspend(struct device *dev)
+{
+       pm_runtime_put_sync(dev);
+       return 0;
+}
+
+static int omap_sham_resume(struct device *dev)
+{
+       pm_runtime_get_sync(dev);
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops omap_sham_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(omap_sham_suspend, omap_sham_resume)
+};
+
 static struct platform_driver omap_sham_driver = {
        .probe  = omap_sham_probe,
        .remove = omap_sham_remove,
        .driver = {
                .name   = "omap-sham",
                .owner  = THIS_MODULE,
+               .pm     = &omap_sham_pm_ops,
+               .of_match_table = omap_sham_of_match,
        },
 };
 
 static int __init omap_sham_mod_init(void)
 {
-       pr_info("loading %s driver\n", "omap-sham");
-
        return platform_driver_register(&omap_sham_driver);
 }
 
index d4c12180c65416043dbbf3940374ad146daad881..239020b46d63bfbcd872723aa552f31f71fd1577 100644 (file)
@@ -51,7 +51,7 @@ config ASYNC_TX_ENABLE_CHANNEL_SWITCH
 
 config AMBA_PL08X
        bool "ARM PrimeCell PL080 or PL081 support"
-       depends on ARM_AMBA && EXPERIMENTAL
+       depends on ARM_AMBA
        select DMA_ENGINE
        select DMA_VIRTUAL_CHANNELS
        help
@@ -83,7 +83,6 @@ config INTEL_IOP_ADMA
 
 config DW_DMAC
        tristate "Synopsys DesignWare AHB DMA support"
-       depends on HAVE_CLK
        select DMA_ENGINE
        default y if CPU_AT32AP7000
        help
@@ -213,15 +212,15 @@ config TIMB_DMA
          Enable support for the Timberdale FPGA DMA engine.
 
 config SIRF_DMA
-       tristate "CSR SiRFprimaII DMA support"
-       depends on ARCH_PRIMA2
+       tristate "CSR SiRFprimaII/SiRFmarco DMA support"
+       depends on ARCH_SIRF
        select DMA_ENGINE
        help
          Enable support for the CSR SiRFprimaII DMA engine.
 
 config TI_EDMA
        tristate "TI EDMA support"
-       depends on ARCH_DAVINCI
+       depends on ARCH_DAVINCI || ARCH_OMAP
        select DMA_ENGINE
        select DMA_VIRTUAL_CHANNELS
        default n
index d1cc5791476bc39c700f9ab0913eb5f3e50d1bda..8bad254a498d7788530e97a5e5599ca90a2aefe1 100644 (file)
@@ -83,7 +83,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
-#include <asm/hardware/pl080.h>
+#include <linux/amba/pl080.h>
 
 #include "dmaengine.h"
 #include "virt-dma.h"
@@ -1096,15 +1096,9 @@ static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
                                struct pl08x_dma_chan *plchan)
 {
        LIST_HEAD(head);
-       struct pl08x_txd *txd;
 
        vchan_get_all_descriptors(&plchan->vc, &head);
-
-       while (!list_empty(&head)) {
-               txd = list_first_entry(&head, struct pl08x_txd, vd.node);
-               list_del(&txd->vd.node);
-               pl08x_desc_free(&txd->vd);
-       }
+       vchan_dma_desc_free_list(&plchan->vc, &head);
 }
 
 /*
index 13a02f4425b0db386c0bca43f7e019a099d26c92..6e13f262139a89acabf4c512ce8461d62e810a61 100644 (file)
@@ -778,7 +778,7 @@ err:
  */
 static int
 atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr,
-               size_t period_len, enum dma_transfer_direction direction)
+               size_t period_len)
 {
        if (period_len > (ATC_BTSIZE_MAX << reg_width))
                goto err_out;
@@ -786,8 +786,6 @@ atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr,
                goto err_out;
        if (unlikely(buf_addr & ((1 << reg_width) - 1)))
                goto err_out;
-       if (unlikely(!(direction & (DMA_DEV_TO_MEM | DMA_MEM_TO_DEV))))
-               goto err_out;
 
        return 0;
 
@@ -886,14 +884,16 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
                return NULL;
        }
 
+       if (unlikely(!is_slave_direction(direction)))
+               goto err_out;
+
        if (sconfig->direction == DMA_MEM_TO_DEV)
                reg_width = convert_buswidth(sconfig->dst_addr_width);
        else
                reg_width = convert_buswidth(sconfig->src_addr_width);
 
        /* Check for too big/unaligned periods and unaligned DMA buffer */
-       if (atc_dma_cyclic_check_values(reg_width, buf_addr,
-                                       period_len, direction))
+       if (atc_dma_cyclic_check_values(reg_width, buf_addr, period_len))
                goto err_out;
 
        /* build cyclic linked list */
index 116e4adffb0812a7a8c41e68b5a555678cd4525b..0eb3c1388667dc4de3a6d2e5284ba493b3d388bd 100644 (file)
@@ -369,10 +369,10 @@ static void vdbg_dump_regs(struct at_dma_chan *atchan) {}
 
 static void atc_dump_lli(struct at_dma_chan *atchan, struct at_lli *lli)
 {
-       dev_printk(KERN_CRIT, chan2dev(&atchan->chan_common),
-                       "  desc: s0x%x d0x%x ctrl0x%x:0x%x l0x%x\n",
-                       lli->saddr, lli->daddr,
-                       lli->ctrla, lli->ctrlb, lli->dscr);
+       dev_crit(chan2dev(&atchan->chan_common),
+                "  desc: s0x%x d0x%x ctrl0x%x:0x%x l0x%x\n",
+                lli->saddr, lli->daddr,
+                lli->ctrla, lli->ctrlb, lli->dscr);
 }
 
 
index a815d44c70a41b802c771dea337bf2e5348592a8..242b8c0a3de8bfb3eea401f13067f3e05143397f 100644 (file)
@@ -62,6 +62,7 @@
 #include <linux/rculist.h>
 #include <linux/idr.h>
 #include <linux/slab.h>
+#include <linux/of_dma.h>
 
 static DEFINE_MUTEX(dma_list_mutex);
 static DEFINE_IDR(dma_idr);
@@ -266,7 +267,10 @@ enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie)
                        pr_err("%s: timeout!\n", __func__);
                        return DMA_ERROR;
                }
-       } while (status == DMA_IN_PROGRESS);
+               if (status != DMA_IN_PROGRESS)
+                       break;
+               cpu_relax();
+       } while (1);
 
        return status;
 }
@@ -546,6 +550,21 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v
 }
 EXPORT_SYMBOL_GPL(__dma_request_channel);
 
+/**
+ * dma_request_slave_channel - try to allocate an exclusive slave channel
+ * @dev:       pointer to client device structure
+ * @name:      slave channel name
+ */
+struct dma_chan *dma_request_slave_channel(struct device *dev, char *name)
+{
+       /* If device-tree is present get slave info from here */
+       if (dev->of_node)
+               return of_dma_request_slave_channel(dev->of_node, name);
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(dma_request_slave_channel);
+
 void dma_release_channel(struct dma_chan *chan)
 {
        mutex_lock(&dma_list_mutex);
index 64b048d7fba751de8f5faf7b73062ea104aeea15..a2c8904b63ea44fd9154631170c391bd601054d2 100644 (file)
@@ -242,6 +242,13 @@ static inline void unmap_dst(struct device *dev, dma_addr_t *addr, size_t len,
                dma_unmap_single(dev, addr[count], len, DMA_BIDIRECTIONAL);
 }
 
+static unsigned int min_odd(unsigned int x, unsigned int y)
+{
+       unsigned int val = min(x, y);
+
+       return val % 2 ? val : val - 1;
+}
+
 /*
  * This function repeatedly tests DMA transfers of various lengths and
  * offsets for a given operation type until it is told to exit by
@@ -262,6 +269,7 @@ static int dmatest_func(void *data)
        struct dmatest_thread   *thread = data;
        struct dmatest_done     done = { .wait = &done_wait };
        struct dma_chan         *chan;
+       struct dma_device       *dev;
        const char              *thread_name;
        unsigned int            src_off, dst_off, len;
        unsigned int            error_count;
@@ -283,13 +291,16 @@ static int dmatest_func(void *data)
 
        smp_rmb();
        chan = thread->chan;
+       dev = chan->device;
        if (thread->type == DMA_MEMCPY)
                src_cnt = dst_cnt = 1;
        else if (thread->type == DMA_XOR) {
-               src_cnt = xor_sources | 1; /* force odd to ensure dst = src */
+               /* force odd to ensure dst = src */
+               src_cnt = min_odd(xor_sources | 1, dev->max_xor);
                dst_cnt = 1;
        } else if (thread->type == DMA_PQ) {
-               src_cnt = pq_sources | 1; /* force odd to ensure dst = src */
+               /* force odd to ensure dst = src */
+               src_cnt = min_odd(pq_sources | 1, dma_maxpq(dev, 0));
                dst_cnt = 2;
                for (i = 0; i < src_cnt; i++)
                        pq_coefs[i] = 1;
@@ -327,7 +338,6 @@ static int dmatest_func(void *data)
 
        while (!kthread_should_stop()
               && !(iterations && total_tests >= iterations)) {
-               struct dma_device *dev = chan->device;
                struct dma_async_tx_descriptor *tx = NULL;
                dma_addr_t dma_srcs[src_cnt];
                dma_addr_t dma_dsts[dst_cnt];
@@ -526,7 +536,9 @@ err_srcs:
                        thread_name, total_tests, failed_tests, ret);
 
        /* terminate all transfers on specified channels */
-       chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+       if (ret)
+               dmaengine_terminate_all(chan);
+
        if (iterations > 0)
                while (!kthread_should_stop()) {
                        DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit);
@@ -551,7 +563,7 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc)
        }
 
        /* terminate all transfers on specified channels */
-       dtc->chan->device->device_control(dtc->chan, DMA_TERMINATE_ALL, 0);
+       dmaengine_terminate_all(dtc->chan);
 
        kfree(dtc);
 }
index 3e8ba02ba29227a89d4950b18d64811c7b9b5667..5b5843a1d2da3e0bc2cc367d504bcae8ed697a97 100644 (file)
@@ -1,6 +1,5 @@
 /*
- * Driver for the Synopsys DesignWare DMA Controller (aka DMACA on
- * AVR32 systems.)
+ * Core driver for the Synopsys DesignWare DMA Controller
  *
  * Copyright (C) 2007-2008 Atmel Corporation
  * Copyright (C) 2010-2011 ST Microelectronics
@@ -9,11 +8,13 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+
 #include <linux/bitops.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -46,15 +47,32 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave)
        return slave ? slave->src_master : 1;
 }
 
+#define SRC_MASTER     0
+#define DST_MASTER     1
+
+static inline unsigned int dwc_get_master(struct dma_chan *chan, int master)
+{
+       struct dw_dma *dw = to_dw_dma(chan->device);
+       struct dw_dma_slave *dws = chan->private;
+       unsigned int m;
+
+       if (master == SRC_MASTER)
+               m = dwc_get_sms(dws);
+       else
+               m = dwc_get_dms(dws);
+
+       return min_t(unsigned int, dw->nr_masters - 1, m);
+}
+
 #define DWC_DEFAULT_CTLLO(_chan) ({                            \
-               struct dw_dma_slave *__slave = (_chan->private);        \
                struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan);       \
                struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \
-               int _dms = dwc_get_dms(__slave);                \
-               int _sms = dwc_get_sms(__slave);                \
-               u8 _smsize = __slave ? _sconfig->src_maxburst : \
+               bool _is_slave = is_slave_direction(_dwc->direction);   \
+               int _dms = dwc_get_master(_chan, DST_MASTER);           \
+               int _sms = dwc_get_master(_chan, SRC_MASTER);           \
+               u8 _smsize = _is_slave ? _sconfig->src_maxburst :       \
                        DW_DMA_MSIZE_16;                        \
-               u8 _dmsize = __slave ? _sconfig->dst_maxburst : \
+               u8 _dmsize = _is_slave ? _sconfig->dst_maxburst :       \
                        DW_DMA_MSIZE_16;                        \
                                                                \
                (DWC_CTLL_DST_MSIZE(_dmsize)                    \
@@ -72,15 +90,14 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave)
  */
 #define NR_DESCS_PER_CHANNEL   64
 
-/*----------------------------------------------------------------------*/
+static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master)
+{
+       struct dw_dma *dw = to_dw_dma(chan->device);
 
-/*
- * Because we're not relying on writeback from the controller (it may not
- * even be configured into the core!) we don't need to use dma_pool.  These
- * descriptors -- and associated data -- are cacheable.  We do need to make
- * sure their dcache entries are written back before handing them off to
- * the controller, though.
- */
+       return dw->data_width[dwc_get_master(chan, master)];
+}
+
+/*----------------------------------------------------------------------*/
 
 static struct device *chan2dev(struct dma_chan *chan)
 {
@@ -93,7 +110,7 @@ static struct device *chan2parent(struct dma_chan *chan)
 
 static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc)
 {
-       return list_entry(dwc->active_list.next, struct dw_desc, desc_node);
+       return to_dw_desc(dwc->active_list.next);
 }
 
 static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc)
@@ -120,19 +137,6 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc)
        return ret;
 }
 
-static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc)
-{
-       struct dw_desc  *child;
-
-       list_for_each_entry(child, &desc->tx_list, desc_node)
-               dma_sync_single_for_cpu(chan2parent(&dwc->chan),
-                               child->txd.phys, sizeof(child->lli),
-                               DMA_TO_DEVICE);
-       dma_sync_single_for_cpu(chan2parent(&dwc->chan),
-                       desc->txd.phys, sizeof(desc->lli),
-                       DMA_TO_DEVICE);
-}
-
 /*
  * Move a descriptor, including any children, to the free list.
  * `desc' must not be on any lists.
@@ -144,8 +148,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
        if (desc) {
                struct dw_desc *child;
 
-               dwc_sync_desc_for_cpu(dwc, desc);
-
                spin_lock_irqsave(&dwc->lock, flags);
                list_for_each_entry(child, &desc->tx_list, desc_node)
                        dev_vdbg(chan2dev(&dwc->chan),
@@ -178,9 +180,9 @@ static void dwc_initialize(struct dw_dma_chan *dwc)
                cfghi = dws->cfg_hi;
                cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK;
        } else {
-               if (dwc->dma_sconfig.direction == DMA_MEM_TO_DEV)
+               if (dwc->direction == DMA_MEM_TO_DEV)
                        cfghi = DWC_CFGH_DST_PER(dwc->dma_sconfig.slave_id);
-               else if (dwc->dma_sconfig.direction == DMA_DEV_TO_MEM)
+               else if (dwc->direction == DMA_DEV_TO_MEM)
                        cfghi = DWC_CFGH_SRC_PER(dwc->dma_sconfig.slave_id);
        }
 
@@ -222,7 +224,6 @@ static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc)
                channel_readl(dwc, CTL_LO));
 }
 
-
 static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc)
 {
        channel_clear_bit(dw, CH_EN, dwc->mask);
@@ -248,6 +249,9 @@ static inline void dwc_do_single_block(struct dw_dma_chan *dwc,
        channel_writel(dwc, CTL_LO, ctllo);
        channel_writel(dwc, CTL_HI, desc->lli.ctlhi);
        channel_set_bit(dw, CH_EN, dwc->mask);
+
+       /* Move pointer to next descriptor */
+       dwc->tx_node_active = dwc->tx_node_active->next;
 }
 
 /* Called with dwc->lock held and bh disabled */
@@ -278,9 +282,10 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first)
 
                dwc_initialize(dwc);
 
-               dwc->tx_list = &first->tx_list;
-               dwc->tx_node_active = first->tx_list.next;
+               dwc->residue = first->total_len;
+               dwc->tx_node_active = &first->tx_list;
 
+               /* Submit first block */
                dwc_do_single_block(dwc, first);
 
                return;
@@ -316,8 +321,6 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc,
                param = txd->callback_param;
        }
 
-       dwc_sync_desc_for_cpu(dwc, desc);
-
        /* async_tx_ack */
        list_for_each_entry(child, &desc->tx_list, desc_node)
                async_tx_ack(&child->txd);
@@ -326,29 +329,29 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc,
        list_splice_init(&desc->tx_list, &dwc->free_list);
        list_move(&desc->desc_node, &dwc->free_list);
 
-       if (!dwc->chan.private) {
+       if (!is_slave_direction(dwc->direction)) {
                struct device *parent = chan2parent(&dwc->chan);
                if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
                        if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE)
                                dma_unmap_single(parent, desc->lli.dar,
-                                               desc->len, DMA_FROM_DEVICE);
+                                       desc->total_len, DMA_FROM_DEVICE);
                        else
                                dma_unmap_page(parent, desc->lli.dar,
-                                               desc->len, DMA_FROM_DEVICE);
+                                       desc->total_len, DMA_FROM_DEVICE);
                }
                if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
                        if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE)
                                dma_unmap_single(parent, desc->lli.sar,
-                                               desc->len, DMA_TO_DEVICE);
+                                       desc->total_len, DMA_TO_DEVICE);
                        else
                                dma_unmap_page(parent, desc->lli.sar,
-                                               desc->len, DMA_TO_DEVICE);
+                                       desc->total_len, DMA_TO_DEVICE);
                }
        }
 
        spin_unlock_irqrestore(&dwc->lock, flags);
 
-       if (callback_required && callback)
+       if (callback)
                callback(param);
 }
 
@@ -383,6 +386,15 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc)
                dwc_descriptor_complete(dwc, desc, true);
 }
 
+/* Returns how many bytes were already received from source */
+static inline u32 dwc_get_sent(struct dw_dma_chan *dwc)
+{
+       u32 ctlhi = channel_readl(dwc, CTL_HI);
+       u32 ctllo = channel_readl(dwc, CTL_LO);
+
+       return (ctlhi & DWC_CTLH_BLOCK_TS_MASK) * (1 << (ctllo >> 4 & 7));
+}
+
 static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
 {
        dma_addr_t llp;
@@ -398,6 +410,39 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
        if (status_xfer & dwc->mask) {
                /* Everything we've submitted is done */
                dma_writel(dw, CLEAR.XFER, dwc->mask);
+
+               if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) {
+                       struct list_head *head, *active = dwc->tx_node_active;
+
+                       /*
+                        * We are inside first active descriptor.
+                        * Otherwise something is really wrong.
+                        */
+                       desc = dwc_first_active(dwc);
+
+                       head = &desc->tx_list;
+                       if (active != head) {
+                               /* Update desc to reflect last sent one */
+                               if (active != head->next)
+                                       desc = to_dw_desc(active->prev);
+
+                               dwc->residue -= desc->len;
+
+                               child = to_dw_desc(active);
+
+                               /* Submit next block */
+                               dwc_do_single_block(dwc, child);
+
+                               spin_unlock_irqrestore(&dwc->lock, flags);
+                               return;
+                       }
+
+                       /* We are done here */
+                       clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags);
+               }
+
+               dwc->residue = 0;
+
                spin_unlock_irqrestore(&dwc->lock, flags);
 
                dwc_complete_all(dw, dwc);
@@ -405,6 +450,13 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
        }
 
        if (list_empty(&dwc->active_list)) {
+               dwc->residue = 0;
+               spin_unlock_irqrestore(&dwc->lock, flags);
+               return;
+       }
+
+       if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) {
+               dev_vdbg(chan2dev(&dwc->chan), "%s: soft LLP mode\n", __func__);
                spin_unlock_irqrestore(&dwc->lock, flags);
                return;
        }
@@ -413,6 +465,9 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
                        (unsigned long long)llp);
 
        list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) {
+               /* initial residue value */
+               dwc->residue = desc->total_len;
+
                /* check first descriptors addr */
                if (desc->txd.phys == llp) {
                        spin_unlock_irqrestore(&dwc->lock, flags);
@@ -422,16 +477,21 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
                /* check first descriptors llp */
                if (desc->lli.llp == llp) {
                        /* This one is currently in progress */
+                       dwc->residue -= dwc_get_sent(dwc);
                        spin_unlock_irqrestore(&dwc->lock, flags);
                        return;
                }
 
-               list_for_each_entry(child, &desc->tx_list, desc_node)
+               dwc->residue -= desc->len;
+               list_for_each_entry(child, &desc->tx_list, desc_node) {
                        if (child->lli.llp == llp) {
                                /* Currently in progress */
+                               dwc->residue -= dwc_get_sent(dwc);
                                spin_unlock_irqrestore(&dwc->lock, flags);
                                return;
                        }
+                       dwc->residue -= child->len;
+               }
 
                /*
                 * No descriptors so far seem to be in progress, i.e.
@@ -457,9 +517,8 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
 
 static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli)
 {
-       dev_printk(KERN_CRIT, chan2dev(&dwc->chan),
-                       "  desc: s0x%x d0x%x l0x%x c0x%x:%x\n",
-                       lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo);
+       dev_crit(chan2dev(&dwc->chan), "  desc: s0x%x d0x%x l0x%x c0x%x:%x\n",
+                lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo);
 }
 
 static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
@@ -487,16 +546,14 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
                dwc_dostart(dwc, dwc_first_active(dwc));
 
        /*
-        * KERN_CRITICAL may seem harsh, but since this only happens
+        * WARN may seem harsh, but since this only happens
         * when someone submits a bad physical address in a
         * descriptor, we should consider ourselves lucky that the
         * controller flagged an error instead of scribbling over
         * random memory locations.
         */
-       dev_printk(KERN_CRIT, chan2dev(&dwc->chan),
-                       "Bad descriptor submitted for DMA!\n");
-       dev_printk(KERN_CRIT, chan2dev(&dwc->chan),
-                       "  cookie: %d\n", bad_desc->txd.cookie);
+       dev_WARN(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n"
+                                      "  cookie: %d\n", bad_desc->txd.cookie);
        dwc_dump_lli(dwc, &bad_desc->lli);
        list_for_each_entry(child, &bad_desc->tx_list, desc_node)
                dwc_dump_lli(dwc, &child->lli);
@@ -597,36 +654,8 @@ static void dw_dma_tasklet(unsigned long data)
                        dwc_handle_cyclic(dw, dwc, status_err, status_xfer);
                else if (status_err & (1 << i))
                        dwc_handle_error(dw, dwc);
-               else if (status_xfer & (1 << i)) {
-                       unsigned long flags;
-
-                       spin_lock_irqsave(&dwc->lock, flags);
-                       if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) {
-                               if (dwc->tx_node_active != dwc->tx_list) {
-                                       struct dw_desc *desc =
-                                               list_entry(dwc->tx_node_active,
-                                                          struct dw_desc,
-                                                          desc_node);
-
-                                       dma_writel(dw, CLEAR.XFER, dwc->mask);
-
-                                       /* move pointer to next descriptor */
-                                       dwc->tx_node_active =
-                                               dwc->tx_node_active->next;
-
-                                       dwc_do_single_block(dwc, desc);
-
-                                       spin_unlock_irqrestore(&dwc->lock, flags);
-                                       continue;
-                               } else {
-                                       /* we are done here */
-                                       clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags);
-                               }
-                       }
-                       spin_unlock_irqrestore(&dwc->lock, flags);
-
+               else if (status_xfer & (1 << i))
                        dwc_scan_descriptors(dw, dwc);
-               }
        }
 
        /*
@@ -708,7 +737,6 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
                size_t len, unsigned long flags)
 {
        struct dw_dma_chan      *dwc = to_dw_dma_chan(chan);
-       struct dw_dma_slave     *dws = chan->private;
        struct dw_desc          *desc;
        struct dw_desc          *first;
        struct dw_desc          *prev;
@@ -729,8 +757,10 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
                return NULL;
        }
 
-       data_width = min_t(unsigned int, dwc->dw->data_width[dwc_get_sms(dws)],
-                                        dwc->dw->data_width[dwc_get_dms(dws)]);
+       dwc->direction = DMA_MEM_TO_MEM;
+
+       data_width = min_t(unsigned int, dwc_get_data_width(chan, SRC_MASTER),
+                          dwc_get_data_width(chan, DST_MASTER));
 
        src_width = dst_width = min_t(unsigned int, data_width,
                                      dwc_fast_fls(src | dest | len));
@@ -755,32 +785,25 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
                desc->lli.dar = dest + offset;
                desc->lli.ctllo = ctllo;
                desc->lli.ctlhi = xfer_count;
+               desc->len = xfer_count << src_width;
 
                if (!first) {
                        first = desc;
                } else {
                        prev->lli.llp = desc->txd.phys;
-                       dma_sync_single_for_device(chan2parent(chan),
-                                       prev->txd.phys, sizeof(prev->lli),
-                                       DMA_TO_DEVICE);
                        list_add_tail(&desc->desc_node,
                                        &first->tx_list);
                }
                prev = desc;
        }
 
-
        if (flags & DMA_PREP_INTERRUPT)
                /* Trigger interrupt after last block */
                prev->lli.ctllo |= DWC_CTLL_INT_EN;
 
        prev->lli.llp = 0;
-       dma_sync_single_for_device(chan2parent(chan),
-                       prev->txd.phys, sizeof(prev->lli),
-                       DMA_TO_DEVICE);
-
        first->txd.flags = flags;
-       first->len = len;
+       first->total_len = len;
 
        return &first->txd;
 
@@ -795,7 +818,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                unsigned long flags, void *context)
 {
        struct dw_dma_chan      *dwc = to_dw_dma_chan(chan);
-       struct dw_dma_slave     *dws = chan->private;
        struct dma_slave_config *sconfig = &dwc->dma_sconfig;
        struct dw_desc          *prev;
        struct dw_desc          *first;
@@ -810,9 +832,11 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
 
        dev_vdbg(chan2dev(chan), "%s\n", __func__);
 
-       if (unlikely(!dws || !sg_len))
+       if (unlikely(!is_slave_direction(direction) || !sg_len))
                return NULL;
 
+       dwc->direction = direction;
+
        prev = first = NULL;
 
        switch (direction) {
@@ -827,7 +851,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) :
                        DWC_CTLL_FC(DW_DMA_FC_D_M2P);
 
-               data_width = dwc->dw->data_width[dwc_get_sms(dws)];
+               data_width = dwc_get_data_width(chan, SRC_MASTER);
 
                for_each_sg(sgl, sg, sg_len, i) {
                        struct dw_desc  *desc;
@@ -860,15 +884,12 @@ slave_sg_todev_fill_desc:
                        }
 
                        desc->lli.ctlhi = dlen >> mem_width;
+                       desc->len = dlen;
 
                        if (!first) {
                                first = desc;
                        } else {
                                prev->lli.llp = desc->txd.phys;
-                               dma_sync_single_for_device(chan2parent(chan),
-                                               prev->txd.phys,
-                                               sizeof(prev->lli),
-                                               DMA_TO_DEVICE);
                                list_add_tail(&desc->desc_node,
                                                &first->tx_list);
                        }
@@ -890,7 +911,7 @@ slave_sg_todev_fill_desc:
                ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) :
                        DWC_CTLL_FC(DW_DMA_FC_D_P2M);
 
-               data_width = dwc->dw->data_width[dwc_get_dms(dws)];
+               data_width = dwc_get_data_width(chan, DST_MASTER);
 
                for_each_sg(sgl, sg, sg_len, i) {
                        struct dw_desc  *desc;
@@ -922,15 +943,12 @@ slave_sg_fromdev_fill_desc:
                                len = 0;
                        }
                        desc->lli.ctlhi = dlen >> reg_width;
+                       desc->len = dlen;
 
                        if (!first) {
                                first = desc;
                        } else {
                                prev->lli.llp = desc->txd.phys;
-                               dma_sync_single_for_device(chan2parent(chan),
-                                               prev->txd.phys,
-                                               sizeof(prev->lli),
-                                               DMA_TO_DEVICE);
                                list_add_tail(&desc->desc_node,
                                                &first->tx_list);
                        }
@@ -950,11 +968,7 @@ slave_sg_fromdev_fill_desc:
                prev->lli.ctllo |= DWC_CTLL_INT_EN;
 
        prev->lli.llp = 0;
-       dma_sync_single_for_device(chan2parent(chan),
-                       prev->txd.phys, sizeof(prev->lli),
-                       DMA_TO_DEVICE);
-
-       first->len = total_len;
+       first->total_len = total_len;
 
        return &first->txd;
 
@@ -984,11 +998,12 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
 {
        struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
 
-       /* Check if it is chan is configured for slave transfers */
-       if (!chan->private)
+       /* Check if chan will be configured for slave transfers */
+       if (!is_slave_direction(sconfig->direction))
                return -EINVAL;
 
        memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig));
+       dwc->direction = sconfig->direction;
 
        convert_burst(&dwc->dma_sconfig.src_maxburst);
        convert_burst(&dwc->dma_sconfig.dst_maxburst);
@@ -996,6 +1011,26 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
        return 0;
 }
 
+static inline void dwc_chan_pause(struct dw_dma_chan *dwc)
+{
+       u32 cfglo = channel_readl(dwc, CFG_LO);
+
+       channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP);
+       while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY))
+               cpu_relax();
+
+       dwc->paused = true;
+}
+
+static inline void dwc_chan_resume(struct dw_dma_chan *dwc)
+{
+       u32 cfglo = channel_readl(dwc, CFG_LO);
+
+       channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP);
+
+       dwc->paused = false;
+}
+
 static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
                       unsigned long arg)
 {
@@ -1003,18 +1038,13 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        struct dw_dma           *dw = to_dw_dma(chan->device);
        struct dw_desc          *desc, *_desc;
        unsigned long           flags;
-       u32                     cfglo;
        LIST_HEAD(list);
 
        if (cmd == DMA_PAUSE) {
                spin_lock_irqsave(&dwc->lock, flags);
 
-               cfglo = channel_readl(dwc, CFG_LO);
-               channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP);
-               while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY))
-                       cpu_relax();
+               dwc_chan_pause(dwc);
 
-               dwc->paused = true;
                spin_unlock_irqrestore(&dwc->lock, flags);
        } else if (cmd == DMA_RESUME) {
                if (!dwc->paused)
@@ -1022,9 +1052,7 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 
                spin_lock_irqsave(&dwc->lock, flags);
 
-               cfglo = channel_readl(dwc, CFG_LO);
-               channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP);
-               dwc->paused = false;
+               dwc_chan_resume(dwc);
 
                spin_unlock_irqrestore(&dwc->lock, flags);
        } else if (cmd == DMA_TERMINATE_ALL) {
@@ -1034,7 +1062,7 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 
                dwc_chan_disable(dw, dwc);
 
-               dwc->paused = false;
+               dwc_chan_resume(dwc);
 
                /* active_list entries will end up before queued entries */
                list_splice_init(&dwc->queue, &list);
@@ -1054,6 +1082,21 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        return 0;
 }
 
+static inline u32 dwc_get_residue(struct dw_dma_chan *dwc)
+{
+       unsigned long flags;
+       u32 residue;
+
+       spin_lock_irqsave(&dwc->lock, flags);
+
+       residue = dwc->residue;
+       if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue)
+               residue -= dwc_get_sent(dwc);
+
+       spin_unlock_irqrestore(&dwc->lock, flags);
+       return residue;
+}
+
 static enum dma_status
 dwc_tx_status(struct dma_chan *chan,
              dma_cookie_t cookie,
@@ -1070,7 +1113,7 @@ dwc_tx_status(struct dma_chan *chan,
        }
 
        if (ret != DMA_SUCCESS)
-               dma_set_residue(txstate, dwc_first_active(dwc)->len);
+               dma_set_residue(txstate, dwc_get_residue(dwc));
 
        if (dwc->paused)
                return DMA_PAUSED;
@@ -1113,22 +1156,22 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
        spin_lock_irqsave(&dwc->lock, flags);
        i = dwc->descs_allocated;
        while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) {
+               dma_addr_t phys;
+
                spin_unlock_irqrestore(&dwc->lock, flags);
 
-               desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL);
-               if (!desc) {
-                       dev_info(chan2dev(chan),
-                               "only allocated %d descriptors\n", i);
-                       spin_lock_irqsave(&dwc->lock, flags);
-                       break;
-               }
+               desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys);
+               if (!desc)
+                       goto err_desc_alloc;
+
+               memset(desc, 0, sizeof(struct dw_desc));
 
                INIT_LIST_HEAD(&desc->tx_list);
                dma_async_tx_descriptor_init(&desc->txd, chan);
                desc->txd.tx_submit = dwc_tx_submit;
                desc->txd.flags = DMA_CTRL_ACK;
-               desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli,
-                               sizeof(desc->lli), DMA_TO_DEVICE);
+               desc->txd.phys = phys;
+
                dwc_desc_put(dwc, desc);
 
                spin_lock_irqsave(&dwc->lock, flags);
@@ -1139,6 +1182,11 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
 
        dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i);
 
+       return i;
+
+err_desc_alloc:
+       dev_info(chan2dev(chan), "only allocated %d descriptors\n", i);
+
        return i;
 }
 
@@ -1171,14 +1219,56 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
 
        list_for_each_entry_safe(desc, _desc, &list, desc_node) {
                dev_vdbg(chan2dev(chan), "  freeing descriptor %p\n", desc);
-               dma_unmap_single(chan2parent(chan), desc->txd.phys,
-                               sizeof(desc->lli), DMA_TO_DEVICE);
-               kfree(desc);
+               dma_pool_free(dw->desc_pool, desc, desc->txd.phys);
        }
 
        dev_vdbg(chan2dev(chan), "%s: done\n", __func__);
 }
 
+bool dw_dma_generic_filter(struct dma_chan *chan, void *param)
+{
+       struct dw_dma *dw = to_dw_dma(chan->device);
+       static struct dw_dma *last_dw;
+       static char *last_bus_id;
+       int i = -1;
+
+       /*
+        * dmaengine framework calls this routine for all channels of all dma
+        * controller, until true is returned. If 'param' bus_id is not
+        * registered with a dma controller (dw), then there is no need of
+        * running below function for all channels of dw.
+        *
+        * This block of code does this by saving the parameters of last
+        * failure. If dw and param are same, i.e. trying on same dw with
+        * different channel, return false.
+        */
+       if ((last_dw == dw) && (last_bus_id == param))
+               return false;
+       /*
+        * Return true:
+        * - If dw_dma's platform data is not filled with slave info, then all
+        *   dma controllers are fine for transfer.
+        * - Or if param is NULL
+        */
+       if (!dw->sd || !param)
+               return true;
+
+       while (++i < dw->sd_count) {
+               if (!strcmp(dw->sd[i].bus_id, param)) {
+                       chan->private = &dw->sd[i];
+                       last_dw = NULL;
+                       last_bus_id = NULL;
+
+                       return true;
+               }
+       }
+
+       last_dw = dw;
+       last_bus_id = param;
+       return false;
+}
+EXPORT_SYMBOL(dw_dma_generic_filter);
+
 /* --------------------- Cyclic DMA API extensions -------------------- */
 
 /**
@@ -1298,6 +1388,11 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
 
        retval = ERR_PTR(-EINVAL);
 
+       if (unlikely(!is_slave_direction(direction)))
+               goto out_err;
+
+       dwc->direction = direction;
+
        if (direction == DMA_MEM_TO_DEV)
                reg_width = __ffs(sconfig->dst_addr_width);
        else
@@ -1312,8 +1407,6 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
                goto out_err;
        if (unlikely(buf_addr & ((1 << reg_width) - 1)))
                goto out_err;
-       if (unlikely(!(direction & (DMA_MEM_TO_DEV | DMA_DEV_TO_MEM))))
-               goto out_err;
 
        retval = ERR_PTR(-ENOMEM);
 
@@ -1371,20 +1464,14 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
                desc->lli.ctlhi = (period_len >> reg_width);
                cdesc->desc[i] = desc;
 
-               if (last) {
+               if (last)
                        last->lli.llp = desc->txd.phys;
-                       dma_sync_single_for_device(chan2parent(chan),
-                                       last->txd.phys, sizeof(last->lli),
-                                       DMA_TO_DEVICE);
-               }
 
                last = desc;
        }
 
        /* lets make a cyclic list */
        last->lli.llp = cdesc->desc[0]->txd.phys;
-       dma_sync_single_for_device(chan2parent(chan), last->txd.phys,
-                       sizeof(last->lli), DMA_TO_DEVICE);
 
        dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu "
                        "period %zu periods %d\n", (unsigned long long)buf_addr,
@@ -1462,6 +1549,91 @@ static void dw_dma_off(struct dw_dma *dw)
                dw->chan[i].initialized = false;
 }
 
+#ifdef CONFIG_OF
+static struct dw_dma_platform_data *
+dw_dma_parse_dt(struct platform_device *pdev)
+{
+       struct device_node *sn, *cn, *np = pdev->dev.of_node;
+       struct dw_dma_platform_data *pdata;
+       struct dw_dma_slave *sd;
+       u32 tmp, arr[4];
+
+       if (!np) {
+               dev_err(&pdev->dev, "Missing DT data\n");
+               return NULL;
+       }
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       if (of_property_read_u32(np, "nr_channels", &pdata->nr_channels))
+               return NULL;
+
+       if (of_property_read_bool(np, "is_private"))
+               pdata->is_private = true;
+
+       if (!of_property_read_u32(np, "chan_allocation_order", &tmp))
+               pdata->chan_allocation_order = (unsigned char)tmp;
+
+       if (!of_property_read_u32(np, "chan_priority", &tmp))
+               pdata->chan_priority = tmp;
+
+       if (!of_property_read_u32(np, "block_size", &tmp))
+               pdata->block_size = tmp;
+
+       if (!of_property_read_u32(np, "nr_masters", &tmp)) {
+               if (tmp > 4)
+                       return NULL;
+
+               pdata->nr_masters = tmp;
+       }
+
+       if (!of_property_read_u32_array(np, "data_width", arr,
+                               pdata->nr_masters))
+               for (tmp = 0; tmp < pdata->nr_masters; tmp++)
+                       pdata->data_width[tmp] = arr[tmp];
+
+       /* parse slave data */
+       sn = of_find_node_by_name(np, "slave_info");
+       if (!sn)
+               return pdata;
+
+       /* calculate number of slaves */
+       tmp = of_get_child_count(sn);
+       if (!tmp)
+               return NULL;
+
+       sd = devm_kzalloc(&pdev->dev, sizeof(*sd) * tmp, GFP_KERNEL);
+       if (!sd)
+               return NULL;
+
+       pdata->sd = sd;
+       pdata->sd_count = tmp;
+
+       for_each_child_of_node(sn, cn) {
+               sd->dma_dev = &pdev->dev;
+               of_property_read_string(cn, "bus_id", &sd->bus_id);
+               of_property_read_u32(cn, "cfg_hi", &sd->cfg_hi);
+               of_property_read_u32(cn, "cfg_lo", &sd->cfg_lo);
+               if (!of_property_read_u32(cn, "src_master", &tmp))
+                       sd->src_master = tmp;
+
+               if (!of_property_read_u32(cn, "dst_master", &tmp))
+                       sd->dst_master = tmp;
+               sd++;
+       }
+
+       return pdata;
+}
+#else
+static inline struct dw_dma_platform_data *
+dw_dma_parse_dt(struct platform_device *pdev)
+{
+       return NULL;
+}
+#endif
+
 static int dw_probe(struct platform_device *pdev)
 {
        struct dw_dma_platform_data *pdata;
@@ -1477,10 +1649,6 @@ static int dw_probe(struct platform_device *pdev)
        int                     err;
        int                     i;
 
-       pdata = dev_get_platdata(&pdev->dev);
-       if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS)
-               return -EINVAL;
-
        io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!io)
                return -EINVAL;
@@ -1496,6 +1664,24 @@ static int dw_probe(struct platform_device *pdev)
        dw_params = dma_read_byaddr(regs, DW_PARAMS);
        autocfg = dw_params >> DW_PARAMS_EN & 0x1;
 
+       dev_dbg(&pdev->dev, "DW_PARAMS: 0x%08x\n", dw_params);
+
+       pdata = dev_get_platdata(&pdev->dev);
+       if (!pdata)
+               pdata = dw_dma_parse_dt(pdev);
+
+       if (!pdata && autocfg) {
+               pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+               if (!pdata)
+                       return -ENOMEM;
+
+               /* Fill platform data with the default values */
+               pdata->is_private = true;
+               pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING;
+               pdata->chan_priority = CHAN_PRIORITY_ASCENDING;
+       } else if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS)
+               return -EINVAL;
+
        if (autocfg)
                nr_channels = (dw_params >> DW_PARAMS_NR_CHAN & 0x7) + 1;
        else
@@ -1512,6 +1698,8 @@ static int dw_probe(struct platform_device *pdev)
        clk_prepare_enable(dw->clk);
 
        dw->regs = regs;
+       dw->sd = pdata->sd;
+       dw->sd_count = pdata->sd_count;
 
        /* get hardware configuration parameters */
        if (autocfg) {
@@ -1543,6 +1731,14 @@ static int dw_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, dw);
 
+       /* create a pool of consistent memory blocks for hardware descriptors */
+       dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev,
+                                        sizeof(struct dw_desc), 4, 0);
+       if (!dw->desc_pool) {
+               dev_err(&pdev->dev, "No memory for descriptors dma pool\n");
+               return -ENOMEM;
+       }
+
        tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw);
 
        INIT_LIST_HEAD(&dw->dma.channels);
@@ -1574,7 +1770,7 @@ static int dw_probe(struct platform_device *pdev)
 
                channel_clear_bit(dw, CH_EN, dwc->mask);
 
-               dwc->dw = dw;
+               dwc->direction = DMA_TRANS_NONE;
 
                /* hardware configuration */
                if (autocfg) {
@@ -1583,6 +1779,9 @@ static int dw_probe(struct platform_device *pdev)
                        dwc_params = dma_read_byaddr(regs + r * sizeof(u32),
                                                     DWC_PARAMS);
 
+                       dev_dbg(&pdev->dev, "DWC_PARAMS[%d]: 0x%08x\n", i,
+                                           dwc_params);
+
                        /* Decode maximum block size for given channel. The
                         * stored 4 bit value represents blocks from 0x00 for 3
                         * up to 0x0a for 4095. */
@@ -1626,8 +1825,8 @@ static int dw_probe(struct platform_device *pdev)
 
        dma_writel(dw, CFG, DW_CFG_DMA_EN);
 
-       printk(KERN_INFO "%s: DesignWare DMA Controller, %d channels\n",
-                       dev_name(&pdev->dev), nr_channels);
+       dev_info(&pdev->dev, "DesignWare DMA Controller, %d channels\n",
+                nr_channels);
 
        dma_async_device_register(&dw->dma);
 
@@ -1657,7 +1856,7 @@ static void dw_shutdown(struct platform_device *pdev)
 {
        struct dw_dma   *dw = platform_get_drvdata(pdev);
 
-       dw_dma_off(platform_get_drvdata(pdev));
+       dw_dma_off(dw);
        clk_disable_unprepare(dw->clk);
 }
 
@@ -1666,7 +1865,7 @@ static int dw_suspend_noirq(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct dw_dma   *dw = platform_get_drvdata(pdev);
 
-       dw_dma_off(platform_get_drvdata(pdev));
+       dw_dma_off(dw);
        clk_disable_unprepare(dw->clk);
 
        return 0;
@@ -1679,6 +1878,7 @@ static int dw_resume_noirq(struct device *dev)
 
        clk_prepare_enable(dw->clk);
        dma_writel(dw, CFG, DW_CFG_DMA_EN);
+
        return 0;
 }
 
@@ -1700,6 +1900,7 @@ MODULE_DEVICE_TABLE(of, dw_dma_id_table);
 #endif
 
 static struct platform_driver dw_driver = {
+       .probe          = dw_probe,
        .remove         = dw_remove,
        .shutdown       = dw_shutdown,
        .driver = {
@@ -1711,7 +1912,7 @@ static struct platform_driver dw_driver = {
 
 static int __init dw_init(void)
 {
-       return platform_driver_probe(&dw_driver, dw_probe);
+       return platform_driver_register(&dw_driver);
 }
 subsys_initcall(dw_init);
 
index 88965597b7d08c9e2b9987957f85284cc9851f4c..88dd8eb31957771e26f67a099cd0d7a48fd2d8aa 100644 (file)
@@ -9,6 +9,7 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/dmaengine.h>
 #include <linux/dw_dmac.h>
 
 #define DW_DMA_MAX_NR_CHANNELS 8
@@ -184,15 +185,15 @@ enum dw_dmac_flags {
 };
 
 struct dw_dma_chan {
-       struct dma_chan         chan;
-       void __iomem            *ch_regs;
-       u8                      mask;
-       u8                      priority;
-       bool                    paused;
-       bool                    initialized;
+       struct dma_chan                 chan;
+       void __iomem                    *ch_regs;
+       u8                              mask;
+       u8                              priority;
+       enum dma_transfer_direction     direction;
+       bool                            paused;
+       bool                            initialized;
 
        /* software emulation of the LLP transfers */
-       struct list_head        *tx_list;
        struct list_head        *tx_node_active;
 
        spinlock_t              lock;
@@ -202,6 +203,7 @@ struct dw_dma_chan {
        struct list_head        active_list;
        struct list_head        queue;
        struct list_head        free_list;
+       u32                     residue;
        struct dw_cyclic_desc   *cdesc;
 
        unsigned int            descs_allocated;
@@ -212,9 +214,6 @@ struct dw_dma_chan {
 
        /* configuration passed via DMA_SLAVE_CONFIG */
        struct dma_slave_config dma_sconfig;
-
-       /* backlink to dw_dma */
-       struct dw_dma           *dw;
 };
 
 static inline struct dw_dma_chan_regs __iomem *
@@ -236,9 +235,14 @@ static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan)
 struct dw_dma {
        struct dma_device       dma;
        void __iomem            *regs;
+       struct dma_pool         *desc_pool;
        struct tasklet_struct   tasklet;
        struct clk              *clk;
 
+       /* slave information */
+       struct dw_dma_slave     *sd;
+       unsigned int            sd_count;
+
        u8                      all_chan_mask;
 
        /* hardware configuration */
@@ -293,8 +297,11 @@ struct dw_desc {
        struct list_head                tx_list;
        struct dma_async_tx_descriptor  txd;
        size_t                          len;
+       size_t                          total_len;
 };
 
+#define to_dw_desc(h)  list_entry(h, struct dw_desc, desc_node)
+
 static inline struct dw_desc *
 txd_to_dw_desc(struct dma_async_tx_descriptor *txd)
 {
index f424298f1ac5094513f6ac9f3e3a64cbaa681ba6..e57cce3322878e41980c5c14bbc7791fa635225f 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 
-#include <mach/edma.h>
+#include <linux/platform_data/edma.h>
 
 #include "dmaengine.h"
 #include "virt-dma.h"
@@ -69,9 +69,8 @@ struct edma_chan {
        int                             ch_num;
        bool                            alloced;
        int                             slot[EDMA_MAX_SLOTS];
-       dma_addr_t                      addr;
-       int                             addr_width;
-       int                             maxburst;
+       struct dma_slave_config         cfg;
+       struct dmaengine_chan_caps      caps;
 };
 
 struct edma_cc {
@@ -178,29 +177,14 @@ static int edma_terminate_all(struct edma_chan *echan)
        return 0;
 }
 
-
 static int edma_slave_config(struct edma_chan *echan,
-       struct dma_slave_config *config)
+       struct dma_slave_config *cfg)
 {
-       if ((config->src_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES) ||
-           (config->dst_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES))
+       if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
+           cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
                return -EINVAL;
 
-       if (config->direction == DMA_MEM_TO_DEV) {
-               if (config->dst_addr)
-                       echan->addr = config->dst_addr;
-               if (config->dst_addr_width)
-                       echan->addr_width = config->dst_addr_width;
-               if (config->dst_maxburst)
-                       echan->maxburst = config->dst_maxburst;
-       } else if (config->direction == DMA_DEV_TO_MEM) {
-               if (config->src_addr)
-                       echan->addr = config->src_addr;
-               if (config->src_addr_width)
-                       echan->addr_width = config->src_addr_width;
-               if (config->src_maxburst)
-                       echan->maxburst = config->src_maxburst;
-       }
+       memcpy(&echan->cfg, cfg, sizeof(echan->cfg));
 
        return 0;
 }
@@ -235,6 +219,9 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
        struct edma_chan *echan = to_edma_chan(chan);
        struct device *dev = chan->device->dev;
        struct edma_desc *edesc;
+       dma_addr_t dev_addr;
+       enum dma_slave_buswidth dev_width;
+       u32 burst;
        struct scatterlist *sg;
        int i;
        int acnt, bcnt, ccnt, src, dst, cidx;
@@ -243,7 +230,20 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
        if (unlikely(!echan || !sgl || !sg_len))
                return NULL;
 
-       if (echan->addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) {
+       if (direction == DMA_DEV_TO_MEM) {
+               dev_addr = echan->cfg.src_addr;
+               dev_width = echan->cfg.src_addr_width;
+               burst = echan->cfg.src_maxburst;
+       } else if (direction == DMA_MEM_TO_DEV) {
+               dev_addr = echan->cfg.dst_addr;
+               dev_width = echan->cfg.dst_addr_width;
+               burst = echan->cfg.dst_maxburst;
+       } else {
+               dev_err(dev, "%s: bad direction?\n", __func__);
+               return NULL;
+       }
+
+       if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) {
                dev_err(dev, "Undefined slave buswidth\n");
                return NULL;
        }
@@ -275,14 +275,14 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
                        }
                }
 
-               acnt = echan->addr_width;
+               acnt = dev_width;
 
                /*
                 * If the maxburst is equal to the fifo width, use
                 * A-synced transfers. This allows for large contiguous
                 * buffer transfers using only one PaRAM set.
                 */
-               if (echan->maxburst == 1) {
+               if (burst == 1) {
                        edesc->absync = false;
                        ccnt = sg_dma_len(sg) / acnt / (SZ_64K - 1);
                        bcnt = sg_dma_len(sg) / acnt - ccnt * (SZ_64K - 1);
@@ -302,7 +302,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
                 */
                } else {
                        edesc->absync = true;
-                       bcnt = echan->maxburst;
+                       bcnt = burst;
                        ccnt = sg_dma_len(sg) / (acnt * bcnt);
                        if (ccnt > (SZ_64K - 1)) {
                                dev_err(dev, "Exceeded max SG segment size\n");
@@ -313,13 +313,13 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
 
                if (direction == DMA_MEM_TO_DEV) {
                        src = sg_dma_address(sg);
-                       dst = echan->addr;
+                       dst = dev_addr;
                        src_bidx = acnt;
                        src_cidx = cidx;
                        dst_bidx = 0;
                        dst_cidx = 0;
                } else {
-                       src = echan->addr;
+                       src = dev_addr;
                        dst = sg_dma_address(sg);
                        src_bidx = 0;
                        src_cidx = 0;
@@ -463,6 +463,28 @@ static void edma_issue_pending(struct dma_chan *chan)
        spin_unlock_irqrestore(&echan->vchan.lock, flags);
 }
 
+static struct dmaengine_chan_caps
+*edma_get_channel_caps(struct dma_chan *chan, enum dma_transfer_direction dir)
+{
+       struct edma_chan *echan;
+       enum dma_slave_buswidth width = 0;
+       u32 burst = 0;
+
+       if (chan) {
+               echan = to_edma_chan(chan);
+               if (dir == DMA_MEM_TO_DEV) {
+                       width = echan->cfg.dst_addr_width;
+                       burst = echan->cfg.dst_maxburst;
+               } else if (dir == DMA_DEV_TO_MEM) {
+                       width = echan->cfg.src_addr_width;
+                       burst = echan->cfg.src_maxburst;
+               }
+               echan->caps.seg_len = (SZ_64K - 1) * width * burst;
+               return &echan->caps;
+       }
+       return NULL;
+}
+
 static size_t edma_desc_size(struct edma_desc *edesc)
 {
        int i;
@@ -522,6 +544,9 @@ static void __init edma_chan_init(struct edma_cc *ecc,
                echan->ch_num = EDMA_CTLR_CHAN(ecc->ctlr, i);
                echan->ecc = ecc;
                echan->vchan.desc_free = edma_desc_free;
+               dma_cap_set(DMA_SLAVE, echan->caps.cap_mask);
+               dma_cap_set(DMA_SG, echan->caps.cap_mask);
+               echan->caps.seg_nr = MAX_NR_SG;
 
                vchan_init(&echan->vchan, dma);
 
@@ -538,6 +563,7 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma,
        dma->device_alloc_chan_resources = edma_alloc_chan_resources;
        dma->device_free_chan_resources = edma_free_chan_resources;
        dma->device_issue_pending = edma_issue_pending;
+       dma->device_channel_caps = edma_get_channel_caps;
        dma->device_tx_status = edma_tx_status;
        dma->device_control = edma_control;
        dma->dev = dev;
index bcfde400904ff9a6dd05e957eb1159d5fc98f771..f2bf8c0c46757d0dbd351af10660cc5643f2cfbd 100644 (file)
@@ -903,8 +903,7 @@ static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan)
                        switch (data->port) {
                        case EP93XX_DMA_SSP:
                        case EP93XX_DMA_IDE:
-                               if (data->direction != DMA_MEM_TO_DEV &&
-                                   data->direction != DMA_DEV_TO_MEM)
+                               if (!is_slave_direction(data->direction))
                                        return -EINVAL;
                                break;
                        default:
index 1a68a8ba87e6c74f2f5afb72afac2d0254eae5cd..1879a5942bfc73ffb74e498cdcb7229ee0a52aaf 100644 (file)
@@ -833,14 +833,14 @@ int ioat_dma_self_test(struct ioatdma_device *device)
 
        dma_src = dma_map_single(dev, src, IOAT_TEST_SIZE, DMA_TO_DEVICE);
        dma_dest = dma_map_single(dev, dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE);
-       flags = DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_DEST_UNMAP_SINGLE |
+       flags = DMA_COMPL_SKIP_SRC_UNMAP | DMA_COMPL_SKIP_DEST_UNMAP |
                DMA_PREP_INTERRUPT;
        tx = device->common.device_prep_dma_memcpy(dma_chan, dma_dest, dma_src,
                                                   IOAT_TEST_SIZE, flags);
        if (!tx) {
                dev_err(dev, "Self-test prep failed, disabling\n");
                err = -ENODEV;
-               goto free_resources;
+               goto unmap_dma;
        }
 
        async_tx_ack(tx);
@@ -851,7 +851,7 @@ int ioat_dma_self_test(struct ioatdma_device *device)
        if (cookie < 0) {
                dev_err(dev, "Self-test setup failed, disabling\n");
                err = -ENODEV;
-               goto free_resources;
+               goto unmap_dma;
        }
        dma->device_issue_pending(dma_chan);
 
@@ -862,7 +862,7 @@ int ioat_dma_self_test(struct ioatdma_device *device)
                                        != DMA_SUCCESS) {
                dev_err(dev, "Self-test copy timed out, disabling\n");
                err = -ENODEV;
-               goto free_resources;
+               goto unmap_dma;
        }
        if (memcmp(src, dest, IOAT_TEST_SIZE)) {
                dev_err(dev, "Self-test copy failed compare, disabling\n");
@@ -870,6 +870,9 @@ int ioat_dma_self_test(struct ioatdma_device *device)
                goto free_resources;
        }
 
+unmap_dma:
+       dma_unmap_single(dev, dma_src, IOAT_TEST_SIZE, DMA_TO_DEVICE);
+       dma_unmap_single(dev, dma_dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE);
 free_resources:
        dma->device_free_chan_resources(dma_chan);
 out:
index 3e9d66920eb3491d85b8c6027ac786a76242e107..908d75bbbe0775cace690dd1355d5e24e46ee5f9 100644 (file)
@@ -863,6 +863,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        unsigned long tmo;
        struct device *dev = &device->pdev->dev;
        struct dma_device *dma = &device->common;
+       u8 op = 0;
 
        dev_dbg(dev, "%s\n", __func__);
 
@@ -908,18 +909,22 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        }
 
        /* test xor */
+       op = IOAT_OP_XOR;
+
        dest_dma = dma_map_page(dev, dest, 0, PAGE_SIZE, DMA_FROM_DEVICE);
        for (i = 0; i < IOAT_NUM_SRC_TEST; i++)
                dma_srcs[i] = dma_map_page(dev, xor_srcs[i], 0, PAGE_SIZE,
                                           DMA_TO_DEVICE);
        tx = dma->device_prep_dma_xor(dma_chan, dest_dma, dma_srcs,
                                      IOAT_NUM_SRC_TEST, PAGE_SIZE,
-                                     DMA_PREP_INTERRUPT);
+                                     DMA_PREP_INTERRUPT |
+                                     DMA_COMPL_SKIP_SRC_UNMAP |
+                                     DMA_COMPL_SKIP_DEST_UNMAP);
 
        if (!tx) {
                dev_err(dev, "Self-test xor prep failed\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
 
        async_tx_ack(tx);
@@ -930,7 +935,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        if (cookie < 0) {
                dev_err(dev, "Self-test xor setup failed\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
        dma->device_issue_pending(dma_chan);
 
@@ -939,9 +944,13 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
                dev_err(dev, "Self-test xor timed out\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
 
+       dma_unmap_page(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE);
+       for (i = 0; i < IOAT_NUM_SRC_TEST; i++)
+               dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE);
+
        dma_sync_single_for_cpu(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE);
        for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) {
                u32 *ptr = page_address(dest);
@@ -957,6 +966,8 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        if (!dma_has_cap(DMA_XOR_VAL, dma_chan->device->cap_mask))
                goto free_resources;
 
+       op = IOAT_OP_XOR_VAL;
+
        /* validate the sources with the destintation page */
        for (i = 0; i < IOAT_NUM_SRC_TEST; i++)
                xor_val_srcs[i] = xor_srcs[i];
@@ -969,11 +980,13 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
                                           DMA_TO_DEVICE);
        tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs,
                                          IOAT_NUM_SRC_TEST + 1, PAGE_SIZE,
-                                         &xor_val_result, DMA_PREP_INTERRUPT);
+                                         &xor_val_result, DMA_PREP_INTERRUPT |
+                                         DMA_COMPL_SKIP_SRC_UNMAP |
+                                         DMA_COMPL_SKIP_DEST_UNMAP);
        if (!tx) {
                dev_err(dev, "Self-test zero prep failed\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
 
        async_tx_ack(tx);
@@ -984,7 +997,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        if (cookie < 0) {
                dev_err(dev, "Self-test zero setup failed\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
        dma->device_issue_pending(dma_chan);
 
@@ -993,9 +1006,12 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
                dev_err(dev, "Self-test validate timed out\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
 
+       for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++)
+               dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE);
+
        if (xor_val_result != 0) {
                dev_err(dev, "Self-test validate failed compare\n");
                err = -ENODEV;
@@ -1007,14 +1023,18 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
                goto free_resources;
 
        /* test memset */
+       op = IOAT_OP_FILL;
+
        dma_addr = dma_map_page(dev, dest, 0,
                        PAGE_SIZE, DMA_FROM_DEVICE);
        tx = dma->device_prep_dma_memset(dma_chan, dma_addr, 0, PAGE_SIZE,
-                                        DMA_PREP_INTERRUPT);
+                                        DMA_PREP_INTERRUPT |
+                                        DMA_COMPL_SKIP_SRC_UNMAP |
+                                        DMA_COMPL_SKIP_DEST_UNMAP);
        if (!tx) {
                dev_err(dev, "Self-test memset prep failed\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
 
        async_tx_ack(tx);
@@ -1025,7 +1045,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        if (cookie < 0) {
                dev_err(dev, "Self-test memset setup failed\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
        dma->device_issue_pending(dma_chan);
 
@@ -1034,9 +1054,11 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
                dev_err(dev, "Self-test memset timed out\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
 
+       dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE);
+
        for (i = 0; i < PAGE_SIZE/sizeof(u32); i++) {
                u32 *ptr = page_address(dest);
                if (ptr[i]) {
@@ -1047,17 +1069,21 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        }
 
        /* test for non-zero parity sum */
+       op = IOAT_OP_XOR_VAL;
+
        xor_val_result = 0;
        for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++)
                dma_srcs[i] = dma_map_page(dev, xor_val_srcs[i], 0, PAGE_SIZE,
                                           DMA_TO_DEVICE);
        tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs,
                                          IOAT_NUM_SRC_TEST + 1, PAGE_SIZE,
-                                         &xor_val_result, DMA_PREP_INTERRUPT);
+                                         &xor_val_result, DMA_PREP_INTERRUPT |
+                                         DMA_COMPL_SKIP_SRC_UNMAP |
+                                         DMA_COMPL_SKIP_DEST_UNMAP);
        if (!tx) {
                dev_err(dev, "Self-test 2nd zero prep failed\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
 
        async_tx_ack(tx);
@@ -1068,7 +1094,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        if (cookie < 0) {
                dev_err(dev, "Self-test  2nd zero setup failed\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
        dma->device_issue_pending(dma_chan);
 
@@ -1077,15 +1103,31 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
        if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
                dev_err(dev, "Self-test 2nd validate timed out\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
 
        if (xor_val_result != SUM_CHECK_P_RESULT) {
                dev_err(dev, "Self-test validate failed compare\n");
                err = -ENODEV;
-               goto free_resources;
+               goto dma_unmap;
        }
 
+       for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++)
+               dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE);
+
+       goto free_resources;
+dma_unmap:
+       if (op == IOAT_OP_XOR) {
+               dma_unmap_page(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE);
+               for (i = 0; i < IOAT_NUM_SRC_TEST; i++)
+                       dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE,
+                                      DMA_TO_DEVICE);
+       } else if (op == IOAT_OP_XOR_VAL) {
+               for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++)
+                       dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE,
+                                      DMA_TO_DEVICE);
+       } else if (op == IOAT_OP_FILL)
+               dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE);
 free_resources:
        dma->device_free_chan_resources(dma_chan);
 out:
@@ -1126,12 +1168,7 @@ static int ioat3_reset_hw(struct ioat_chan_common *chan)
        chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET);
        writel(chanerr, chan->reg_base + IOAT_CHANERR_OFFSET);
 
-       /* -= IOAT ver.3 workarounds =- */
-       /* Write CHANERRMSK_INT with 3E07h to mask out the errors
-        * that can cause stability issues for IOAT ver.3, and clear any
-        * pending errors
-        */
-       pci_write_config_dword(pdev, IOAT_PCI_CHANERRMASK_INT_OFFSET, 0x3e07);
+       /* clear any pending errors */
        err = pci_read_config_dword(pdev, IOAT_PCI_CHANERR_INT_OFFSET, &chanerr);
        if (err) {
                dev_err(&pdev->dev, "channel error register unreachable\n");
@@ -1187,6 +1224,26 @@ static bool is_snb_ioat(struct pci_dev *pdev)
        }
 }
 
+static bool is_ivb_ioat(struct pci_dev *pdev)
+{
+       switch (pdev->device) {
+       case PCI_DEVICE_ID_INTEL_IOAT_IVB0:
+       case PCI_DEVICE_ID_INTEL_IOAT_IVB1:
+       case PCI_DEVICE_ID_INTEL_IOAT_IVB2:
+       case PCI_DEVICE_ID_INTEL_IOAT_IVB3:
+       case PCI_DEVICE_ID_INTEL_IOAT_IVB4:
+       case PCI_DEVICE_ID_INTEL_IOAT_IVB5:
+       case PCI_DEVICE_ID_INTEL_IOAT_IVB6:
+       case PCI_DEVICE_ID_INTEL_IOAT_IVB7:
+       case PCI_DEVICE_ID_INTEL_IOAT_IVB8:
+       case PCI_DEVICE_ID_INTEL_IOAT_IVB9:
+               return true;
+       default:
+               return false;
+       }
+
+}
+
 int ioat3_dma_probe(struct ioatdma_device *device, int dca)
 {
        struct pci_dev *pdev = device->pdev;
@@ -1207,7 +1264,7 @@ int ioat3_dma_probe(struct ioatdma_device *device, int dca)
        dma->device_alloc_chan_resources = ioat2_alloc_chan_resources;
        dma->device_free_chan_resources = ioat2_free_chan_resources;
 
-       if (is_jf_ioat(pdev) || is_snb_ioat(pdev))
+       if (is_jf_ioat(pdev) || is_snb_ioat(pdev) || is_ivb_ioat(pdev))
                dma->copy_align = 6;
 
        dma_cap_set(DMA_INTERRUPT, dma->cap_mask);
index d2ff3fda0b18c3b7c8ca1d2fe656ecc53bea0ba3..7cb74c62c7192f5bcae7d5fe6999eb17cb21dfd3 100644 (file)
 #define IOAT_VER_3_0            0x30    /* Version 3.0 */
 #define IOAT_VER_3_2            0x32    /* Version 3.2 */
 
+#define PCI_DEVICE_ID_INTEL_IOAT_IVB0  0x0e20
+#define PCI_DEVICE_ID_INTEL_IOAT_IVB1  0x0e21
+#define PCI_DEVICE_ID_INTEL_IOAT_IVB2  0x0e22
+#define PCI_DEVICE_ID_INTEL_IOAT_IVB3  0x0e23
+#define PCI_DEVICE_ID_INTEL_IOAT_IVB4  0x0e24
+#define PCI_DEVICE_ID_INTEL_IOAT_IVB5  0x0e25
+#define PCI_DEVICE_ID_INTEL_IOAT_IVB6  0x0e26
+#define PCI_DEVICE_ID_INTEL_IOAT_IVB7  0x0e27
+#define PCI_DEVICE_ID_INTEL_IOAT_IVB8  0x0e2e
+#define PCI_DEVICE_ID_INTEL_IOAT_IVB9  0x0e2f
+
 int system_has_dca_enabled(struct pci_dev *pdev);
 
 struct ioat_dma_descriptor {
index 4f686c527ab606750951c98af85acdb007f303ed..71c7ecd80fac8d3b9b33dd909ba2533da17e2e83 100644 (file)
@@ -40,17 +40,6 @@ MODULE_VERSION(IOAT_DMA_VERSION);
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Intel Corporation");
 
-#define PCI_DEVICE_ID_INTEL_IOAT_IVB0  0x0e20
-#define PCI_DEVICE_ID_INTEL_IOAT_IVB1  0x0e21
-#define PCI_DEVICE_ID_INTEL_IOAT_IVB2  0x0e22
-#define PCI_DEVICE_ID_INTEL_IOAT_IVB3  0x0e23
-#define PCI_DEVICE_ID_INTEL_IOAT_IVB4  0x0e24
-#define PCI_DEVICE_ID_INTEL_IOAT_IVB5  0x0e25
-#define PCI_DEVICE_ID_INTEL_IOAT_IVB6  0x0e26
-#define PCI_DEVICE_ID_INTEL_IOAT_IVB7  0x0e27
-#define PCI_DEVICE_ID_INTEL_IOAT_IVB8  0x0e2e
-#define PCI_DEVICE_ID_INTEL_IOAT_IVB9  0x0e2f
-
 static struct pci_device_id ioat_pci_tbl[] = {
        /* I/OAT v1 platforms */
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT) },
index eacb8be99812dc85168d0d521346f5e7f4d01ea6..7dafb9f3785fdd22f4a942ce36a77913c84da27b 100644 (file)
@@ -936,7 +936,7 @@ static irqreturn_t iop_adma_err_handler(int irq, void *data)
        struct iop_adma_chan *chan = data;
        unsigned long status = iop_chan_get_status(chan);
 
-       dev_printk(KERN_ERR, chan->device->common.dev,
+       dev_err(chan->device->common.dev,
                "error ( %s%s%s%s%s%s%s)\n",
                iop_is_err_int_parity(status, chan) ? "int_parity " : "",
                iop_is_err_mcu_abort(status, chan) ? "mcu_abort " : "",
@@ -1017,7 +1017,7 @@ static int iop_adma_memcpy_self_test(struct iop_adma_device *device)
 
        if (iop_adma_status(dma_chan, cookie, NULL) !=
                        DMA_SUCCESS) {
-               dev_printk(KERN_ERR, dma_chan->device->dev,
+               dev_err(dma_chan->device->dev,
                        "Self-test copy timed out, disabling\n");
                err = -ENODEV;
                goto free_resources;
@@ -1027,7 +1027,7 @@ static int iop_adma_memcpy_self_test(struct iop_adma_device *device)
        dma_sync_single_for_cpu(&iop_chan->device->pdev->dev, dest_dma,
                IOP_ADMA_TEST_SIZE, DMA_FROM_DEVICE);
        if (memcmp(src, dest, IOP_ADMA_TEST_SIZE)) {
-               dev_printk(KERN_ERR, dma_chan->device->dev,
+               dev_err(dma_chan->device->dev,
                        "Self-test copy failed compare, disabling\n");
                err = -ENODEV;
                goto free_resources;
@@ -1117,7 +1117,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device)
 
        if (iop_adma_status(dma_chan, cookie, NULL) !=
                DMA_SUCCESS) {
-               dev_printk(KERN_ERR, dma_chan->device->dev,
+               dev_err(dma_chan->device->dev,
                        "Self-test xor timed out, disabling\n");
                err = -ENODEV;
                goto free_resources;
@@ -1129,7 +1129,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device)
        for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) {
                u32 *ptr = page_address(dest);
                if (ptr[i] != cmp_word) {
-                       dev_printk(KERN_ERR, dma_chan->device->dev,
+                       dev_err(dma_chan->device->dev,
                                "Self-test xor failed compare, disabling\n");
                        err = -ENODEV;
                        goto free_resources;
@@ -1163,14 +1163,14 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device)
        msleep(8);
 
        if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
-               dev_printk(KERN_ERR, dma_chan->device->dev,
+               dev_err(dma_chan->device->dev,
                        "Self-test zero sum timed out, disabling\n");
                err = -ENODEV;
                goto free_resources;
        }
 
        if (zero_sum_result != 0) {
-               dev_printk(KERN_ERR, dma_chan->device->dev,
+               dev_err(dma_chan->device->dev,
                        "Self-test zero sum failed compare, disabling\n");
                err = -ENODEV;
                goto free_resources;
@@ -1187,7 +1187,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device)
        msleep(8);
 
        if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
-               dev_printk(KERN_ERR, dma_chan->device->dev,
+               dev_err(dma_chan->device->dev,
                        "Self-test memset timed out, disabling\n");
                err = -ENODEV;
                goto free_resources;
@@ -1196,7 +1196,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device)
        for (i = 0; i < PAGE_SIZE/sizeof(u32); i++) {
                u32 *ptr = page_address(dest);
                if (ptr[i]) {
-                       dev_printk(KERN_ERR, dma_chan->device->dev,
+                       dev_err(dma_chan->device->dev,
                                "Self-test memset failed compare, disabling\n");
                        err = -ENODEV;
                        goto free_resources;
@@ -1219,14 +1219,14 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device)
        msleep(8);
 
        if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
-               dev_printk(KERN_ERR, dma_chan->device->dev,
+               dev_err(dma_chan->device->dev,
                        "Self-test non-zero sum timed out, disabling\n");
                err = -ENODEV;
                goto free_resources;
        }
 
        if (zero_sum_result != 1) {
-               dev_printk(KERN_ERR, dma_chan->device->dev,
+               dev_err(dma_chan->device->dev,
                        "Self-test non-zero sum failed compare, disabling\n");
                err = -ENODEV;
                goto free_resources;
@@ -1579,15 +1579,14 @@ static int iop_adma_probe(struct platform_device *pdev)
                        goto err_free_iop_chan;
        }
 
-       dev_printk(KERN_INFO, &pdev->dev, "Intel(R) IOP: "
-         "( %s%s%s%s%s%s%s)\n",
-         dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "pq " : "",
-         dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "",
-         dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "",
-         dma_has_cap(DMA_XOR_VAL, dma_dev->cap_mask) ? "xor_val " : "",
-         dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)  ? "fill " : "",
-         dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "",
-         dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : "");
+       dev_info(&pdev->dev, "Intel(R) IOP: ( %s%s%s%s%s%s%s)\n",
+                dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "pq " : "",
+                dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "",
+                dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "",
+                dma_has_cap(DMA_XOR_VAL, dma_dev->cap_mask) ? "xor_val " : "",
+                dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)  ? "fill " : "",
+                dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "",
+                dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : "");
 
        dma_async_device_register(dma_dev);
        goto out;
@@ -1651,8 +1650,8 @@ static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan)
                /* run the descriptor */
                iop_chan_enable(iop_chan);
        } else
-               dev_printk(KERN_ERR, iop_chan->device->common.dev,
-                        "failed to allocate null descriptor\n");
+               dev_err(iop_chan->device->common.dev,
+                       "failed to allocate null descriptor\n");
        spin_unlock_bh(&iop_chan->lock);
 }
 
@@ -1704,7 +1703,7 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan)
                /* run the descriptor */
                iop_chan_enable(iop_chan);
        } else
-               dev_printk(KERN_ERR, iop_chan->device->common.dev,
+               dev_err(iop_chan->device->common.dev,
                        "failed to allocate null descriptor\n");
        spin_unlock_bh(&iop_chan->lock);
 }
index 65855373cee656888cc68739e622abd8ce10e59f..8c61d17a86bf182b750d14df3b57f51708a2b326 100644 (file)
@@ -1347,7 +1347,7 @@ static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan
            chan->chan_id != IDMAC_IC_7)
                return NULL;
 
-       if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) {
+       if (!is_slave_direction(direction)) {
                dev_err(chan->device->dev, "Invalid DMA direction %d!\n", direction);
                return NULL;
        }
index a5ee37d5320f9e94e4cdbf73a144a86216045078..2e284a4438bcac3161fd3107931dc72156ff9601 100644 (file)
@@ -44,7 +44,6 @@ static void ipu_write_reg(struct ipu *ipu, u32 value, unsigned long reg)
 struct ipu_irq_bank {
        unsigned int    control;
        unsigned int    status;
-       spinlock_t      lock;
        struct ipu      *ipu;
 };
 
index c6d98c00f05c9bbe64a87696194cc12f29094bb3..81d7b89852fbaa3628432f849dcdc5d98a2d5501 100644 (file)
@@ -617,10 +617,8 @@ static int mmp_pdma_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd,
                else if (maxburst == 32)
                        chan->dcmd |= DCMD_BURST32;
 
-               if (cfg) {
-                       chan->dir = cfg->direction;
-                       chan->drcmr = cfg->slave_id;
-               }
+               chan->dir = cfg->direction;
+               chan->drcmr = cfg->slave_id;
                chan->dev_addr = addr;
                break;
        default:
index e17fad03cb804b7d538bd2928923a50928475d4f..d64ae14f2706e52c5c80144ae1ea73b0607b9aad 100644 (file)
@@ -210,7 +210,7 @@ static void mv_set_mode(struct mv_xor_chan *chan,
                break;
        default:
                dev_err(mv_chan_to_devp(chan),
-                       "error: unsupported operation %d.\n",
+                       "error: unsupported operation %d\n",
                        type);
                BUG();
                return;
@@ -828,28 +828,22 @@ static void mv_dump_xor_regs(struct mv_xor_chan *chan)
        u32 val;
 
        val = __raw_readl(XOR_CONFIG(chan));
-       dev_err(mv_chan_to_devp(chan),
-               "config       0x%08x.\n", val);
+       dev_err(mv_chan_to_devp(chan), "config       0x%08x\n", val);
 
        val = __raw_readl(XOR_ACTIVATION(chan));
-       dev_err(mv_chan_to_devp(chan),
-               "activation   0x%08x.\n", val);
+       dev_err(mv_chan_to_devp(chan), "activation   0x%08x\n", val);
 
        val = __raw_readl(XOR_INTR_CAUSE(chan));
-       dev_err(mv_chan_to_devp(chan),
-               "intr cause   0x%08x.\n", val);
+       dev_err(mv_chan_to_devp(chan), "intr cause   0x%08x\n", val);
 
        val = __raw_readl(XOR_INTR_MASK(chan));
-       dev_err(mv_chan_to_devp(chan),
-               "intr mask    0x%08x.\n", val);
+       dev_err(mv_chan_to_devp(chan), "intr mask    0x%08x\n", val);
 
        val = __raw_readl(XOR_ERROR_CAUSE(chan));
-       dev_err(mv_chan_to_devp(chan),
-               "error cause  0x%08x.\n", val);
+       dev_err(mv_chan_to_devp(chan), "error cause  0x%08x\n", val);
 
        val = __raw_readl(XOR_ERROR_ADDR(chan));
-       dev_err(mv_chan_to_devp(chan),
-               "error addr   0x%08x.\n", val);
+       dev_err(mv_chan_to_devp(chan), "error addr   0x%08x\n", val);
 }
 
 static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan,
@@ -862,7 +856,7 @@ static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan,
        }
 
        dev_err(mv_chan_to_devp(chan),
-               "error on chan %d. intr cause 0x%08x.\n",
+               "error on chan %d. intr cause 0x%08x\n",
                chan->idx, intr_cause);
 
        mv_dump_xor_regs(chan);
@@ -1052,9 +1046,8 @@ mv_xor_xor_self_test(struct mv_xor_chan *mv_chan)
                u32 *ptr = page_address(dest);
                if (ptr[i] != cmp_word) {
                        dev_err(dma_chan->device->dev,
-                               "Self-test xor failed compare, disabling."
-                               " index %d, data %x, expected %x\n", i,
-                               ptr[i], cmp_word);
+                               "Self-test xor failed compare, disabling. index %d, data %x, expected %x\n",
+                               i, ptr[i], cmp_word);
                        err = -ENODEV;
                        goto free_resources;
                }
@@ -1194,12 +1187,11 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
                        goto err_free_irq;
        }
 
-       dev_info(&pdev->dev, "Marvell XOR: "
-         "( %s%s%s%s)\n",
-         dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "",
-         dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)  ? "fill " : "",
-         dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "",
-         dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : "");
+       dev_info(&pdev->dev, "Marvell XOR: ( %s%s%s%s)\n",
+                dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "",
+                dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)  ? "fill " : "",
+                dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "",
+                dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : "");
 
        dma_async_device_register(dma_dev);
        return mv_chan;
@@ -1253,7 +1245,7 @@ static int mv_xor_probe(struct platform_device *pdev)
        struct resource *res;
        int i, ret;
 
-       dev_notice(&pdev->dev, "Marvell XOR driver\n");
+       dev_notice(&pdev->dev, "Marvell shared XOR driver\n");
 
        xordev = devm_kzalloc(&pdev->dev, sizeof(*xordev), GFP_KERNEL);
        if (!xordev)
index 9f02e794b12b00d4ed75d62a67bd255f321992ee..8f6d30d37c45bd4e995f160e4e85fc55d81b1fe5 100644 (file)
@@ -109,7 +109,7 @@ struct mxs_dma_chan {
        struct dma_chan                 chan;
        struct dma_async_tx_descriptor  desc;
        struct tasklet_struct           tasklet;
-       int                             chan_irq;
+       unsigned int                    chan_irq;
        struct mxs_dma_ccw              *ccw;
        dma_addr_t                      ccw_phys;
        int                             desc_count;
@@ -441,7 +441,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
        struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
        struct mxs_dma_ccw *ccw;
        struct scatterlist *sg;
-       int i, j;
+       u32 i, j;
        u32 *pio;
        bool append = flags & DMA_PREP_INTERRUPT;
        int idx = append ? mxs_chan->desc_count : 0;
@@ -537,8 +537,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic(
 {
        struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
        struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
-       int num_periods = buf_len / period_len;
-       int i = 0, buf = 0;
+       u32 num_periods = buf_len / period_len;
+       u32 i = 0, buf = 0;
 
        if (mxs_chan->status == DMA_IN_PROGRESS)
                return NULL;
index 3f2617255ef21b07fde6a6d71279e1ec80ba91ce..d01faeb0f27c15ee9a55e78d257caf58136ff8d9 100644 (file)
@@ -1029,18 +1029,7 @@ static struct pci_driver pch_dma_driver = {
 #endif
 };
 
-static int __init pch_dma_init(void)
-{
-       return pci_register_driver(&pch_dma_driver);
-}
-
-static void __exit pch_dma_exit(void)
-{
-       pci_unregister_driver(&pch_dma_driver);
-}
-
-module_init(pch_dma_init);
-module_exit(pch_dma_exit);
+module_pci_driver(pch_dma_driver);
 
 MODULE_DESCRIPTION("Intel EG20T PCH / LAPIS Semicon ML7213/ML7223/ML7831 IOH "
                   "DMA controller driver");
index 80680eee017141aa86f72388e0a98305533e7966..316a43e2be2abab019262059b134b767596b578c 100644 (file)
@@ -2866,7 +2866,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
        pdat = adev->dev.platform_data;
 
        /* Allocate a new DMAC and its Channels */
-       pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL);
+       pdmac = devm_kzalloc(&adev->dev, sizeof(*pdmac), GFP_KERNEL);
        if (!pdmac) {
                dev_err(&adev->dev, "unable to allocate mem\n");
                return -ENOMEM;
@@ -2878,13 +2878,9 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
        pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0;
 
        res = &adev->res;
-       request_mem_region(res->start, resource_size(res), "dma-pl330");
-
-       pi->base = ioremap(res->start, resource_size(res));
-       if (!pi->base) {
-               ret = -ENXIO;
-               goto probe_err1;
-       }
+       pi->base = devm_request_and_ioremap(&adev->dev, res);
+       if (!pi->base)
+               return -ENXIO;
 
        amba_set_drvdata(adev, pdmac);
 
@@ -2892,11 +2888,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
        ret = request_irq(irq, pl330_irq_handler, 0,
                        dev_name(&adev->dev), pi);
        if (ret)
-               goto probe_err2;
+               return ret;
 
        ret = pl330_add(pi);
        if (ret)
-               goto probe_err3;
+               goto probe_err1;
 
        INIT_LIST_HEAD(&pdmac->desc_pool);
        spin_lock_init(&pdmac->pool_lock);
@@ -2918,7 +2914,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
        if (!pdmac->peripherals) {
                ret = -ENOMEM;
                dev_err(&adev->dev, "unable to allocate pdmac->peripherals\n");
-               goto probe_err4;
+               goto probe_err2;
        }
 
        for (i = 0; i < num_chan; i++) {
@@ -2962,7 +2958,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
        ret = dma_async_device_register(pd);
        if (ret) {
                dev_err(&adev->dev, "unable to register DMAC\n");
-               goto probe_err4;
+               goto probe_err2;
        }
 
        dev_info(&adev->dev,
@@ -2975,15 +2971,10 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
 
        return 0;
 
-probe_err4:
-       pl330_del(pi);
-probe_err3:
-       free_irq(irq, pi);
 probe_err2:
-       iounmap(pi->base);
+       pl330_del(pi);
 probe_err1:
-       release_mem_region(res->start, resource_size(res));
-       kfree(pdmac);
+       free_irq(irq, pi);
 
        return ret;
 }
@@ -2993,7 +2984,6 @@ static int pl330_remove(struct amba_device *adev)
        struct dma_pl330_dmac *pdmac = amba_get_drvdata(adev);
        struct dma_pl330_chan *pch, *_p;
        struct pl330_info *pi;
-       struct resource *res;
        int irq;
 
        if (!pdmac)
@@ -3020,13 +3010,6 @@ static int pl330_remove(struct amba_device *adev)
        irq = adev->irq[0];
        free_irq(irq, pi);
 
-       iounmap(pi->base);
-
-       res = &adev->res;
-       release_mem_region(res->start, resource_size(res));
-
-       kfree(pdmac);
-
        return 0;
 }
 
index f4cd946d259db9939ce78c6443c9075fe8a3cb0f..4acb85a102508adda7e0a91201431b8549218522 100644 (file)
@@ -638,9 +638,6 @@ static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        unsigned long flags;
        int ret;
 
-       if (!chan)
-               return -EINVAL;
-
        switch (cmd) {
        case DMA_TERMINATE_ALL:
                spin_lock_irqsave(&schan->chan_lock, flags);
index 94674a96c64613a0b2e79b04a62d4c1f4007c0d0..1d627e2391f495ef2b8b9fd2e6b5359da940df2d 100644 (file)
@@ -32,7 +32,9 @@
 #define SIRFSOC_DMA_CH_VALID                    0x140
 #define SIRFSOC_DMA_CH_INT                      0x144
 #define SIRFSOC_DMA_INT_EN                      0x148
+#define SIRFSOC_DMA_INT_EN_CLR                 0x14C
 #define SIRFSOC_DMA_CH_LOOP_CTRL                0x150
+#define SIRFSOC_DMA_CH_LOOP_CTRL_CLR            0x15C
 
 #define SIRFSOC_DMA_MODE_CTRL_BIT               4
 #define SIRFSOC_DMA_DIR_CTRL_BIT                5
@@ -76,6 +78,7 @@ struct sirfsoc_dma {
        struct sirfsoc_dma_chan         channels[SIRFSOC_DMA_CHANNELS];
        void __iomem                    *base;
        int                             irq;
+       bool                            is_marco;
 };
 
 #define DRV_NAME       "sirfsoc_dma"
@@ -288,17 +291,67 @@ static int sirfsoc_dma_terminate_all(struct sirfsoc_dma_chan *schan)
        int cid = schan->chan.chan_id;
        unsigned long flags;
 
-       writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) &
-               ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN);
-       writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID);
+       spin_lock_irqsave(&schan->lock, flags);
 
-       writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL)
-               & ~((1 << cid) | 1 << (cid + 16)),
+       if (!sdma->is_marco) {
+               writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) &
+                       ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN);
+               writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL)
+                       & ~((1 << cid) | 1 << (cid + 16)),
                        sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+       } else {
+               writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_INT_EN_CLR);
+               writel_relaxed((1 << cid) | 1 << (cid + 16),
+                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL_CLR);
+       }
+
+       writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID);
 
-       spin_lock_irqsave(&schan->lock, flags);
        list_splice_tail_init(&schan->active, &schan->free);
        list_splice_tail_init(&schan->queued, &schan->free);
+
+       spin_unlock_irqrestore(&schan->lock, flags);
+
+       return 0;
+}
+
+static int sirfsoc_dma_pause_chan(struct sirfsoc_dma_chan *schan)
+{
+       struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan);
+       int cid = schan->chan.chan_id;
+       unsigned long flags;
+
+       spin_lock_irqsave(&schan->lock, flags);
+
+       if (!sdma->is_marco)
+               writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL)
+                       & ~((1 << cid) | 1 << (cid + 16)),
+                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+       else
+               writel_relaxed((1 << cid) | 1 << (cid + 16),
+                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL_CLR);
+
+       spin_unlock_irqrestore(&schan->lock, flags);
+
+       return 0;
+}
+
+static int sirfsoc_dma_resume_chan(struct sirfsoc_dma_chan *schan)
+{
+       struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan);
+       int cid = schan->chan.chan_id;
+       unsigned long flags;
+
+       spin_lock_irqsave(&schan->lock, flags);
+
+       if (!sdma->is_marco)
+               writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL)
+                       | ((1 << cid) | 1 << (cid + 16)),
+                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+       else
+               writel_relaxed((1 << cid) | 1 << (cid + 16),
+                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+
        spin_unlock_irqrestore(&schan->lock, flags);
 
        return 0;
@@ -311,6 +364,10 @@ static int sirfsoc_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
 
        switch (cmd) {
+       case DMA_PAUSE:
+               return sirfsoc_dma_pause_chan(schan);
+       case DMA_RESUME:
+               return sirfsoc_dma_resume_chan(schan);
        case DMA_TERMINATE_ALL:
                return sirfsoc_dma_terminate_all(schan);
        case DMA_SLAVE_CONFIG:
@@ -568,6 +625,9 @@ static int sirfsoc_dma_probe(struct platform_device *op)
                return -ENOMEM;
        }
 
+       if (of_device_is_compatible(dn, "sirf,marco-dmac"))
+               sdma->is_marco = true;
+
        if (of_property_read_u32(dn, "cell-index", &id)) {
                dev_err(dev, "Fail to get DMAC index\n");
                return -ENODEV;
@@ -668,6 +728,7 @@ static int sirfsoc_dma_remove(struct platform_device *op)
 
 static struct of_device_id sirfsoc_dma_match[] = {
        { .compatible = "sirf,prima2-dmac", },
+       { .compatible = "sirf,marco-dmac", },
        {},
 };
 
index 23c5573e62ddd1d6b8aec8be10e9f47bcfaad39e..ad860a221c33355287d69ef88798653602b1bbee 100644 (file)
@@ -53,6 +53,8 @@
 #define D40_ALLOC_PHY          (1 << 30)
 #define D40_ALLOC_LOG_FREE     0
 
+#define MAX(a, b) (((a) < (b)) ? (b) : (a))
+
 /**
  * enum 40_command - The different commands and/or statuses.
  *
@@ -100,8 +102,19 @@ static u32 d40_backup_regs[] = {
 
 #define BACKUP_REGS_SZ ARRAY_SIZE(d40_backup_regs)
 
-/* TODO: Check if all these registers have to be saved/restored on dma40 v3 */
-static u32 d40_backup_regs_v3[] = {
+/*
+ * since 9540 and 8540 has the same HW revision
+ * use v4a for 9540 or ealier
+ * use v4b for 8540 or later
+ * HW revision:
+ * DB8500ed has revision 0
+ * DB8500v1 has revision 2
+ * DB8500v2 has revision 3
+ * AP9540v1 has revision 4
+ * DB8540v1 has revision 4
+ * TODO: Check if all these registers have to be saved/restored on dma40 v4a
+ */
+static u32 d40_backup_regs_v4a[] = {
        D40_DREG_PSEG1,
        D40_DREG_PSEG2,
        D40_DREG_PSEG3,
@@ -120,7 +133,32 @@ static u32 d40_backup_regs_v3[] = {
        D40_DREG_RCEG4,
 };
 
-#define BACKUP_REGS_SZ_V3 ARRAY_SIZE(d40_backup_regs_v3)
+#define BACKUP_REGS_SZ_V4A ARRAY_SIZE(d40_backup_regs_v4a)
+
+static u32 d40_backup_regs_v4b[] = {
+       D40_DREG_CPSEG1,
+       D40_DREG_CPSEG2,
+       D40_DREG_CPSEG3,
+       D40_DREG_CPSEG4,
+       D40_DREG_CPSEG5,
+       D40_DREG_CPCEG1,
+       D40_DREG_CPCEG2,
+       D40_DREG_CPCEG3,
+       D40_DREG_CPCEG4,
+       D40_DREG_CPCEG5,
+       D40_DREG_CRSEG1,
+       D40_DREG_CRSEG2,
+       D40_DREG_CRSEG3,
+       D40_DREG_CRSEG4,
+       D40_DREG_CRSEG5,
+       D40_DREG_CRCEG1,
+       D40_DREG_CRCEG2,
+       D40_DREG_CRCEG3,
+       D40_DREG_CRCEG4,
+       D40_DREG_CRCEG5,
+};
+
+#define BACKUP_REGS_SZ_V4B ARRAY_SIZE(d40_backup_regs_v4b)
 
 static u32 d40_backup_regs_chan[] = {
        D40_CHAN_REG_SSCFG,
@@ -133,6 +171,102 @@ static u32 d40_backup_regs_chan[] = {
        D40_CHAN_REG_SDLNK,
 };
 
+/**
+ * struct d40_interrupt_lookup - lookup table for interrupt handler
+ *
+ * @src: Interrupt mask register.
+ * @clr: Interrupt clear register.
+ * @is_error: true if this is an error interrupt.
+ * @offset: start delta in the lookup_log_chans in d40_base. If equals to
+ * D40_PHY_CHAN, the lookup_phy_chans shall be used instead.
+ */
+struct d40_interrupt_lookup {
+       u32 src;
+       u32 clr;
+       bool is_error;
+       int offset;
+};
+
+
+static struct d40_interrupt_lookup il_v4a[] = {
+       {D40_DREG_LCTIS0, D40_DREG_LCICR0, false,  0},
+       {D40_DREG_LCTIS1, D40_DREG_LCICR1, false, 32},
+       {D40_DREG_LCTIS2, D40_DREG_LCICR2, false, 64},
+       {D40_DREG_LCTIS3, D40_DREG_LCICR3, false, 96},
+       {D40_DREG_LCEIS0, D40_DREG_LCICR0, true,   0},
+       {D40_DREG_LCEIS1, D40_DREG_LCICR1, true,  32},
+       {D40_DREG_LCEIS2, D40_DREG_LCICR2, true,  64},
+       {D40_DREG_LCEIS3, D40_DREG_LCICR3, true,  96},
+       {D40_DREG_PCTIS,  D40_DREG_PCICR,  false, D40_PHY_CHAN},
+       {D40_DREG_PCEIS,  D40_DREG_PCICR,  true,  D40_PHY_CHAN},
+};
+
+static struct d40_interrupt_lookup il_v4b[] = {
+       {D40_DREG_CLCTIS1, D40_DREG_CLCICR1, false,  0},
+       {D40_DREG_CLCTIS2, D40_DREG_CLCICR2, false, 32},
+       {D40_DREG_CLCTIS3, D40_DREG_CLCICR3, false, 64},
+       {D40_DREG_CLCTIS4, D40_DREG_CLCICR4, false, 96},
+       {D40_DREG_CLCTIS5, D40_DREG_CLCICR5, false, 128},
+       {D40_DREG_CLCEIS1, D40_DREG_CLCICR1, true,   0},
+       {D40_DREG_CLCEIS2, D40_DREG_CLCICR2, true,  32},
+       {D40_DREG_CLCEIS3, D40_DREG_CLCICR3, true,  64},
+       {D40_DREG_CLCEIS4, D40_DREG_CLCICR4, true,  96},
+       {D40_DREG_CLCEIS5, D40_DREG_CLCICR5, true,  128},
+       {D40_DREG_CPCTIS,  D40_DREG_CPCICR,  false, D40_PHY_CHAN},
+       {D40_DREG_CPCEIS,  D40_DREG_CPCICR,  true,  D40_PHY_CHAN},
+};
+
+/**
+ * struct d40_reg_val - simple lookup struct
+ *
+ * @reg: The register.
+ * @val: The value that belongs to the register in reg.
+ */
+struct d40_reg_val {
+       unsigned int reg;
+       unsigned int val;
+};
+
+static __initdata struct d40_reg_val dma_init_reg_v4a[] = {
+       /* Clock every part of the DMA block from start */
+       { .reg = D40_DREG_GCC,    .val = D40_DREG_GCC_ENABLE_ALL},
+
+       /* Interrupts on all logical channels */
+       { .reg = D40_DREG_LCMIS0, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_LCMIS1, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_LCMIS2, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_LCMIS3, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_LCICR0, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_LCICR1, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_LCICR2, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_LCICR3, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_LCTIS0, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_LCTIS1, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_LCTIS2, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_LCTIS3, .val = 0xFFFFFFFF}
+};
+static __initdata struct d40_reg_val dma_init_reg_v4b[] = {
+       /* Clock every part of the DMA block from start */
+       { .reg = D40_DREG_GCC,    .val = D40_DREG_GCC_ENABLE_ALL},
+
+       /* Interrupts on all logical channels */
+       { .reg = D40_DREG_CLCMIS1, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCMIS2, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCMIS3, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCMIS4, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCMIS5, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCICR1, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCICR2, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCICR3, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCICR4, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCICR5, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCTIS1, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCTIS2, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCTIS3, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCTIS4, .val = 0xFFFFFFFF},
+       { .reg = D40_DREG_CLCTIS5, .val = 0xFFFFFFFF}
+};
+
 /**
  * struct d40_lli_pool - Structure for keeping LLIs in memory
  *
@@ -221,6 +355,7 @@ struct d40_lcla_pool {
  * @allocated_dst: Same as for src but is dst.
  * allocated_dst and allocated_src uses the D40_ALLOC* defines as well as
  * event line number.
+ * @use_soft_lli: To mark if the linked lists of channel are managed by SW.
  */
 struct d40_phy_res {
        spinlock_t lock;
@@ -228,6 +363,7 @@ struct d40_phy_res {
        int        num;
        u32        allocated_src;
        u32        allocated_dst;
+       bool       use_soft_lli;
 };
 
 struct d40_base;
@@ -248,6 +384,7 @@ struct d40_base;
  * @client: Cliented owned descriptor list.
  * @pending_queue: Submitted jobs, to be issued by issue_pending()
  * @active: Active descriptor.
+ * @done: Completed jobs
  * @queue: Queued jobs.
  * @prepare_queue: Prepared jobs.
  * @dma_cfg: The client configuration of this dma channel.
@@ -273,6 +410,7 @@ struct d40_chan {
        struct list_head                 client;
        struct list_head                 pending_queue;
        struct list_head                 active;
+       struct list_head                 done;
        struct list_head                 queue;
        struct list_head                 prepare_queue;
        struct stedma40_chan_cfg         dma_cfg;
@@ -288,6 +426,38 @@ struct d40_chan {
        enum dma_transfer_direction     runtime_direction;
 };
 
+/**
+ * struct d40_gen_dmac - generic values to represent u8500/u8540 DMA
+ * controller
+ *
+ * @backup: the pointer to the registers address array for backup
+ * @backup_size: the size of the registers address array for backup
+ * @realtime_en: the realtime enable register
+ * @realtime_clear: the realtime clear register
+ * @high_prio_en: the high priority enable register
+ * @high_prio_clear: the high priority clear register
+ * @interrupt_en: the interrupt enable register
+ * @interrupt_clear: the interrupt clear register
+ * @il: the pointer to struct d40_interrupt_lookup
+ * @il_size: the size of d40_interrupt_lookup array
+ * @init_reg: the pointer to the struct d40_reg_val
+ * @init_reg_size: the size of d40_reg_val array
+ */
+struct d40_gen_dmac {
+       u32                             *backup;
+       u32                              backup_size;
+       u32                              realtime_en;
+       u32                              realtime_clear;
+       u32                              high_prio_en;
+       u32                              high_prio_clear;
+       u32                              interrupt_en;
+       u32                              interrupt_clear;
+       struct d40_interrupt_lookup     *il;
+       u32                              il_size;
+       struct d40_reg_val              *init_reg;
+       u32                              init_reg_size;
+};
+
 /**
  * struct d40_base - The big global struct, one for each probe'd instance.
  *
@@ -326,11 +496,13 @@ struct d40_chan {
  * @desc_slab: cache for descriptors.
  * @reg_val_backup: Here the values of some hardware registers are stored
  * before the DMA is powered off. They are restored when the power is back on.
- * @reg_val_backup_v3: Backup of registers that only exits on dma40 v3 and
- * later.
+ * @reg_val_backup_v4: Backup of registers that only exits on dma40 v3 and
+ * later
  * @reg_val_backup_chan: Backup data for standard channel parameter registers.
  * @gcc_pwr_off_mask: Mask to maintain the channels that can be turned off.
  * @initialized: true if the dma has been initialized
+ * @gen_dmac: the struct for generic registers values to represent u8500/8540
+ * DMA controller
  */
 struct d40_base {
        spinlock_t                       interrupt_lock;
@@ -344,6 +516,7 @@ struct d40_base {
        int                               irq;
        int                               num_phy_chans;
        int                               num_log_chans;
+       struct device_dma_parameters      dma_parms;
        struct dma_device                 dma_both;
        struct dma_device                 dma_slave;
        struct dma_device                 dma_memcpy;
@@ -361,37 +534,11 @@ struct d40_base {
        resource_size_t                   lcpa_size;
        struct kmem_cache                *desc_slab;
        u32                               reg_val_backup[BACKUP_REGS_SZ];
-       u32                               reg_val_backup_v3[BACKUP_REGS_SZ_V3];
+       u32                               reg_val_backup_v4[MAX(BACKUP_REGS_SZ_V4A, BACKUP_REGS_SZ_V4B)];
        u32                              *reg_val_backup_chan;
        u16                               gcc_pwr_off_mask;
        bool                              initialized;
-};
-
-/**
- * struct d40_interrupt_lookup - lookup table for interrupt handler
- *
- * @src: Interrupt mask register.
- * @clr: Interrupt clear register.
- * @is_error: true if this is an error interrupt.
- * @offset: start delta in the lookup_log_chans in d40_base. If equals to
- * D40_PHY_CHAN, the lookup_phy_chans shall be used instead.
- */
-struct d40_interrupt_lookup {
-       u32 src;
-       u32 clr;
-       bool is_error;
-       int offset;
-};
-
-/**
- * struct d40_reg_val - simple lookup struct
- *
- * @reg: The register.
- * @val: The value that belongs to the register in reg.
- */
-struct d40_reg_val {
-       unsigned int reg;
-       unsigned int val;
+       struct d40_gen_dmac               gen_dmac;
 };
 
 static struct device *chan2dev(struct d40_chan *d40c)
@@ -494,19 +641,18 @@ static int d40_lcla_alloc_one(struct d40_chan *d40c,
        unsigned long flags;
        int i;
        int ret = -EINVAL;
-       int p;
 
        spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags);
 
-       p = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP;
-
        /*
         * Allocate both src and dst at the same time, therefore the half
         * start on 1 since 0 can't be used since zero is used as end marker.
         */
        for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) {
-               if (!d40c->base->lcla_pool.alloc_map[p + i]) {
-                       d40c->base->lcla_pool.alloc_map[p + i] = d40d;
+               int idx = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP + i;
+
+               if (!d40c->base->lcla_pool.alloc_map[idx]) {
+                       d40c->base->lcla_pool.alloc_map[idx] = d40d;
                        d40d->lcla_alloc++;
                        ret = i;
                        break;
@@ -531,10 +677,10 @@ static int d40_lcla_free_all(struct d40_chan *d40c,
        spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags);
 
        for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) {
-               if (d40c->base->lcla_pool.alloc_map[d40c->phy_chan->num *
-                                                   D40_LCLA_LINK_PER_EVENT_GRP + i] == d40d) {
-                       d40c->base->lcla_pool.alloc_map[d40c->phy_chan->num *
-                                                       D40_LCLA_LINK_PER_EVENT_GRP + i] = NULL;
+               int idx = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP + i;
+
+               if (d40c->base->lcla_pool.alloc_map[idx] == d40d) {
+                       d40c->base->lcla_pool.alloc_map[idx] = NULL;
                        d40d->lcla_alloc--;
                        if (d40d->lcla_alloc == 0) {
                                ret = 0;
@@ -611,6 +757,11 @@ static void d40_phy_lli_load(struct d40_chan *chan, struct d40_desc *desc)
        writel(lli_dst->reg_lnk, base + D40_CHAN_REG_SDLNK);
 }
 
+static void d40_desc_done(struct d40_chan *d40c, struct d40_desc *desc)
+{
+       list_add_tail(&desc->node, &d40c->done);
+}
+
 static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc)
 {
        struct d40_lcla_pool *pool = &chan->base->lcla_pool;
@@ -634,7 +785,16 @@ static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc)
         * can't link back to the one in LCPA space
         */
        if (linkback || (lli_len - lli_current > 1)) {
-               curr_lcla = d40_lcla_alloc_one(chan, desc);
+               /*
+                * If the channel is expected to use only soft_lli don't
+                * allocate a lcla. This is to avoid a HW issue that exists
+                * in some controller during a peripheral to memory transfer
+                * that uses linked lists.
+                */
+               if (!(chan->phy_chan->use_soft_lli &&
+                       chan->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM))
+                       curr_lcla = d40_lcla_alloc_one(chan, desc);
+
                first_lcla = curr_lcla;
        }
 
@@ -771,6 +931,14 @@ static struct d40_desc *d40_first_queued(struct d40_chan *d40c)
        return d;
 }
 
+static struct d40_desc *d40_first_done(struct d40_chan *d40c)
+{
+       if (list_empty(&d40c->done))
+               return NULL;
+
+       return list_first_entry(&d40c->done, struct d40_desc, node);
+}
+
 static int d40_psize_2_burst_size(bool is_log, int psize)
 {
        if (is_log) {
@@ -874,11 +1042,11 @@ static void d40_save_restore_registers(struct d40_base *base, bool save)
                     save);
 
        /* Save/Restore registers only existing on dma40 v3 and later */
-       if (base->rev >= 3)
-               dma40_backup(base->virtbase, base->reg_val_backup_v3,
-                            d40_backup_regs_v3,
-                            ARRAY_SIZE(d40_backup_regs_v3),
-                            save);
+       if (base->gen_dmac.backup)
+               dma40_backup(base->virtbase, base->reg_val_backup_v4,
+                            base->gen_dmac.backup,
+                       base->gen_dmac.backup_size,
+                       save);
 }
 #else
 static void d40_save_restore_registers(struct d40_base *base, bool save)
@@ -961,6 +1129,12 @@ static void d40_term_all(struct d40_chan *d40c)
        struct d40_desc *d40d;
        struct d40_desc *_d;
 
+       /* Release completed descriptors */
+       while ((d40d = d40_first_done(d40c))) {
+               d40_desc_remove(d40d);
+               d40_desc_free(d40c, d40d);
+       }
+
        /* Release active descriptors */
        while ((d40d = d40_first_active_get(d40c))) {
                d40_desc_remove(d40d);
@@ -1398,6 +1572,9 @@ static void dma_tc_handle(struct d40_chan *d40c)
                pm_runtime_put_autosuspend(d40c->base->dev);
        }
 
+       d40_desc_remove(d40d);
+       d40_desc_done(d40c, d40d);
+
        d40c->pending_tx++;
        tasklet_schedule(&d40c->tasklet);
 
@@ -1413,10 +1590,14 @@ static void dma_tasklet(unsigned long data)
 
        spin_lock_irqsave(&d40c->lock, flags);
 
-       /* Get first active entry from list */
-       d40d = d40_first_active_get(d40c);
-       if (d40d == NULL)
-               goto err;
+       /* Get first entry from the done list */
+       d40d = d40_first_done(d40c);
+       if (d40d == NULL) {
+               /* Check if we have reached here for cyclic job */
+               d40d = d40_first_active_get(d40c);
+               if (d40d == NULL || !d40d->cyclic)
+                       goto err;
+       }
 
        if (!d40d->cyclic)
                dma_cookie_complete(&d40d->txd);
@@ -1438,13 +1619,11 @@ static void dma_tasklet(unsigned long data)
                if (async_tx_test_ack(&d40d->txd)) {
                        d40_desc_remove(d40d);
                        d40_desc_free(d40c, d40d);
-               } else {
-                       if (!d40d->is_in_client_list) {
-                               d40_desc_remove(d40d);
-                               d40_lcla_free_all(d40c, d40d);
-                               list_add_tail(&d40d->node, &d40c->client);
-                               d40d->is_in_client_list = true;
-                       }
+               } else if (!d40d->is_in_client_list) {
+                       d40_desc_remove(d40d);
+                       d40_lcla_free_all(d40c, d40d);
+                       list_add_tail(&d40d->node, &d40c->client);
+                       d40d->is_in_client_list = true;
                }
        }
 
@@ -1469,53 +1648,51 @@ err:
 
 static irqreturn_t d40_handle_interrupt(int irq, void *data)
 {
-       static const struct d40_interrupt_lookup il[] = {
-               {D40_DREG_LCTIS0, D40_DREG_LCICR0, false,  0},
-               {D40_DREG_LCTIS1, D40_DREG_LCICR1, false, 32},
-               {D40_DREG_LCTIS2, D40_DREG_LCICR2, false, 64},
-               {D40_DREG_LCTIS3, D40_DREG_LCICR3, false, 96},
-               {D40_DREG_LCEIS0, D40_DREG_LCICR0, true,   0},
-               {D40_DREG_LCEIS1, D40_DREG_LCICR1, true,  32},
-               {D40_DREG_LCEIS2, D40_DREG_LCICR2, true,  64},
-               {D40_DREG_LCEIS3, D40_DREG_LCICR3, true,  96},
-               {D40_DREG_PCTIS,  D40_DREG_PCICR,  false, D40_PHY_CHAN},
-               {D40_DREG_PCEIS,  D40_DREG_PCICR,  true,  D40_PHY_CHAN},
-       };
-
        int i;
-       u32 regs[ARRAY_SIZE(il)];
        u32 idx;
        u32 row;
        long chan = -1;
        struct d40_chan *d40c;
        unsigned long flags;
        struct d40_base *base = data;
+       u32 regs[base->gen_dmac.il_size];
+       struct d40_interrupt_lookup *il = base->gen_dmac.il;
+       u32 il_size = base->gen_dmac.il_size;
 
        spin_lock_irqsave(&base->interrupt_lock, flags);
 
        /* Read interrupt status of both logical and physical channels */
-       for (i = 0; i < ARRAY_SIZE(il); i++)
+       for (i = 0; i < il_size; i++)
                regs[i] = readl(base->virtbase + il[i].src);
 
        for (;;) {
 
                chan = find_next_bit((unsigned long *)regs,
-                                    BITS_PER_LONG * ARRAY_SIZE(il), chan + 1);
+                                    BITS_PER_LONG * il_size, chan + 1);
 
                /* No more set bits found? */
-               if (chan == BITS_PER_LONG * ARRAY_SIZE(il))
+               if (chan == BITS_PER_LONG * il_size)
                        break;
 
                row = chan / BITS_PER_LONG;
                idx = chan & (BITS_PER_LONG - 1);
 
-               /* ACK interrupt */
-               writel(1 << idx, base->virtbase + il[row].clr);
-
                if (il[row].offset == D40_PHY_CHAN)
                        d40c = base->lookup_phy_chans[idx];
                else
                        d40c = base->lookup_log_chans[il[row].offset + idx];
+
+               if (!d40c) {
+                       /*
+                        * No error because this can happen if something else
+                        * in the system is using the channel.
+                        */
+                       continue;
+               }
+
+               /* ACK interrupt */
+               writel(1 << idx, base->virtbase + il[row].clr);
+
                spin_lock(&d40c->lock);
 
                if (!il[row].is_error)
@@ -1710,10 +1887,12 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user)
        int i;
        int j;
        int log_num;
+       int num_phy_chans;
        bool is_src;
        bool is_log = d40c->dma_cfg.mode == STEDMA40_MODE_LOGICAL;
 
        phys = d40c->base->phy_res;
+       num_phy_chans = d40c->base->num_phy_chans;
 
        if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) {
                dev_type = d40c->dma_cfg.src_dev_type;
@@ -1734,12 +1913,19 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user)
        if (!is_log) {
                if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) {
                        /* Find physical half channel */
-                       for (i = 0; i < d40c->base->num_phy_chans; i++) {
-
+                       if (d40c->dma_cfg.use_fixed_channel) {
+                               i = d40c->dma_cfg.phy_channel;
                                if (d40_alloc_mask_set(&phys[i], is_src,
                                                       0, is_log,
                                                       first_phy_user))
                                        goto found_phy;
+                       } else {
+                               for (i = 0; i < num_phy_chans; i++) {
+                                       if (d40_alloc_mask_set(&phys[i], is_src,
+                                                      0, is_log,
+                                                      first_phy_user))
+                                               goto found_phy;
+                               }
                        }
                } else
                        for (j = 0; j < d40c->base->num_phy_chans; j += 8) {
@@ -1954,7 +2140,6 @@ _exit:
 
 }
 
-
 static u32 stedma40_residue(struct dma_chan *chan)
 {
        struct d40_chan *d40c =
@@ -2030,7 +2215,6 @@ d40_prep_sg_phy(struct d40_chan *chan, struct d40_desc *desc,
        return ret < 0 ? ret : 0;
 }
 
-
 static struct d40_desc *
 d40_prep_desc(struct d40_chan *chan, struct scatterlist *sg,
              unsigned int sg_len, unsigned long dma_flags)
@@ -2056,7 +2240,6 @@ d40_prep_desc(struct d40_chan *chan, struct scatterlist *sg,
                goto err;
        }
 
-
        desc->lli_current = 0;
        desc->txd.flags = dma_flags;
        desc->txd.tx_submit = d40_tx_submit;
@@ -2105,7 +2288,6 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src,
                return NULL;
        }
 
-
        spin_lock_irqsave(&chan->lock, flags);
 
        desc = d40_prep_desc(chan, sg_src, sg_len, dma_flags);
@@ -2179,11 +2361,26 @@ static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src)
 {
        bool realtime = d40c->dma_cfg.realtime;
        bool highprio = d40c->dma_cfg.high_priority;
-       u32 prioreg = highprio ? D40_DREG_PSEG1 : D40_DREG_PCEG1;
-       u32 rtreg = realtime ? D40_DREG_RSEG1 : D40_DREG_RCEG1;
+       u32 rtreg;
        u32 event = D40_TYPE_TO_EVENT(dev_type);
        u32 group = D40_TYPE_TO_GROUP(dev_type);
        u32 bit = 1 << event;
+       u32 prioreg;
+       struct d40_gen_dmac *dmac = &d40c->base->gen_dmac;
+
+       rtreg = realtime ? dmac->realtime_en : dmac->realtime_clear;
+       /*
+        * Due to a hardware bug, in some cases a logical channel triggered by
+        * a high priority destination event line can generate extra packet
+        * transactions.
+        *
+        * The workaround is to not set the high priority level for the
+        * destination event lines that trigger logical channels.
+        */
+       if (!src && chan_is_logical(d40c))
+               highprio = false;
+
+       prioreg = highprio ? dmac->high_prio_en : dmac->high_prio_clear;
 
        /* Destination event lines are stored in the upper halfword */
        if (!src)
@@ -2248,11 +2445,11 @@ static int d40_alloc_chan_resources(struct dma_chan *chan)
 
                if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM)
                        d40c->lcpa = d40c->base->lcpa_base +
-                         d40c->dma_cfg.src_dev_type * D40_LCPA_CHAN_SIZE;
+                               d40c->dma_cfg.src_dev_type * D40_LCPA_CHAN_SIZE;
                else
                        d40c->lcpa = d40c->base->lcpa_base +
-                         d40c->dma_cfg.dst_dev_type *
-                         D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA;
+                               d40c->dma_cfg.dst_dev_type *
+                               D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA;
        }
 
        dev_dbg(chan2dev(d40c), "allocated %s channel (phy %d%s)\n",
@@ -2287,7 +2484,6 @@ static void d40_free_chan_resources(struct dma_chan *chan)
                return;
        }
 
-
        spin_lock_irqsave(&d40c->lock, flags);
 
        err = d40_free_dma(d40c);
@@ -2330,14 +2526,12 @@ d40_prep_memcpy_sg(struct dma_chan *chan,
        return d40_prep_sg(chan, src_sg, dst_sg, src_nents, DMA_NONE, dma_flags);
 }
 
-static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan,
-                                                        struct scatterlist *sgl,
-                                                        unsigned int sg_len,
-                                                        enum dma_transfer_direction direction,
-                                                        unsigned long dma_flags,
-                                                        void *context)
+static struct dma_async_tx_descriptor *
+d40_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+                 unsigned int sg_len, enum dma_transfer_direction direction,
+                 unsigned long dma_flags, void *context)
 {
-       if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV)
+       if (!is_slave_direction(direction))
                return NULL;
 
        return d40_prep_sg(chan, sgl, sgl, sg_len, direction, dma_flags);
@@ -2577,6 +2771,14 @@ static int d40_set_runtime_config(struct dma_chan *chan,
                return -EINVAL;
        }
 
+       if (src_maxburst > 16) {
+               src_maxburst = 16;
+               dst_maxburst = src_maxburst * src_addr_width / dst_addr_width;
+       } else if (dst_maxburst > 16) {
+               dst_maxburst = 16;
+               src_maxburst = dst_maxburst * dst_addr_width / src_addr_width;
+       }
+
        ret = dma40_config_to_halfchannel(d40c, &cfg->src_info,
                                          src_addr_width,
                                          src_maxburst);
@@ -2659,6 +2861,7 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma,
 
                d40c->log_num = D40_PHY_CHAN;
 
+               INIT_LIST_HEAD(&d40c->done);
                INIT_LIST_HEAD(&d40c->active);
                INIT_LIST_HEAD(&d40c->queue);
                INIT_LIST_HEAD(&d40c->pending_queue);
@@ -2773,8 +2976,6 @@ static int dma40_pm_suspend(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct d40_base *base = platform_get_drvdata(pdev);
        int ret = 0;
-       if (!pm_runtime_suspended(dev))
-               return -EBUSY;
 
        if (base->lcpa_regulator)
                ret = regulator_disable(base->lcpa_regulator);
@@ -2882,6 +3083,13 @@ static int __init d40_phy_res_init(struct d40_base *base)
                num_phy_chans_avail--;
        }
 
+       /* Mark soft_lli channels */
+       for (i = 0; i < base->plat_data->num_of_soft_lli_chans; i++) {
+               int chan = base->plat_data->soft_lli_chans[i];
+
+               base->phy_res[chan].use_soft_lli = true;
+       }
+
        dev_info(base->dev, "%d of %d physical DMA channels available\n",
                 num_phy_chans_avail, base->num_phy_chans);
 
@@ -2975,14 +3183,21 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
         * ? has revision 1
         * DB8500v1 has revision 2
         * DB8500v2 has revision 3
+        * AP9540v1 has revision 4
+        * DB8540v1 has revision 4
         */
        rev = AMBA_REV_BITS(pid);
 
+       plat_data = pdev->dev.platform_data;
+
        /* The number of physical channels on this HW */
-       num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4;
+       if (plat_data->num_of_phy_chans)
+               num_phy_chans = plat_data->num_of_phy_chans;
+       else
+               num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4;
 
-       dev_info(&pdev->dev, "hardware revision: %d @ 0x%x\n",
-                rev, res->start);
+       dev_info(&pdev->dev, "hardware revision: %d @ 0x%x with %d physical channels\n",
+                rev, res->start, num_phy_chans);
 
        if (rev < 2) {
                d40_err(&pdev->dev, "hardware revision: %d is not supported",
@@ -2990,8 +3205,6 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
                goto failure;
        }
 
-       plat_data = pdev->dev.platform_data;
-
        /* Count the number of logical channels in use */
        for (i = 0; i < plat_data->dev_len; i++)
                if (plat_data->dev_rx[i] != 0)
@@ -3022,6 +3235,36 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
        base->phy_chans = ((void *)base) + ALIGN(sizeof(struct d40_base), 4);
        base->log_chans = &base->phy_chans[num_phy_chans];
 
+       if (base->plat_data->num_of_phy_chans == 14) {
+               base->gen_dmac.backup = d40_backup_regs_v4b;
+               base->gen_dmac.backup_size = BACKUP_REGS_SZ_V4B;
+               base->gen_dmac.interrupt_en = D40_DREG_CPCMIS;
+               base->gen_dmac.interrupt_clear = D40_DREG_CPCICR;
+               base->gen_dmac.realtime_en = D40_DREG_CRSEG1;
+               base->gen_dmac.realtime_clear = D40_DREG_CRCEG1;
+               base->gen_dmac.high_prio_en = D40_DREG_CPSEG1;
+               base->gen_dmac.high_prio_clear = D40_DREG_CPCEG1;
+               base->gen_dmac.il = il_v4b;
+               base->gen_dmac.il_size = ARRAY_SIZE(il_v4b);
+               base->gen_dmac.init_reg = dma_init_reg_v4b;
+               base->gen_dmac.init_reg_size = ARRAY_SIZE(dma_init_reg_v4b);
+       } else {
+               if (base->rev >= 3) {
+                       base->gen_dmac.backup = d40_backup_regs_v4a;
+                       base->gen_dmac.backup_size = BACKUP_REGS_SZ_V4A;
+               }
+               base->gen_dmac.interrupt_en = D40_DREG_PCMIS;
+               base->gen_dmac.interrupt_clear = D40_DREG_PCICR;
+               base->gen_dmac.realtime_en = D40_DREG_RSEG1;
+               base->gen_dmac.realtime_clear = D40_DREG_RCEG1;
+               base->gen_dmac.high_prio_en = D40_DREG_PSEG1;
+               base->gen_dmac.high_prio_clear = D40_DREG_PCEG1;
+               base->gen_dmac.il = il_v4a;
+               base->gen_dmac.il_size = ARRAY_SIZE(il_v4a);
+               base->gen_dmac.init_reg = dma_init_reg_v4a;
+               base->gen_dmac.init_reg_size = ARRAY_SIZE(dma_init_reg_v4a);
+       }
+
        base->phy_res = kzalloc(num_phy_chans * sizeof(struct d40_phy_res),
                                GFP_KERNEL);
        if (!base->phy_res)
@@ -3093,31 +3336,15 @@ failure:
 static void __init d40_hw_init(struct d40_base *base)
 {
 
-       static struct d40_reg_val dma_init_reg[] = {
-               /* Clock every part of the DMA block from start */
-               { .reg = D40_DREG_GCC,    .val = D40_DREG_GCC_ENABLE_ALL},
-
-               /* Interrupts on all logical channels */
-               { .reg = D40_DREG_LCMIS0, .val = 0xFFFFFFFF},
-               { .reg = D40_DREG_LCMIS1, .val = 0xFFFFFFFF},
-               { .reg = D40_DREG_LCMIS2, .val = 0xFFFFFFFF},
-               { .reg = D40_DREG_LCMIS3, .val = 0xFFFFFFFF},
-               { .reg = D40_DREG_LCICR0, .val = 0xFFFFFFFF},
-               { .reg = D40_DREG_LCICR1, .val = 0xFFFFFFFF},
-               { .reg = D40_DREG_LCICR2, .val = 0xFFFFFFFF},
-               { .reg = D40_DREG_LCICR3, .val = 0xFFFFFFFF},
-               { .reg = D40_DREG_LCTIS0, .val = 0xFFFFFFFF},
-               { .reg = D40_DREG_LCTIS1, .val = 0xFFFFFFFF},
-               { .reg = D40_DREG_LCTIS2, .val = 0xFFFFFFFF},
-               { .reg = D40_DREG_LCTIS3, .val = 0xFFFFFFFF}
-       };
        int i;
        u32 prmseo[2] = {0, 0};
        u32 activeo[2] = {0xFFFFFFFF, 0xFFFFFFFF};
        u32 pcmis = 0;
        u32 pcicr = 0;
+       struct d40_reg_val *dma_init_reg = base->gen_dmac.init_reg;
+       u32 reg_size = base->gen_dmac.init_reg_size;
 
-       for (i = 0; i < ARRAY_SIZE(dma_init_reg); i++)
+       for (i = 0; i < reg_size; i++)
                writel(dma_init_reg[i].val,
                       base->virtbase + dma_init_reg[i].reg);
 
@@ -3150,11 +3377,14 @@ static void __init d40_hw_init(struct d40_base *base)
        writel(activeo[0], base->virtbase + D40_DREG_ACTIVO);
 
        /* Write which interrupt to enable */
-       writel(pcmis, base->virtbase + D40_DREG_PCMIS);
+       writel(pcmis, base->virtbase + base->gen_dmac.interrupt_en);
 
        /* Write which interrupt to clear */
-       writel(pcicr, base->virtbase + D40_DREG_PCICR);
+       writel(pcicr, base->virtbase + base->gen_dmac.interrupt_clear);
 
+       /* These are __initdata and cannot be accessed after init */
+       base->gen_dmac.init_reg = NULL;
+       base->gen_dmac.init_reg_size = 0;
 }
 
 static int __init d40_lcla_allocate(struct d40_base *base)
@@ -3362,6 +3592,13 @@ static int __init d40_probe(struct platform_device *pdev)
        if (err)
                goto failure;
 
+       base->dev->dma_parms = &base->dma_parms;
+       err = dma_set_max_seg_size(base->dev, STEDMA40_MAX_SEG_SIZE);
+       if (err) {
+               d40_err(&pdev->dev, "Failed to set dma max seg size\n");
+               goto failure;
+       }
+
        d40_hw_init(base);
 
        dev_info(base->dev, "initialized\n");
@@ -3397,7 +3634,7 @@ failure:
                        release_mem_region(base->phy_start,
                                           base->phy_size);
                if (base->clk) {
-                       clk_disable(base->clk);
+                       clk_disable_unprepare(base->clk);
                        clk_put(base->clk);
                }
 
index 851ad56e84097b7e21e73ce5bf1a10f42d05f7bb..7180e0d417228e2a07f5b9ab2fda02e5379e0979 100644 (file)
@@ -102,17 +102,18 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
                src |= cfg->src_info.data_width << D40_SREG_CFG_ESIZE_POS;
                dst |= cfg->dst_info.data_width << D40_SREG_CFG_ESIZE_POS;
 
+               /* Set the priority bit to high for the physical channel */
+               if (cfg->high_priority) {
+                       src |= 1 << D40_SREG_CFG_PRI_POS;
+                       dst |= 1 << D40_SREG_CFG_PRI_POS;
+               }
+
        } else {
                /* Logical channel */
                dst |= 1 << D40_SREG_CFG_LOG_GIM_POS;
                src |= 1 << D40_SREG_CFG_LOG_GIM_POS;
        }
 
-       if (cfg->high_priority) {
-               src |= 1 << D40_SREG_CFG_PRI_POS;
-               dst |= 1 << D40_SREG_CFG_PRI_POS;
-       }
-
        if (cfg->src_info.big_endian)
                src |= 1 << D40_SREG_CFG_LBE_POS;
        if (cfg->dst_info.big_endian)
@@ -250,7 +251,7 @@ d40_phy_buf_to_lli(struct d40_phy_lli *lli, dma_addr_t addr, u32 size,
 
        return lli;
 
- err:
+err:
        return NULL;
 }
 
@@ -331,10 +332,10 @@ void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa,
 {
        d40_log_lli_link(lli_dst, lli_src, next, flags);
 
-       writel(lli_src->lcsp02, &lcpa[0].lcsp0);
-       writel(lli_src->lcsp13, &lcpa[0].lcsp1);
-       writel(lli_dst->lcsp02, &lcpa[0].lcsp2);
-       writel(lli_dst->lcsp13, &lcpa[0].lcsp3);
+       writel_relaxed(lli_src->lcsp02, &lcpa[0].lcsp0);
+       writel_relaxed(lli_src->lcsp13, &lcpa[0].lcsp1);
+       writel_relaxed(lli_dst->lcsp02, &lcpa[0].lcsp2);
+       writel_relaxed(lli_dst->lcsp13, &lcpa[0].lcsp3);
 }
 
 void d40_log_lli_lcla_write(struct d40_log_lli *lcla,
@@ -344,10 +345,10 @@ void d40_log_lli_lcla_write(struct d40_log_lli *lcla,
 {
        d40_log_lli_link(lli_dst, lli_src, next, flags);
 
-       writel(lli_src->lcsp02, &lcla[0].lcsp02);
-       writel(lli_src->lcsp13, &lcla[0].lcsp13);
-       writel(lli_dst->lcsp02, &lcla[1].lcsp02);
-       writel(lli_dst->lcsp13, &lcla[1].lcsp13);
+       writel_relaxed(lli_src->lcsp02, &lcla[0].lcsp02);
+       writel_relaxed(lli_src->lcsp13, &lcla[0].lcsp13);
+       writel_relaxed(lli_dst->lcsp02, &lcla[1].lcsp02);
+       writel_relaxed(lli_dst->lcsp13, &lcla[1].lcsp13);
 }
 
 static void d40_log_fill_lli(struct d40_log_lli *lli,
index 6d47373f3f5893c86fad20976285138faaaf3758..fdde8ef775422fff777eff169a8b39733b72af43 100644 (file)
 #define D40_DREG_GCC           0x000
 #define D40_DREG_GCC_ENA       0x1
 /* This assumes that there are only 4 event groups */
-#define D40_DREG_GCC_ENABLE_ALL        0xff01
+#define D40_DREG_GCC_ENABLE_ALL        0x3ff01
 #define D40_DREG_GCC_EVTGRP_POS 8
 #define D40_DREG_GCC_SRC 0
 #define D40_DREG_GCC_DST 1
 
 #define D40_DREG_LCPA          0x020
 #define D40_DREG_LCLA          0x024
+
+#define D40_DREG_SSEG1         0x030
+#define D40_DREG_SSEG2         0x034
+#define D40_DREG_SSEG3         0x038
+#define D40_DREG_SSEG4         0x03C
+
+#define D40_DREG_SCEG1         0x040
+#define D40_DREG_SCEG2         0x044
+#define D40_DREG_SCEG3         0x048
+#define D40_DREG_SCEG4         0x04C
+
 #define D40_DREG_ACTIVE                0x050
 #define D40_DREG_ACTIVO                0x054
-#define D40_DREG_FSEB1         0x058
-#define D40_DREG_FSEB2         0x05C
+#define D40_DREG_CIDMOD                0x058
+#define D40_DREG_TCIDV         0x05C
 #define D40_DREG_PCMIS         0x060
 #define D40_DREG_PCICR         0x064
 #define D40_DREG_PCTIS         0x068
 #define D40_DREG_PCEIS         0x06C
+
+#define D40_DREG_SPCMIS                0x070
+#define D40_DREG_SPCICR                0x074
+#define D40_DREG_SPCTIS                0x078
+#define D40_DREG_SPCEIS                0x07C
+
 #define D40_DREG_LCMIS0                0x080
 #define D40_DREG_LCMIS1                0x084
 #define D40_DREG_LCMIS2                0x088
 #define D40_DREG_LCEIS1                0x0B4
 #define D40_DREG_LCEIS2                0x0B8
 #define D40_DREG_LCEIS3                0x0BC
+
+#define D40_DREG_SLCMIS1       0x0C0
+#define D40_DREG_SLCMIS2       0x0C4
+#define D40_DREG_SLCMIS3       0x0C8
+#define D40_DREG_SLCMIS4       0x0CC
+
+#define D40_DREG_SLCICR1       0x0D0
+#define D40_DREG_SLCICR2       0x0D4
+#define D40_DREG_SLCICR3       0x0D8
+#define D40_DREG_SLCICR4       0x0DC
+
+#define D40_DREG_SLCTIS1       0x0E0
+#define D40_DREG_SLCTIS2       0x0E4
+#define D40_DREG_SLCTIS3       0x0E8
+#define D40_DREG_SLCTIS4       0x0EC
+
+#define D40_DREG_SLCEIS1       0x0F0
+#define D40_DREG_SLCEIS2       0x0F4
+#define D40_DREG_SLCEIS3       0x0F8
+#define D40_DREG_SLCEIS4       0x0FC
+
+#define D40_DREG_FSESS1                0x100
+#define D40_DREG_FSESS2                0x104
+
+#define D40_DREG_FSEBS1                0x108
+#define D40_DREG_FSEBS2                0x10C
+
 #define D40_DREG_PSEG1         0x110
 #define D40_DREG_PSEG2         0x114
 #define D40_DREG_PSEG3         0x118
 #define D40_DREG_RCEG2         0x144
 #define D40_DREG_RCEG3         0x148
 #define D40_DREG_RCEG4         0x14C
+
+#define D40_DREG_PREFOT                0x15C
+#define D40_DREG_EXTCFG                0x160
+
+#define D40_DREG_CPSEG1                0x200
+#define D40_DREG_CPSEG2                0x204
+#define D40_DREG_CPSEG3                0x208
+#define D40_DREG_CPSEG4                0x20C
+#define D40_DREG_CPSEG5                0x210
+
+#define D40_DREG_CPCEG1                0x220
+#define D40_DREG_CPCEG2                0x224
+#define D40_DREG_CPCEG3                0x228
+#define D40_DREG_CPCEG4                0x22C
+#define D40_DREG_CPCEG5                0x230
+
+#define D40_DREG_CRSEG1                0x240
+#define D40_DREG_CRSEG2                0x244
+#define D40_DREG_CRSEG3                0x248
+#define D40_DREG_CRSEG4                0x24C
+#define D40_DREG_CRSEG5                0x250
+
+#define D40_DREG_CRCEG1                0x260
+#define D40_DREG_CRCEG2                0x264
+#define D40_DREG_CRCEG3                0x268
+#define D40_DREG_CRCEG4                0x26C
+#define D40_DREG_CRCEG5                0x270
+
+#define D40_DREG_CFSESS1       0x280
+#define D40_DREG_CFSESS2       0x284
+#define D40_DREG_CFSESS3       0x288
+
+#define D40_DREG_CFSEBS1       0x290
+#define D40_DREG_CFSEBS2       0x294
+#define D40_DREG_CFSEBS3       0x298
+
+#define D40_DREG_CLCMIS1       0x300
+#define D40_DREG_CLCMIS2       0x304
+#define D40_DREG_CLCMIS3       0x308
+#define D40_DREG_CLCMIS4       0x30C
+#define D40_DREG_CLCMIS5       0x310
+
+#define D40_DREG_CLCICR1       0x320
+#define D40_DREG_CLCICR2       0x324
+#define D40_DREG_CLCICR3       0x328
+#define D40_DREG_CLCICR4       0x32C
+#define D40_DREG_CLCICR5       0x330
+
+#define D40_DREG_CLCTIS1       0x340
+#define D40_DREG_CLCTIS2       0x344
+#define D40_DREG_CLCTIS3       0x348
+#define D40_DREG_CLCTIS4       0x34C
+#define D40_DREG_CLCTIS5       0x350
+
+#define D40_DREG_CLCEIS1       0x360
+#define D40_DREG_CLCEIS2       0x364
+#define D40_DREG_CLCEIS3       0x368
+#define D40_DREG_CLCEIS4       0x36C
+#define D40_DREG_CLCEIS5       0x370
+
+#define D40_DREG_CPCMIS                0x380
+#define D40_DREG_CPCICR                0x384
+#define D40_DREG_CPCTIS                0x388
+#define D40_DREG_CPCEIS                0x38C
+
+#define D40_DREG_SCCIDA1       0xE80
+#define D40_DREG_SCCIDA2       0xE90
+#define D40_DREG_SCCIDA3       0xEA0
+#define D40_DREG_SCCIDA4       0xEB0
+#define D40_DREG_SCCIDA5       0xEC0
+
+#define D40_DREG_SCCIDB1       0xE84
+#define D40_DREG_SCCIDB2       0xE94
+#define D40_DREG_SCCIDB3       0xEA4
+#define D40_DREG_SCCIDB4       0xEB4
+#define D40_DREG_SCCIDB5       0xEC4
+
+#define D40_DREG_PRSCCIDA      0xF80
+#define D40_DREG_PRSCCIDB      0xF84
+
 #define D40_DREG_STFU          0xFC8
 #define D40_DREG_ICFG          0xFCC
 #define D40_DREG_PERIPHID0     0xFE0
index 3cad856fe67f9f9518cbf79b0f02bd41c32733b8..136b3fca8785d2e0d4c17b24365dc0848bb069ef 100644 (file)
@@ -62,6 +62,9 @@
 #define TEGRA_APBDMA_STATUS_COUNT_SHIFT                2
 #define TEGRA_APBDMA_STATUS_COUNT_MASK         0xFFFC
 
+#define TEGRA_APBDMA_CHAN_CSRE                 0x00C
+#define TEGRA_APBDMA_CHAN_CSRE_PAUSE           (1 << 31)
+
 /* AHB memory address */
 #define TEGRA_APBDMA_CHAN_AHBPTR               0x010
 
@@ -112,10 +115,12 @@ struct tegra_dma;
  * tegra_dma_chip_data Tegra chip specific DMA data
  * @nr_channels: Number of channels available in the controller.
  * @max_dma_count: Maximum DMA transfer count supported by DMA controller.
+ * @support_channel_pause: Support channel wise pause of dma.
  */
 struct tegra_dma_chip_data {
        int nr_channels;
        int max_dma_count;
+       bool support_channel_pause;
 };
 
 /* DMA channel registers */
@@ -354,6 +359,32 @@ static void tegra_dma_global_resume(struct tegra_dma_channel *tdc)
        spin_unlock(&tdma->global_lock);
 }
 
+static void tegra_dma_pause(struct tegra_dma_channel *tdc,
+       bool wait_for_burst_complete)
+{
+       struct tegra_dma *tdma = tdc->tdma;
+
+       if (tdma->chip_data->support_channel_pause) {
+               tdc_write(tdc, TEGRA_APBDMA_CHAN_CSRE,
+                               TEGRA_APBDMA_CHAN_CSRE_PAUSE);
+               if (wait_for_burst_complete)
+                       udelay(TEGRA_APBDMA_BURST_COMPLETE_TIME);
+       } else {
+               tegra_dma_global_pause(tdc, wait_for_burst_complete);
+       }
+}
+
+static void tegra_dma_resume(struct tegra_dma_channel *tdc)
+{
+       struct tegra_dma *tdma = tdc->tdma;
+
+       if (tdma->chip_data->support_channel_pause) {
+               tdc_write(tdc, TEGRA_APBDMA_CHAN_CSRE, 0);
+       } else {
+               tegra_dma_global_resume(tdc);
+       }
+}
+
 static void tegra_dma_stop(struct tegra_dma_channel *tdc)
 {
        u32 csr;
@@ -409,7 +440,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc,
         * If there is already IEC status then interrupt handler need to
         * load new configuration.
         */
-       tegra_dma_global_pause(tdc, false);
+       tegra_dma_pause(tdc, false);
        status  = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS);
 
        /*
@@ -419,7 +450,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc,
        if (status & TEGRA_APBDMA_STATUS_ISE_EOC) {
                dev_err(tdc2dev(tdc),
                        "Skipping new configuration as interrupt is pending\n");
-               tegra_dma_global_resume(tdc);
+               tegra_dma_resume(tdc);
                return;
        }
 
@@ -430,7 +461,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc,
                                nsg_req->ch_regs.csr | TEGRA_APBDMA_CSR_ENB);
        nsg_req->configured = true;
 
-       tegra_dma_global_resume(tdc);
+       tegra_dma_resume(tdc);
 }
 
 static void tdc_start_head_req(struct tegra_dma_channel *tdc)
@@ -691,7 +722,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc)
                goto skip_dma_stop;
 
        /* Pause DMA before checking the queue status */
-       tegra_dma_global_pause(tdc, true);
+       tegra_dma_pause(tdc, true);
 
        status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS);
        if (status & TEGRA_APBDMA_STATUS_ISE_EOC) {
@@ -709,7 +740,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc)
                sgreq->dma_desc->bytes_transferred +=
                                get_current_xferred_count(tdc, sgreq, status);
        }
-       tegra_dma_global_resume(tdc);
+       tegra_dma_resume(tdc);
 
 skip_dma_stop:
        tegra_dma_abort_all(tdc);
@@ -1179,6 +1210,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc)
 static const struct tegra_dma_chip_data tegra20_dma_chip_data = {
        .nr_channels            = 16,
        .max_dma_count          = 1024UL * 64,
+       .support_channel_pause  = false,
 };
 
 #if defined(CONFIG_OF)
@@ -1186,10 +1218,21 @@ static const struct tegra_dma_chip_data tegra20_dma_chip_data = {
 static const struct tegra_dma_chip_data tegra30_dma_chip_data = {
        .nr_channels            = 32,
        .max_dma_count          = 1024UL * 64,
+       .support_channel_pause  = false,
+};
+
+/* Tegra114 specific DMA controller information */
+static const struct tegra_dma_chip_data tegra114_dma_chip_data = {
+       .nr_channels            = 32,
+       .max_dma_count          = 1024UL * 64,
+       .support_channel_pause  = true,
 };
 
 static const struct of_device_id tegra_dma_of_match[] = {
        {
+               .compatible = "nvidia,tegra114-apbdma",
+               .data = &tegra114_dma_chip_data,
+       }, {
                .compatible = "nvidia,tegra30-apbdma",
                .data = &tegra30_dma_chip_data,
        }, {
index 682de754d63f166a92de96338c7f4d01bf87cdb2..a1b31d32c8e2f19ee894c248c2c836acc7f15e67 100644 (file)
@@ -684,6 +684,13 @@ config GPIO_MSIC
          Enable support for GPIO on intel MSIC controllers found in
          intel MID devices
 
+config GPIO_PALMAS
+       bool "PALMAS GPIO"
+       depends on MFD_PALMAS
+       help
+         Select this option to enable GPIO driver for the Palmas
+         chip familly.
+
 comment "USB GPIO expanders:"
 
 config GPIO_VIPERBOARD
@@ -696,5 +703,4 @@ config GPIO_VIPERBOARD
           See viperboard API specification and Nano
           River Tech's viperboard.h for detailed meaning
           of the module parameters.
-
 endif
index c5aebd008dde4d3e5428fb90de3563f82ae159ac..2883b21215b871e0e2787fd75d65cd522aa66fb5 100644 (file)
@@ -49,6 +49,7 @@ obj-$(CONFIG_GPIO_MVEBU)        += gpio-mvebu.o
 obj-$(CONFIG_GPIO_MXC)         += gpio-mxc.o
 obj-$(CONFIG_GPIO_MXS)         += gpio-mxs.o
 obj-$(CONFIG_ARCH_OMAP)                += gpio-omap.o
+obj-$(CONFIG_GPIO_PALMAS)      += gpio-palmas.o
 obj-$(CONFIG_GPIO_PCA953X)     += gpio-pca953x.o
 obj-$(CONFIG_GPIO_PCF857X)     += gpio-pcf857x.o
 obj-$(CONFIG_GPIO_PCH)         += gpio-pch.o
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
new file mode 100644 (file)
index 0000000..c667c86
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * gpiolib support for Palmas Series PMICS
+ *
+ * Copyright 2011 Texas Instruments
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ * Based on gpio-wm831x.c
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/slab.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/regmap.h>
+#include <linux/mfd/palmas.h>
+
+struct palmas_gpio {
+       struct palmas *palmas;
+       struct gpio_chip gpio_chip;
+};
+
+static int palmas_gpio_read(struct palmas *palmas, unsigned int reg,
+               unsigned int *dest)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_GPIO_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_GPIO_BASE, reg);
+
+       return regmap_read(palmas->regmap[slave], addr, dest);
+}
+
+static int palmas_gpio_write(struct palmas *palmas, unsigned int reg,
+               unsigned int data)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_GPIO_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_GPIO_BASE, reg);
+
+       return regmap_write(palmas->regmap[slave], addr, data);
+}
+
+static struct palmas_gpio *to_palmas_gpio(struct gpio_chip *chip)
+{
+       return container_of(chip, struct palmas_gpio, gpio_chip);
+}
+
+static int palmas_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+       struct palmas_gpio *palmas_gpio = to_palmas_gpio(chip);
+       struct palmas *palmas = palmas_gpio->palmas;
+       int ret;
+       unsigned int reg = 0;
+
+       if (!((1 << offset) & palmas->gpio_muxed))
+               return -EINVAL;
+
+       ret = palmas_gpio_read(palmas, PALMAS_GPIO_DATA_DIR, &reg);
+       if (ret)
+               return ret;
+
+       reg &= ~(1 << offset);
+
+       return palmas_gpio_write(palmas, PALMAS_GPIO_DATA_DIR, reg);
+}
+
+static int palmas_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct palmas_gpio *palmas_gpio = to_palmas_gpio(chip);
+       struct palmas *palmas = palmas_gpio->palmas;
+       unsigned int reg = 0;
+
+       if (!((1 << offset) & palmas->gpio_muxed))
+               return 0;
+
+       palmas_gpio_read(palmas, PALMAS_GPIO_DATA_IN, &reg);
+
+       return !!(reg & (1 << offset));
+}
+
+static void palmas_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       struct palmas_gpio *palmas_gpio = to_palmas_gpio(chip);
+       struct palmas *palmas = palmas_gpio->palmas;
+       unsigned int reg;
+
+       if (!((1 << offset) & palmas->gpio_muxed))
+               return;
+
+       palmas_gpio_read(palmas, PALMAS_GPIO_DATA_OUT, &reg);
+
+       reg &= ~(1 << offset);
+       reg |= value << offset;
+
+       palmas_gpio_write(palmas, PALMAS_GPIO_DATA_OUT, reg);
+}
+
+static int palmas_gpio_direction_out(struct gpio_chip *chip,
+                                    unsigned offset, int value)
+{
+       struct palmas_gpio *palmas_gpio = to_palmas_gpio(chip);
+       struct palmas *palmas = palmas_gpio->palmas;
+       int ret;
+       unsigned int reg;
+
+       if (!((1 << offset) & palmas->gpio_muxed))
+               return -EINVAL;
+
+       ret = palmas_gpio_read(palmas, PALMAS_GPIO_DATA_DIR, &reg);
+       if (ret)
+               return ret;
+
+       reg |= 1 << offset;
+
+       return palmas_gpio_write(palmas, PALMAS_GPIO_DATA_DIR, reg);
+}
+
+static int palmas_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+       struct palmas_gpio *palmas_gpio = to_palmas_gpio(chip);
+       struct palmas *palmas = palmas_gpio->palmas;
+
+       return regmap_irq_get_virq(palmas->irq_data, offset);
+}
+
+static int palmas_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
+                                   unsigned debounce)
+{
+       struct palmas_gpio *palmas_gpio = to_palmas_gpio(chip);
+       struct palmas *palmas = palmas_gpio->palmas;
+       int ret;
+       unsigned int reg;
+
+       if (!((1 << offset) & palmas->gpio_muxed))
+               return -EINVAL;
+
+       ret = palmas_gpio_read(palmas, PALMAS_GPIO_DATA_DIR, &reg);
+       if (ret)
+               return ret;
+
+       if (debounce)
+               reg |= 1 << offset;
+       else
+               reg &= ~(1 << offset);
+
+       return palmas_gpio_write(palmas, PALMAS_GPIO_DATA_DIR, reg);
+}
+
+static struct gpio_chip template_chip = {
+       .label                  = "palmas",
+       .owner                  = THIS_MODULE,
+       .direction_input        = palmas_gpio_direction_in,
+       .get                    = palmas_gpio_get,
+       .direction_output       = palmas_gpio_direction_out,
+       .set                    = palmas_gpio_set,
+       .to_irq                 = palmas_gpio_to_irq,
+       .set_debounce           = palmas_gpio_set_debounce,
+       .can_sleep              = 1,
+       .ngpio                  = 8,
+};
+
+static int palmas_gpio_probe(struct platform_device *pdev)
+{
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+       struct palmas_platform_data *pdata = palmas->dev->platform_data;
+       struct palmas_gpio *gpio;
+       int ret;
+
+       gpio = kzalloc(sizeof(*gpio), GFP_KERNEL);
+       if (!gpio)
+               return -ENOMEM;
+
+       gpio->palmas = palmas;
+       gpio->gpio_chip = template_chip;
+       gpio->gpio_chip.dev = &pdev->dev;
+
+       if (pdata && pdata->gpio_base)
+               gpio->gpio_chip.base = pdata->gpio_base;
+       else
+               gpio->gpio_chip.base = -1;
+
+       ret = gpiochip_add(&gpio->gpio_chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
+                       ret);
+               goto err;
+       }
+
+       platform_set_drvdata(pdev, gpio);
+
+       return ret;
+
+err:
+       kfree(gpio);
+       return ret;
+}
+
+static int palmas_gpio_remove(struct platform_device *pdev)
+{
+       struct palmas_gpio *gpio = platform_get_drvdata(pdev);
+       int ret;
+
+       ret = gpiochip_remove(&gpio->gpio_chip);
+       if (ret == 0)
+               kfree(gpio);
+
+       return ret;
+}
+
+static struct of_device_id of_palmas_match_tbl[] = {
+       { .compatible = "ti,palmas-gpio", },
+       { /* end */ }
+};
+
+static struct platform_driver palmas_gpio_driver = {
+       .driver = {
+               .name = "palmas-gpio",
+               .of_match_table = of_palmas_match_tbl,
+               .owner = THIS_MODULE,
+       },
+       .probe = palmas_gpio_probe,
+       .remove = palmas_gpio_remove,
+};
+
+static int __init palmas_gpio_init(void)
+{
+       return platform_driver_register(&palmas_gpio_driver);
+}
+subsys_initcall(palmas_gpio_init);
+
+static void __exit palmas_gpio_exit(void)
+{
+       platform_driver_unregister(&palmas_gpio_driver);
+}
+module_exit(palmas_gpio_exit);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("GPIO interface for the Palmas series chips");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:palmas-gpio");
index cc102d25ee249e15f775d61aa8814fd394ab40ef..3cb81d40c8eb3eb3bb2f6e74c9af42fed6ec7b5f 100644 (file)
@@ -67,6 +67,7 @@ static const struct i2c_device_id pca953x_id[] = {
        { "tca6408", 8  | PCA953X_TYPE | PCA_INT, },
        { "tca6416", 16 | PCA953X_TYPE | PCA_INT, },
        { "tca6424", 24 | PCA953X_TYPE | PCA_INT, },
+       { "tca6424a", 24 | PCA953X_TYPE | PCA_INT, },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, pca953x_id);
@@ -807,10 +808,7 @@ static int __init pca953x_init(void)
 {
        return i2c_add_driver(&pca953x_driver);
 }
-/* register after i2c postcore initcall and before
- * subsys initcalls that may rely on these GPIOs
- */
-subsys_initcall(pca953x_init);
+module_init(pca953x_init);
 
 static void __exit pca953x_exit(void)
 {
index 259ef31abb18c9eccec32189f6f8641c79d9819f..1458f215ae8e25ae6a6dd52c2589fdacdac7331b 100644 (file)
@@ -627,4 +627,11 @@ config INPUT_XEN_KBDDEV_FRONTEND
          To compile this driver as a module, choose M here: the
          module will be called xen-kbdfront.
 
+config INPUT_PALMAS_PWRBUTTON
+       tristate "PALMAS Power Button Driver"
+       depends on MFD_PALMAS
+       help
+         Say Y here if you want to eable power key reporting for the palmas
+         familty of PMICs
+
 endif
index 1f1e1b109d9dd3dd739c12c01cdff6018469027e..470d7dfce3e5a2261dcb06fe09b8d6f6d213ea99 100644 (file)
@@ -52,6 +52,7 @@ obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)       += rotary_encoder.o
 obj-$(CONFIG_INPUT_SGI_BTNS)           += sgi_btns.o
 obj-$(CONFIG_INPUT_SPARCSPKR)          += sparcspkr.o
 obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON)  += twl4030-pwrbutton.o
+obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON)   += palmas-pwrbutton.o
 obj-$(CONFIG_INPUT_TWL4030_VIBRA)      += twl4030-vibra.o
 obj-$(CONFIG_INPUT_TWL6040_VIBRA)      += twl6040-vibra.o
 obj-$(CONFIG_INPUT_UINPUT)             += uinput.o
diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c
new file mode 100644 (file)
index 0000000..54bb85c
--- /dev/null
@@ -0,0 +1,162 @@
+/**
+ * Palmas Power Button Input Driver
+ *
+ * Copyright (C) 2012 Texas Instrument Inc
+ *
+ * Written by Girish S Ghongdemath <girishsg@ti.com>
+ * Based on twl4030-pwrbutton.c driver.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/palmas.h>
+#include <linux/of_platform.h>
+
+struct palmas_pwron {
+       struct palmas *palmas;
+       struct input_dev *input_dev;
+};
+
+static irqreturn_t pwron_irq(int irq, void *palmas_pwron)
+{
+       struct palmas_pwron *pwron = palmas_pwron;
+       struct input_dev *input_dev = pwron->input_dev;
+
+       input_report_key(input_dev, KEY_POWER, 1);
+       input_report_key(input_dev, KEY_POWER, 0);
+       input_sync(input_dev);
+
+       return IRQ_HANDLED;
+}
+
+static int palmas_pwron_probe(struct platform_device *pdev)
+{
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+       struct input_dev *input_dev;
+       struct palmas_pwron *pwron;
+       int irq;
+       unsigned int addr;
+       int err, slave;
+
+
+       pwron = kzalloc(sizeof(*pwron), GFP_KERNEL);
+       if (!pwron) {
+               dev_err(&pdev->dev, "PWRON memory allocation failed\n");
+               return -ENOMEM;
+       }
+
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+                       dev_err(&pdev->dev,
+                               "input_dev memory allocation failed\n");
+                       err = -ENOMEM;
+                       goto free_pwron;
+       }
+
+       input_dev->evbit[0] = BIT_MASK(EV_KEY);
+       input_dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+       input_dev->name = "palmas_pwron";
+       input_dev->phys = "palmas_pwron/input0";
+       input_dev->dev.parent = &pdev->dev;
+
+       pwron->palmas = palmas;
+       pwron->input_dev = input_dev;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_PMU_CONTROL_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE,
+                       PALMAS_PMU_CONTROL_BASE);
+
+       /* 6 Seconds as the LPK_TIME Long Press Key Time */
+       regmap_update_bits(palmas->regmap[slave], addr,
+                       PALMAS_LONG_PRESS_KEY_LPK_TIME_MASK, 0);
+
+       irq = regmap_irq_get_virq(palmas->irq_data, PALMAS_PWRON_IRQ);
+       err = request_threaded_irq(irq, NULL, pwron_irq,
+                       IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+                       "palmas_pwron", pwron);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Can't get IRQ for pwron: %d\n", err);
+               goto free_input_dev;
+       }
+
+       err = input_register_device(input_dev);
+       if (err) {
+               dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
+               goto free_irq;
+       }
+
+       platform_set_drvdata(pdev, input_dev);
+
+       return 0;
+
+free_irq:
+       free_irq(irq, input_dev);
+free_input_dev:
+       input_free_device(input_dev);
+free_pwron:
+       kfree(pwron);
+       return err;
+}
+
+static int palmas_pwron_remove(struct platform_device *pdev)
+{
+       struct input_dev *input_dev = platform_get_drvdata(pdev);
+       int irq = platform_get_irq(pdev, 0);
+
+       free_irq(irq, input_dev);
+       input_unregister_device(input_dev);
+
+       return 0;
+}
+
+static struct of_device_id of_palmas_match_tbl[] = {
+       { .compatible = "ti,palmas-pwrbutton", },
+       { /* end */ }
+};
+
+static struct platform_driver palmas_pwron_driver = {
+       .remove = palmas_pwron_remove,
+       .driver = {
+               .name = "palmas-pwrbutton",
+               .of_match_table = of_palmas_match_tbl,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init palmas_pwron_init(void)
+{
+       return platform_driver_probe(&palmas_pwron_driver,
+                       palmas_pwron_probe);
+}
+module_init(palmas_pwron_init);
+
+static void __exit palmas_pwron_exit(void)
+{
+       platform_driver_unregister(&palmas_pwron_driver);
+}
+module_exit(palmas_pwron_exit);
+
+MODULE_ALIAS("platform:palmas-pwrbutton");
+MODULE_DESCRIPTION("Palmas Power Button");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Girish S Ghongdemath <girishsg@ti.com>");
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
index 78eb6b30580acdf5aa86096ecef01b653ffec3f6..68a5f33152a8cd8914d39245257735d0e5ecae38 100644 (file)
@@ -43,7 +43,6 @@ struct vibra_info {
        struct device           *dev;
        struct input_dev        *input_dev;
 
-       struct workqueue_struct *workqueue;
        struct work_struct      play_work;
 
        bool                    enabled;
@@ -143,19 +142,7 @@ static int vibra_play(struct input_dev *input, void *data,
        if (!info->speed)
                info->speed = effect->u.rumble.weak_magnitude >> 9;
        info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
-       queue_work(info->workqueue, &info->play_work);
-       return 0;
-}
-
-static int twl4030_vibra_open(struct input_dev *input)
-{
-       struct vibra_info *info = input_get_drvdata(input);
-
-       info->workqueue = create_singlethread_workqueue("vibra");
-       if (info->workqueue == NULL) {
-               dev_err(&input->dev, "couldn't create workqueue\n");
-               return -ENOMEM;
-       }
+       schedule_work(&info->play_work);
        return 0;
 }
 
@@ -164,9 +151,6 @@ static void twl4030_vibra_close(struct input_dev *input)
        struct vibra_info *info = input_get_drvdata(input);
 
        cancel_work_sync(&info->play_work);
-       INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */
-       destroy_workqueue(info->workqueue);
-       info->workqueue = NULL;
 
        if (info->enabled)
                vibra_disable(info);
@@ -219,7 +203,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;
 
@@ -227,11 +211,10 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
        info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
        INIT_WORK(&info->play_work, vibra_play_work);
 
-       info->input_dev = input_allocate_device();
+       info->input_dev = devm_input_allocate_device(&pdev->dev);
        if (info->input_dev == NULL) {
                dev_err(&pdev->dev, "couldn't allocate input device\n");
-               ret = -ENOMEM;
-               goto err_kzalloc;
+               return -ENOMEM;
        }
 
        input_set_drvdata(info->input_dev, info);
@@ -239,14 +222,13 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
        info->input_dev->name = "twl4030:vibrator";
        info->input_dev->id.version = 1;
        info->input_dev->dev.parent = pdev->dev.parent;
-       info->input_dev->open = twl4030_vibra_open;
        info->input_dev->close = twl4030_vibra_close;
        __set_bit(FF_RUMBLE, info->input_dev->ffbit);
 
        ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
        if (ret < 0) {
                dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
-               goto err_ialloc;
+               return ret;
        }
 
        ret = input_register_device(info->input_dev);
@@ -262,28 +244,11 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
 
 err_iff:
        input_ff_destroy(info->input_dev);
-err_ialloc:
-       input_free_device(info->input_dev);
-err_kzalloc:
-       kfree(info);
        return ret;
 }
 
-static int twl4030_vibra_remove(struct platform_device *pdev)
-{
-       struct vibra_info *info = platform_get_drvdata(pdev);
-
-       /* this also free ff-memless and calls close if needed */
-       input_unregister_device(info->input_dev);
-       kfree(info);
-       platform_set_drvdata(pdev, NULL);
-
-       return 0;
-}
-
 static struct platform_driver twl4030_vibra_driver = {
        .probe          = twl4030_vibra_probe,
-       .remove         = twl4030_vibra_remove,
        .driver         = {
                .name   = "twl4030-vibra",
                .owner  = THIS_MODULE,
index 71a28ee699f3ef52732e7cd601f26d1ed2eb72c0..0c2dfc8e96918c3e022aad1920ece05e1115abd8 100644 (file)
@@ -275,7 +275,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
        if (!info) {
                dev_err(&pdev->dev, "couldn't allocate memory\n");
                return -ENOMEM;
@@ -309,53 +309,23 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
        if ((!info->vibldrv_res && !info->viblmotor_res) ||
            (!info->vibrdrv_res && !info->vibrmotor_res)) {
                dev_err(info->dev, "invalid vibra driver/motor resistance\n");
-               ret = -EINVAL;
-               goto err_kzalloc;
+               return -EINVAL;
        }
 
        info->irq = platform_get_irq(pdev, 0);
        if (info->irq < 0) {
                dev_err(info->dev, "invalid irq\n");
-               ret = -EINVAL;
-               goto err_kzalloc;
+               return -EINVAL;
        }
 
        mutex_init(&info->mutex);
 
-       info->input_dev = input_allocate_device();
-       if (info->input_dev == NULL) {
-               dev_err(info->dev, "couldn't allocate input device\n");
-               ret = -ENOMEM;
-               goto err_kzalloc;
-       }
-
-       input_set_drvdata(info->input_dev, info);
-
-       info->input_dev->name = "twl6040:vibrator";
-       info->input_dev->id.version = 1;
-       info->input_dev->dev.parent = pdev->dev.parent;
-       info->input_dev->close = twl6040_vibra_close;
-       __set_bit(FF_RUMBLE, info->input_dev->ffbit);
-
-       ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
-       if (ret < 0) {
-               dev_err(info->dev, "couldn't register vibrator to FF\n");
-               goto err_ialloc;
-       }
-
-       ret = input_register_device(info->input_dev);
-       if (ret < 0) {
-               dev_err(info->dev, "couldn't register input device\n");
-               goto err_iff;
-       }
-
-       platform_set_drvdata(pdev, info);
-
-       ret = request_threaded_irq(info->irq, NULL, twl6040_vib_irq_handler, 0,
-                                  "twl6040_irq_vib", info);
+       ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
+                                       twl6040_vib_irq_handler, 0,
+                                       "twl6040_irq_vib", info);
        if (ret) {
                dev_err(info->dev, "VIB IRQ request failed: %d\n", ret);
-               goto err_irq;
+               return ret;
        }
 
        info->supplies[0].supply = "vddvibl";
@@ -368,7 +338,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
                                 ARRAY_SIZE(info->supplies), info->supplies);
        if (ret) {
                dev_err(info->dev, "couldn't get regulators %d\n", ret);
-               goto err_regulator;
+               return ret;
        }
 
        if (vddvibl_uV) {
@@ -377,7 +347,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
                if (ret) {
                        dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
                                ret);
-                       goto err_voltage;
+                       goto err_regulator;
                }
        }
 
@@ -387,34 +357,49 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
                if (ret) {
                        dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
                                ret);
-                       goto err_voltage;
+                       goto err_regulator;
                }
        }
 
-       info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0);
-       if (info->workqueue == NULL) {
-               dev_err(info->dev, "couldn't create workqueue\n");
+       INIT_WORK(&info->play_work, vibra_play_work);
+
+       info->input_dev = input_allocate_device();
+       if (info->input_dev == NULL) {
+               dev_err(info->dev, "couldn't allocate input device\n");
                ret = -ENOMEM;
-               goto err_voltage;
+               goto err_regulator;
        }
-       INIT_WORK(&info->play_work, vibra_play_work);
+
+       input_set_drvdata(info->input_dev, info);
+
+       info->input_dev->name = "twl6040:vibrator";
+       info->input_dev->id.version = 1;
+       info->input_dev->dev.parent = pdev->dev.parent;
+       info->input_dev->close = twl6040_vibra_close;
+       __set_bit(FF_RUMBLE, info->input_dev->ffbit);
+
+       ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
+       if (ret < 0) {
+               dev_err(info->dev, "couldn't register vibrator to FF\n");
+               goto err_ialloc;
+       }
+
+       ret = input_register_device(info->input_dev);
+       if (ret < 0) {
+               dev_err(info->dev, "couldn't register input device\n");
+               goto err_iff;
+       }
+
+       platform_set_drvdata(pdev, info);
 
        return 0;
 
-err_voltage:
-       regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
-err_regulator:
-       free_irq(info->irq, info);
-err_irq:
-       input_unregister_device(info->input_dev);
-       info->input_dev = NULL;
 err_iff:
-       if (info->input_dev)
-               input_ff_destroy(info->input_dev);
+       input_ff_destroy(info->input_dev);
 err_ialloc:
        input_free_device(info->input_dev);
-err_kzalloc:
-       kfree(info);
+err_regulator:
+       regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
        return ret;
 }
 
@@ -423,10 +408,7 @@ static int twl6040_vibra_remove(struct platform_device *pdev)
        struct vibra_info *info = platform_get_drvdata(pdev);
 
        input_unregister_device(info->input_dev);
-       free_irq(info->irq, info);
        regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
-       destroy_workqueue(info->workqueue);
-       kfree(info);
 
        return 0;
 }
index b58bc8a14b9cecc484ef86d0088c53f0a3de9168..76e317007ccda2e50ab2da5ded470409b90c2a14 100644 (file)
@@ -459,6 +459,15 @@ config LEDS_BLINKM
          This option enables support for the BlinkM RGB LED connected
          through I2C. Say Y to enable support for the BlinkM LED.
 
+config LEDS_PALMAS
+       bool "LED support for the Palmas family of PMICs"
+       depends on LEDS_CLASS
+       depends on MFD_PALMAS
+       help
+         This option enables the driver for LED1 & LED2 pins on Palmas PMIC
+         if these pins are enabled in the mux configuration. The driver support
+         ON/OFF and blinking with hardware control.
+
 config LEDS_TRIGGERS
        bool "LED Trigger support"
        depends on LEDS_CLASS
index 3fb9641b619411ba058ca3cbd6c584deeeb20a19..4ae938d0b138b91945e50729d4ce1ce874588f9a 100644 (file)
@@ -51,6 +51,7 @@ obj-$(CONFIG_LEDS_RENESAS_TPU)                += leds-renesas-tpu.o
 obj-$(CONFIG_LEDS_MAX8997)             += leds-max8997.o
 obj-$(CONFIG_LEDS_LM355x)              += leds-lm355x.o
 obj-$(CONFIG_LEDS_BLINKM)              += leds-blinkm.o
+obj-$(CONFIG_LEDS_PALMAS)              += leds-palmas.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)          += leds-dac124s085.o
diff --git a/drivers/leds/leds-palmas.c b/drivers/leds/leds-palmas.c
new file mode 100644 (file)
index 0000000..4bdb48a
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * Driver for LED part of Palmas PMIC Chips
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ *  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/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/mfd/palmas.h>
+#include <linux/module.h>
+
+struct palmas_leds_data;
+
+struct palmas_led {
+       struct led_classdev cdev;
+       struct palmas_leds_data *leds_data;
+       int led_no;
+       struct work_struct work;
+       struct mutex mutex;
+
+       spinlock_t value_lock;
+
+       int blink;
+       int on_time;
+       int period;
+       enum led_brightness brightness;
+};
+
+struct palmas_leds_data {
+       struct device *dev;
+       struct led_classdev cdev;
+       struct palmas *palmas;
+
+       struct palmas_led *palmas_led;
+       int no_leds;
+};
+
+#define to_palmas_led(led_cdev) \
+       container_of(led_cdev, struct palmas_led, cdev)
+
+#define LED_ON_62_5MS          0x00
+#define LED_ON_125MS           0x01
+#define LED_ON_250MS           0x02
+#define LED_ON_500MS           0x03
+
+#define LED_PERIOD_OFF         0x00
+#define LED_PERIOD_125MS       0x01
+#define LED_PERIOD_250MS       0x02
+#define LED_PERIOD_500MS       0x03
+#define LED_PERIOD_1000MS      0x04
+#define LED_PERIOD_2000MS      0x05
+#define LED_PERIOD_4000MS      0x06
+#define LED_PERIOD_8000MS      0x07
+
+
+static char *palmas_led_names[] = {
+       "palmas:led1",
+       "palmas:led2",
+};
+
+int palmas_led_read(struct palmas_leds_data *leds, unsigned int reg,
+               unsigned int *dest)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_LED_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_LED_BASE, reg);
+
+       return regmap_read(leds->palmas->regmap[slave], addr, dest);
+}
+
+int palmas_led_write(struct palmas_leds_data *leds, unsigned int reg,
+               unsigned int value)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_LED_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_LED_BASE, reg);
+
+       return regmap_write(leds->palmas->regmap[slave], addr, value);
+}
+
+static void palmas_leds_work(struct work_struct *work)
+{
+       struct palmas_led *led = container_of(work, struct palmas_led, work);
+       unsigned int period, ctrl;
+       unsigned long flags;
+
+       mutex_lock(&led->mutex);
+
+       palmas_led_read(led->leds_data, PALMAS_LED_CTRL, &ctrl);
+       palmas_led_read(led->leds_data, PALMAS_LED_PERIOD_CTRL, &period);
+
+       spin_lock_irqsave(&led->value_lock, flags);
+
+       switch (led->led_no) {
+       case 1:
+               ctrl &= ~PALMAS_LED_CTRL_LED_1_ON_TIME_MASK;
+               period &= ~PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_MASK;
+
+               if (led->blink) {
+                       ctrl |= led->on_time <<
+                               PALMAS_LED_CTRL_LED_1_ON_TIME_SHIFT;
+                       period |= led->period <<
+                               PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_SHIFT;
+               } else {
+                       /*
+                        * for off value is zero which we already set by
+                        * masking earlier.
+                        */
+                       if (led->brightness) {
+                               ctrl |= LED_ON_500MS <<
+                                       PALMAS_LED_CTRL_LED_1_ON_TIME_SHIFT;
+                               period |= LED_PERIOD_500MS <<
+                                       PALMAS_LED_PERIOD_CTRL_LED_1_PERIOD_SHIFT;
+                       }
+               }
+       case 2:
+               ctrl &= ~PALMAS_LED_CTRL_LED_2_ON_TIME_MASK;
+               period &= ~PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_MASK;
+
+               if (led->blink) {
+                       ctrl |= led->on_time <<
+                               PALMAS_LED_CTRL_LED_2_ON_TIME_SHIFT;
+                       period |= led->period <<
+                               PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_SHIFT;
+               } else {
+                       /*
+                        * for off value is zero which we already set by
+                        * masking earlier.
+                        */
+                       if (led->brightness) {
+                               ctrl |= LED_ON_500MS <<
+                                       PALMAS_LED_CTRL_LED_2_ON_TIME_SHIFT;
+                               period |= LED_PERIOD_500MS <<
+                                       PALMAS_LED_PERIOD_CTRL_LED_2_PERIOD_SHIFT;
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&led->value_lock, flags);
+
+       palmas_led_write(led->leds_data, PALMAS_LED_CTRL, ctrl);
+       palmas_led_write(led->leds_data, PALMAS_LED_PERIOD_CTRL, period);
+
+       mutex_unlock(&led->mutex);
+}
+
+static void palmas_leds_set(struct led_classdev *led_cdev,
+                          enum led_brightness value)
+{
+       struct palmas_led *led = to_palmas_led(led_cdev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&led->value_lock, flags);
+       led->brightness = value;
+       led->blink = 0;
+       schedule_work(&led->work);
+       spin_unlock_irqrestore(&led->value_lock, flags);
+}
+
+static int palmas_leds_blink_set(struct led_classdev *led_cdev,
+                                  unsigned long *delay_on,
+                                  unsigned long *delay_off)
+{
+       struct palmas_led *led = to_palmas_led(led_cdev);
+       unsigned long flags;
+       int ret = 0;
+       int period;
+
+       /* Pick some defaults if we've not been given times */
+       if (*delay_on == 0 && *delay_off == 0) {
+               *delay_on = 250;
+               *delay_off = 250;
+       }
+
+       spin_lock_irqsave(&led->value_lock, flags);
+
+       /* We only have a limited selection of settings, see if we can
+        * support the configuration we're being given */
+       switch (*delay_on) {
+       case 500:
+               led->on_time = LED_ON_500MS;
+               break;
+       case 250:
+               led->on_time = LED_ON_250MS;
+               break;
+       case 125:
+               led->on_time = LED_ON_125MS;
+               break;
+       case 62:
+       case 63:
+               /* Actually 62.5ms */
+               led->on_time = LED_ON_62_5MS;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       period = *delay_on + *delay_off;
+
+       if (ret == 0) {
+               switch (period) {
+               case 124:
+               case 125:
+               case 126:
+                       /* All possible variations of 62.5 + 62.5 */
+                       led->period = LED_PERIOD_125MS;
+                       break;
+               case 250:
+                       led->period = LED_PERIOD_250MS;
+                       break;
+               case 500:
+                       led->period = LED_PERIOD_500MS;
+                       break;
+               case 1000:
+                       led->period = LED_PERIOD_1000MS;
+                       break;
+               case 2000:
+                       led->period = LED_PERIOD_2000MS;
+                       break;
+               case 4000:
+                       led->period = LED_PERIOD_4000MS;
+                       break;
+               case 8000:
+                       led->period = LED_PERIOD_8000MS;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
+               }
+       }
+
+       if (ret == 0)
+               led->blink = 1;
+       else
+               led->blink = 0;
+
+       /* Always update; if we fail turn off blinking since we expect
+        * a software fallback. */
+       schedule_work(&led->work);
+
+       spin_unlock_irqrestore(&led->value_lock, flags);
+
+       return ret;
+}
+
+static void palmas_init_led(struct palmas_leds_data *leds_data, int offset,
+               int led_no)
+{
+       mutex_init(&leds_data->palmas_led[offset].mutex);
+       INIT_WORK(&leds_data->palmas_led[offset].work, palmas_leds_work);
+       spin_lock_init(&leds_data->palmas_led[offset].value_lock);
+
+       leds_data->palmas_led[offset].leds_data = leds_data;
+       leds_data->palmas_led[offset].led_no = led_no;
+       leds_data->palmas_led[offset].cdev.name = palmas_led_names[led_no - 1];
+       leds_data->palmas_led[offset].cdev.default_trigger = NULL;
+       leds_data->palmas_led[offset].cdev.brightness_set = palmas_leds_set;
+       leds_data->palmas_led[offset].cdev.blink_set = palmas_leds_blink_set;
+}
+
+static int palmas_leds_probe(struct platform_device *pdev)
+{
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+       struct palmas_leds_data *leds_data;
+       int ret, i;
+       int offset = 0;
+
+       if (!palmas->led_muxed) {
+               dev_err(&pdev->dev, "there are no LEDs muxed\n");
+               return -EINVAL;
+       }
+
+       leds_data = kzalloc(sizeof(*leds_data), GFP_KERNEL);
+       if (!leds_data)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, leds_data);
+
+       leds_data->palmas = palmas;
+
+       if (palmas->led_muxed == (PALMAS_LED1_MUXED | PALMAS_LED2_MUXED))
+               leds_data->no_leds = 2;
+       else
+               leds_data->no_leds = 1;
+
+       leds_data->palmas_led = kcalloc(leds_data->no_leds,
+                       sizeof(struct palmas_led), GFP_KERNEL);
+
+       /* Initialise LED1 */
+       if (palmas->led_muxed & PALMAS_LED1_MUXED) {
+               palmas_init_led(leds_data, offset, 1);
+               offset = 1;
+       }
+
+       /* Initialise LED2 */
+       if (palmas->led_muxed & PALMAS_LED2_MUXED)
+               palmas_init_led(leds_data, offset, 2);
+
+       for (i = 0; i < leds_data->no_leds; i++) {
+               ret = led_classdev_register(leds_data->dev,
+                               &leds_data->palmas_led[i].cdev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev,
+                               "Failed to register LED no: %d err: %d\n",
+                               i, ret);
+                       goto err_led;
+               }
+       }
+
+       return 0;
+
+err_led:
+       for (i = 0; i < leds_data->no_leds; i++)
+               led_classdev_unregister(&leds_data->palmas_led[i].cdev);
+       kfree(leds_data->palmas_led);
+       kfree(leds_data);
+       return ret;
+}
+
+static int palmas_leds_remove(struct platform_device *pdev)
+{
+       struct palmas_leds_data *leds_data = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < leds_data->no_leds; i++)
+               led_classdev_unregister(&leds_data->palmas_led[i].cdev);
+       if (i)
+               kfree(leds_data->palmas_led);
+       kfree(leds_data);
+
+       return 0;
+}
+
+static struct of_device_id of_palmas_match_tbl[] = {
+       { .compatible = "ti,palmas-leds", },
+       { /* end */ }
+};
+
+static struct platform_driver palmas_leds_driver = {
+       .driver = {
+               .name = "palmas-leds",
+               .of_match_table = of_palmas_match_tbl,
+               .owner = THIS_MODULE,
+       },
+       .probe = palmas_leds_probe,
+       .remove = palmas_leds_remove,
+};
+
+static int __init palmas_leds_init(void)
+{
+       return platform_driver_register(&palmas_leds_driver);
+}
+module_init(palmas_leds_init);
+
+static void __exit palmas_leds_exit(void)
+{
+       platform_driver_unregister(&palmas_leds_driver);
+}
+module_exit(palmas_leds_exit);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("Palmas LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:palmas-leds");
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
new file mode 100644 (file)
index 0000000..d352028
--- /dev/null
@@ -0,0 +1,52 @@
+menuconfig MAILBOX
+       bool "Mailbox Hardware Support"
+       help
+         Mailbox is a framework to control hardware communication between
+         on-chip processors through queued messages and interrupt driven
+         signals. Say Y if your platform supports hardware mailboxes.
+
+if MAILBOX
+
+config OMAP1_MBOX
+       tristate "OMAP1 Mailbox framework support"
+       depends on ARCH_OMAP1
+       help
+         Mailbox implementation for OMAP chips with hardware for
+         interprocessor communication involving DSP in OMAP1. Say Y here
+         if you want to use OMAP1 Mailbox framework support.
+
+config OMAP2PLUS_MBOX
+       tristate "OMAP2+ Mailbox framework support"
+       depends on ARCH_OMAP2PLUS
+       help
+         Mailbox implementation for OMAP family chips with hardware for
+         interprocessor communication involving DSP, IVA1.0 and IVA2 in
+         OMAP2/3; or IPU, IVA HD and DSP in OMAP4. Say Y here if you want
+         to use OMAP2+ Mailbox framework support.
+
+config DBX500_MBOX
+       tristate "DBx500 Mailbox driver support"
+       depends on ARCH_U8500
+       help
+         Say Y here if you want to use DBx500 Mailbox driver support for
+         power coprocessor access on Ux500 and Ux540 families
+
+config MBOX_KFIFO_SIZE
+       int "Mailbox kfifo default buffer size (bytes)"
+       default 256
+       help
+         Specify the default size of mailbox's kfifo buffers (bytes).
+         This can also be changed at runtime (via the mbox_kfifo_size
+         module parameter).
+
+config MBOX_DATA_SIZE
+       int "Mailbox associated data max size (bytes)"
+       default 64 if DBX500_MBOX
+       default 4
+       help
+         Specify the default size of mailbox's associated data buffer
+         (bytes)
+          This can also be changed at runtime (via the mbox_kfifo_size
+          module parameter).
+
+endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
new file mode 100644 (file)
index 0000000..c04ce79
--- /dev/null
@@ -0,0 +1,5 @@
+obj-$(CONFIG_MAILBOX) += mailbox.o
+
+obj-$(CONFIG_OMAP1_MBOX)       += mailbox-omap1.o
+obj-$(CONFIG_OMAP2PLUS_MBOX)   += mailbox-omap2.o
+obj-$(CONFIG_DBX500_MBOX)      += mailbox-dbx500.o
diff --git a/drivers/mailbox/mailbox-dbx500.c b/drivers/mailbox/mailbox-dbx500.c
new file mode 100644 (file)
index 0000000..e9d2b0a
--- /dev/null
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Loic Pallardy <loic.pallardy@st.com> for ST-Ericsson
+ * DBX500 PRCM Mailbox driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/platform_data/mailbox-dbx500.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/irqdomain.h>
+
+#include "mailbox_internal.h"
+
+#define MAILBOX_LEGACY         0
+#define MAILBOX_UPAP           1
+
+enum {
+       MAILBOX_BLOCKING,
+       MAILBOX_ATOMIC,
+};
+
+/* CPU mailbox registers */
+#define PRCM_MBOX_CPU_VAL      0x0fc
+#define PRCM_MBOX_CPU_SET      0x100
+#define PRCM_MBOX_CPU_CLR      0x104
+
+#define PRCM_ARM_IT1_CLR       0x48C
+#define PRCM_ARM_IT1_VAL       0x494
+
+#define NUM_MB 8
+#define MBOX_BIT BIT
+#define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1)
+
+/* CPU mailbox shared memory */
+#define PRCM_MBOX_LEGACY_SIZE          0x220
+#define _PRCM_MBOX_HEADER              0x214 /* 16 bytes */
+#define PRCM_MBOX_HEADER_REQ_MB0       (_PRCM_MBOX_HEADER + 0x0)
+#define PRCM_MBOX_HEADER_REQ_MB1       (_PRCM_MBOX_HEADER + 0x1)
+#define PRCM_MBOX_HEADER_REQ_MB2       (_PRCM_MBOX_HEADER + 0x2)
+#define PRCM_MBOX_HEADER_REQ_MB3       (_PRCM_MBOX_HEADER + 0x3)
+#define PRCM_MBOX_HEADER_REQ_MB4       (_PRCM_MBOX_HEADER + 0x4)
+#define PRCM_MBOX_HEADER_REQ_MB5       (_PRCM_MBOX_HEADER + 0x5)
+#define PRCM_MBOX_HEADER_ACK_MB0       (_PRCM_MBOX_HEADER + 0x8)
+
+/* Req Mailboxes */
+#define PRCM_REQ_MB0                   0x208 /* 12 bytes  */
+#define PRCM_REQ_MB1                   0x1FC /* 12 bytes  */
+#define PRCM_REQ_MB2                   0x1EC /* 16 bytes  */
+#define PRCM_REQ_MB3                   0x78  /* 372 bytes  */
+#define PRCM_REQ_MB4                   0x74  /* 4 bytes  */
+#define PRCM_REQ_MB5                   0x70  /* 4 bytes  */
+#define PRCM_REQ_PASR                  0x30 /* 4 bytes */
+
+/* Ack Mailboxes */
+#define PRCM_ACK_MB0                   0x34 /* 52 bytes  */
+#define PRCM_ACK_MB1                   0x30 /* 4 bytes */
+#define PRCM_ACK_MB2                   0x2C /* 4 bytes */
+#define PRCM_ACK_MB3                   0x28 /* 4 bytes */
+#define PRCM_ACK_MB4                   0x24 /* 4 bytes */
+#define PRCM_ACK_MB5                   0x20 /* 4 bytes */
+
+/* Ack Mailboxe sizes */
+#define PRCM_ACK_MB0_SIZE              0x24 /* 52 bytes  */
+#define PRCM_ACK_MB1_SIZE              0x4 /* 4 bytes */
+#define PRCM_ACK_MB2_SIZE              0x1 /* 1 bytes */
+#define PRCM_ACK_MB3_SIZE              0x2 /* 2 bytes */
+#define PRCM_ACK_MB4_SIZE              0x0 /* 0 bytes */
+#define PRCM_ACK_MB5_SIZE              0x4 /* 4 bytes */
+
+static void __iomem *mbox_base;
+static void __iomem *tcdm_mem_base;
+
+static u8 prcm_mbox_irq_mask; /* masked by default */
+static DEFINE_SPINLOCK(prcm_mbox_irqs_lock);
+
+struct dbx500_mbox_priv {
+       int                     access_mode;
+       int                     type;
+       int                     header_size;
+       unsigned int            tx_header_offset;
+       unsigned int            rx_header_offset;
+       unsigned int            tx_offset;
+       unsigned int            rx_offset;
+       unsigned int            rx_size;
+       unsigned int            sw_irq;
+       bool                    empty_flag;
+};
+
+static inline unsigned int mbox_read_reg(size_t ofs)
+{
+       return __raw_readl(mbox_base + ofs);
+}
+
+static inline void mbox_write_reg(u32 val, size_t ofs)
+{
+       __raw_writel(val, mbox_base + ofs);
+}
+
+/* Mailbox H/W preparations */
+static struct irq_chip dbx500_mbox_irq_chip;
+static struct irq_domain *dbx500_mbox_irq_domain;
+
+static int dbx500_mbox_startup(struct mailbox *mbox)
+{
+       /* Nothing to do */
+       return 0;
+}
+
+/* Mailbox IRQ handle functions */
+
+static void dbx500_mbox_enable_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+       prcm_mbox_irq_mask |= MBOX_BIT(mbox->id);
+
+       spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+
+}
+
+static void dbx500_mbox_disable_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+       prcm_mbox_irq_mask &= ~MBOX_BIT(mbox->id);
+
+       spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+
+}
+
+static void dbx500_mbox_ack_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+       if (irq == IRQ_RX)
+               mbox_write_reg(MBOX_BIT(mbox->id), PRCM_ARM_IT1_CLR);
+}
+
+static int dbx500_mbox_is_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+       struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+       if (irq == IRQ_RX) {
+               if (mbox_read_reg(PRCM_ARM_IT1_VAL) & MBOX_BIT(mbox->id)) {
+                       priv->empty_flag = true;
+                       return 1;
+               } else {
+                       return 0;
+               }
+       } else {
+               return 0;
+       }
+}
+
+/* message management */
+
+static int dbx500_mbox_is_ready(struct mailbox *mbox)
+{
+       return mbox_read_reg(PRCM_MBOX_CPU_VAL) & MBOX_BIT(mbox->id);
+}
+
+static int dbx500_mbox_write(struct mailbox *mbox,
+               struct mailbox_msg *msg)
+{
+       int j;
+       struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+       if (msg->size && !msg->pdata)
+               return -EINVAL;
+
+       while (dbx500_mbox_is_ready(mbox))
+               cpu_relax();
+
+       /* write header */
+       if (priv->header_size)
+               writeb(msg->header, tcdm_mem_base + priv->tx_header_offset);
+
+       /* write data */
+       for (j = 0; j < msg->size; j++)
+               writeb(((unsigned char *)msg->pdata)[j],
+                               tcdm_mem_base + priv->tx_offset + j);
+
+       /* send event */
+       mbox_write_reg(MBOX_BIT(mbox->id), PRCM_MBOX_CPU_SET);
+
+       return 0;
+}
+
+static void dbx500_mbox_read(struct mailbox *mbox, struct mailbox_msg *msg)
+{
+       struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+
+       msg->header = readb(tcdm_mem_base + priv->rx_header_offset);
+       msg->pdata = (unsigned char *)(tcdm_mem_base + priv->rx_offset);
+
+       msg->size = priv->rx_size;
+       priv->empty_flag = false;
+}
+
+static int dbx500_mbox_empty(struct mailbox *mbox)
+{
+       struct dbx500_mbox_priv *priv = (struct dbx500_mbox_priv *)mbox->priv;
+       if (priv->empty_flag)
+               return 0;
+       else
+               return 1;
+}
+
+static int dbx500_mbox_poll_for_space(struct mailbox *mbox)
+{
+       return 0;
+}
+/*  interrupt management */
+
+/*  mask/unmask must be managed by SW */
+
+static void mbox_irq_mask(struct irq_data *d)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+       prcm_mbox_irq_mask &= ~MBOX_BIT(d->hwirq);
+
+       spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+}
+
+static void mbox_irq_unmask(struct irq_data *d)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&prcm_mbox_irqs_lock, flags);
+
+       prcm_mbox_irq_mask |= MBOX_BIT(d->hwirq);
+
+       spin_unlock_irqrestore(&prcm_mbox_irqs_lock, flags);
+}
+
+static void mbox_irq_ack(struct irq_data *d)
+{
+       mbox_write_reg(MBOX_BIT(d->hwirq), PRCM_ARM_IT1_CLR);
+}
+
+static struct irq_chip dbx500_mbox_irq_chip = {
+       .name           = "dbx500_mbox",
+       .irq_disable    = mbox_irq_unmask,
+       .irq_ack        = mbox_irq_ack,
+       .irq_mask       = mbox_irq_mask,
+       .irq_unmask     = mbox_irq_unmask,
+};
+
+static int dbx500_mbox_irq_map(struct irq_domain *d, unsigned int irq,
+                              irq_hw_number_t hwirq)
+{
+       irq_set_chip_and_handler(irq, &dbx500_mbox_irq_chip,
+                               handle_simple_irq);
+       set_irq_flags(irq, IRQF_VALID);
+
+       return 0;
+}
+
+static struct irq_domain_ops dbx500_mbox_irq_ops = {
+       .map    = dbx500_mbox_irq_map,
+       .xlate  = irq_domain_xlate_twocell,
+};
+
+static irqreturn_t dbx500_mbox_irq_handler(int irq, void *data)
+{
+       u32 bits;
+       u8 n;
+
+       bits = (mbox_read_reg(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+       if (unlikely(!bits))
+               return IRQ_NONE;
+
+       bits &= prcm_mbox_irq_mask;
+
+       for (n = 0; bits; n++) {
+               if (bits & MBOX_BIT(n)) {
+                       bits -= MBOX_BIT(n);
+                       generic_handle_irq(
+                               irq_find_mapping(dbx500_mbox_irq_domain, n));
+               }
+       }
+       return IRQ_HANDLED;
+}
+
+/* 5 mailboxes AP <--> PRCMU */
+static struct mailbox_ops dbx500_mbox_ops = {
+       .type           = MBOX_SHARED_MEM_TYPE,
+       .startup        = dbx500_mbox_startup,
+       .enable_irq     = dbx500_mbox_enable_irq,
+       .disable_irq    = dbx500_mbox_disable_irq,
+       .ack_irq        = dbx500_mbox_ack_irq,
+       .is_irq         = dbx500_mbox_is_irq,
+       .read           = dbx500_mbox_read,
+       .write          = dbx500_mbox_write,
+       .empty          = dbx500_mbox_empty,
+       .poll_for_space = dbx500_mbox_poll_for_space,
+};
+
+struct dbx500_mbox_priv mbox0_priv = {
+       .access_mode            = MAILBOX_ATOMIC,
+       .type                   = MAILBOX_LEGACY,
+       .tx_header_offset       = PRCM_MBOX_HEADER_REQ_MB0,
+       .header_size            = 1,
+       .tx_offset              = PRCM_REQ_MB0,
+       .rx_header_offset       = PRCM_MBOX_HEADER_ACK_MB0,
+       .rx_offset              = PRCM_ACK_MB0,
+       .rx_size                = PRCM_ACK_MB0_SIZE,
+};
+
+struct mailbox mbox0_info = {
+       .name   = "mbox0",
+       .id     = 0,
+       .ops    = &dbx500_mbox_ops,
+       .priv   = &mbox0_priv,
+};
+
+struct dbx500_mbox_priv mbox1_priv = {
+       .access_mode            = MAILBOX_BLOCKING,
+       .type                   = MAILBOX_LEGACY,
+       .tx_header_offset       = PRCM_MBOX_HEADER_REQ_MB1,
+       .header_size            = 1,
+       .tx_offset              = PRCM_REQ_MB1,
+       .rx_header_offset       = PRCM_MBOX_HEADER_REQ_MB1,
+       .rx_offset              = PRCM_ACK_MB1,
+       .rx_size                = PRCM_ACK_MB1_SIZE,
+};
+
+struct mailbox mbox1_info = {
+       .name   = "mbox1",
+       .id     = 1,
+       .ops    = &dbx500_mbox_ops,
+       .priv   = &mbox1_priv,
+};
+
+struct dbx500_mbox_priv mbox2_priv = {
+       .access_mode            = MAILBOX_BLOCKING,
+       .type                   = MAILBOX_LEGACY,
+       .tx_header_offset       = PRCM_MBOX_HEADER_REQ_MB2,
+       .header_size            = 1,
+       .tx_offset              = PRCM_REQ_MB2,
+       .rx_header_offset       = PRCM_MBOX_HEADER_REQ_MB2,
+       .rx_offset              = PRCM_ACK_MB2,
+       .rx_size                = PRCM_ACK_MB2_SIZE,
+};
+
+struct mailbox mbox2_info = {
+       .name   = "mbox2",
+       .id     = 2,
+       .ops    = &dbx500_mbox_ops,
+       .priv   = &mbox2_priv,
+};
+
+struct dbx500_mbox_priv mbox3_priv = {
+       .access_mode            = MAILBOX_BLOCKING,
+       .type                   = MAILBOX_LEGACY,
+       .tx_header_offset       = PRCM_MBOX_HEADER_REQ_MB3,
+       .header_size            = 1,
+       .tx_offset              = PRCM_REQ_MB3,
+       .rx_header_offset       = PRCM_MBOX_HEADER_REQ_MB3,
+       .rx_offset              = PRCM_ACK_MB3,
+       .rx_size                = PRCM_ACK_MB3_SIZE,
+};
+
+struct mailbox mbox3_info = {
+       .name   = "mbox3",
+       .id     = 3,
+       .ops    = &dbx500_mbox_ops,
+       .priv   = &mbox3_priv,
+};
+
+struct dbx500_mbox_priv mbox4_priv = {
+       .access_mode            = MAILBOX_BLOCKING,
+       .type                   = MAILBOX_LEGACY,
+       .tx_header_offset       = PRCM_MBOX_HEADER_REQ_MB4,
+       .header_size            = 1,
+       .tx_offset              = PRCM_REQ_MB4,
+       .rx_header_offset       = PRCM_MBOX_HEADER_REQ_MB4,
+       .rx_offset              = PRCM_ACK_MB4,
+       .rx_size                = PRCM_ACK_MB4_SIZE,
+};
+
+struct mailbox mbox4_info = {
+       .name   = "mbox4",
+       .id     = 4,
+       .ops    = &dbx500_mbox_ops,
+       .priv   = &mbox4_priv,
+};
+
+struct dbx500_mbox_priv mbox5_priv = {
+       .access_mode            = MAILBOX_BLOCKING,
+       .type                   = MAILBOX_LEGACY,
+       .tx_header_offset       = PRCM_MBOX_HEADER_REQ_MB5,
+       .header_size            = 1,
+       .tx_offset              = PRCM_REQ_MB5,
+       .rx_header_offset       = PRCM_MBOX_HEADER_REQ_MB5,
+       .rx_offset              = PRCM_ACK_MB5,
+       .rx_size                = PRCM_ACK_MB5_SIZE,
+};
+
+struct mailbox mbox5_info = {
+       .name   = "mbox5",
+       .id     = 5,
+       .ops    = &dbx500_mbox_ops,
+       .priv   = &mbox5_priv,
+};
+
+struct mailbox mbox6_info = {
+       .name   = "mbox6",
+       .id     = 6,
+       .ops    = &dbx500_mbox_ops,
+};
+
+struct mailbox mbox7_info = {
+       .name   = "mbox7",
+       .id     = 7,
+       .ops    = &dbx500_mbox_ops,
+};
+
+/* x540 mailbox definition */
+struct dbx500_mbox_priv mbox1_upap_priv = {
+       .access_mode    = MAILBOX_BLOCKING,
+       .type                   = MAILBOX_UPAP,
+       .tx_header_offset       = 0,
+       .header_size    = 0,
+       .tx_offset      = 0,
+       .rx_offset      = 0, /* TODO to be replaced by dynamic detection */
+       .rx_size        = 0x40,
+};
+
+struct mailbox mbox1_upap_info = {
+       .name   = "mbox1_upap",
+       .id     = 1,
+       .ops    = &dbx500_mbox_ops,
+       .priv   = &mbox1_upap_priv,
+};
+
+struct mailbox *db8500_mboxes[] = { &mbox0_info, &mbox1_info, &mbox2_info,
+       &mbox3_info, &mbox4_info, &mbox5_info, &mbox6_info, &mbox7_info,
+       NULL };
+
+struct mailbox *dbx540_mboxes[] = { &mbox0_info, &mbox2_info,
+       &mbox3_info, &mbox4_info, &mbox5_info, &mbox6_info, &mbox7_info,
+       &mbox1_upap_info, NULL };
+
+static const struct of_device_id dbx500_mailbox_match[] = {
+       { .compatible = "stericsson,db8500-mailbox",
+               .data = (void *)db8500_mboxes,
+       },
+       { .compatible = "stericsson,db9540-mailbox",
+               .data = (void *)dbx540_mboxes,
+       },
+       { /* sentinel */}
+};
+
+static int dbx500_mbox_probe(struct platform_device *pdev)
+{
+       const struct platform_device_id *platid = platform_get_device_id(pdev);
+       struct resource *mem;
+       int ret, i, irq;
+       u32 legacy_offset = 0;
+       u32 upap_offset = 0, upap_size = PRCM_MBOX_LEGACY_SIZE;
+       struct mailbox **list;
+       struct dbx500_plat_data *pdata = dev_get_platdata(&pdev->dev);
+       struct device_node *np = pdev->dev.of_node;
+       bool upap_used = false;
+
+       if (!pdata) {
+               if (np) {
+                       if (of_property_read_u32(np, "legacy-offset",
+                                                       &legacy_offset)) {
+                               /* missing legacy-offset */
+                               dev_err(&pdev->dev, "No legacy offset found\n");
+                               return -ENODEV;
+                       }
+                       if (of_property_read_u32(np, "upap-offset",
+                                                       &upap_offset)) {
+                               dev_dbg(&pdev->dev, "No upap offset found\n");
+                               upap_offset = -1;
+                       }
+                       list = (struct mailbox **)of_match_device(
+                                       dbx500_mailbox_match, &pdev->dev)->data;
+                       if (!list) {
+                               /* No mailbox configuration */
+                               dev_err(&pdev->dev, "No configuration found\n");
+                               return -ENODEV;
+                       }
+               } else {
+                       /* No mailbox configuration */
+                       dev_err(&pdev->dev, "No configuration found\n");
+                       return -ENODEV;
+               }
+       } else {
+               list = (struct mailbox **)platid->driver_data;
+               legacy_offset = pdata->legacy_offset;
+               upap_offset = pdata->upap_offset;
+       }
+
+       mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcm-reg");
+       mbox_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+       if (!mbox_base) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm");
+       tcdm_mem_base = devm_ioremap(&pdev->dev, mem->start,
+                                                       resource_size(mem));
+       if (!tcdm_mem_base) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       /* check mailbox offset values */
+       if (legacy_offset >= (resource_size(mem) - PRCM_MBOX_LEGACY_SIZE)) {
+               dev_err(&pdev->dev, "Incorrect legacy offset value\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       for (i = 0; list[i]; i++) {
+               struct mailbox *mbox = list[i];
+               struct dbx500_mbox_priv *priv =
+                       (struct dbx500_mbox_priv *)mbox->priv;
+               if (!priv)
+                       continue;
+               if (priv->type == MAILBOX_UPAP) {
+                       upap_used = true;
+                       upap_size += priv->rx_size;
+                       break;
+               }
+       }
+
+       if (upap_used && (upap_offset >= (resource_size(mem) - upap_size))) {
+               dev_err(&pdev->dev, "Incorrect upap offset value\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       /* clean all mailboxes */
+       mbox_write_reg(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
+       ret = request_irq(irq, dbx500_mbox_irq_handler,
+                       IRQF_NO_SUSPEND, "db8500_mbox", NULL);
+       if (ret) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       dbx500_mbox_irq_domain = irq_domain_add_linear(
+               np, NUM_MB, &dbx500_mbox_irq_ops, NULL);
+
+       if (!dbx500_mbox_irq_domain) {
+               dev_err(&pdev->dev, "Failed to create irqdomain\n");
+               ret = -ENOSYS;
+               goto irq_free;
+       }
+
+       /*
+        * Update mailbox shared memory buffer offset according to mailbox
+        * type
+        */
+       for (i = 0; list[i]; i++) {
+               struct mailbox *mbox = list[i];
+               struct dbx500_mbox_priv *priv =
+                       (struct dbx500_mbox_priv *)mbox->priv;
+               if (!priv)
+                       continue;
+               mbox->irq = irq_create_mapping(dbx500_mbox_irq_domain,
+                                                               mbox->id);
+               if (priv->type == MAILBOX_LEGACY) {
+                       priv->rx_offset += legacy_offset;
+                       priv->rx_header_offset += legacy_offset;
+                       priv->tx_offset += legacy_offset;
+                       priv->tx_header_offset += legacy_offset;
+               } else if (priv->type == MAILBOX_UPAP) {
+                       priv->tx_offset += upap_offset;
+                       priv->rx_offset += upap_offset;
+               }
+       }
+
+       ret = mailbox_register(&pdev->dev, list);
+irq_free:
+       if (ret)
+               free_irq(irq, NULL);
+
+out:
+       return ret;
+}
+
+static int dbx500_mbox_remove(struct platform_device *pdev)
+{
+       mailbox_unregister();
+       iounmap(mbox_base);
+       return 0;
+}
+
+static const struct platform_device_id dbx500_mbox_id[] = {
+       {
+               .name = "db8500-mailbox",
+               .driver_data = (unsigned long) db8500_mboxes,
+       }, {
+               .name = "db9540-mailbox",
+               .driver_data = (unsigned long) dbx540_mboxes,
+       }, {
+               /* sentinel */
+       }
+};
+
+static struct platform_driver dbx500_mbox_driver = {
+       .probe = dbx500_mbox_probe,
+       .remove = dbx500_mbox_remove,
+       .driver = {
+               .name = "dbx500-mailbox",
+               .of_match_table = dbx500_mailbox_match,
+       },
+       .id_table = dbx500_mbox_id,
+};
+
+static int __init dbx500_mbox_init(void)
+{
+       return platform_driver_register(&dbx500_mbox_driver);
+}
+
+static void __exit dbx500_mbox_exit(void)
+{
+       platform_driver_unregister(&dbx500_mbox_driver);
+}
+
+postcore_initcall(dbx500_mbox_init);
+module_exit(dbx500_mbox_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ST-Ericsson mailbox: dbx500 architecture specific functions");
+MODULE_AUTHOR("Loic Pallardy <loic.pallardy@st.com>");
+MODULE_ALIAS("platform:dbx500-mailbox");
similarity index 64%
rename from arch/arm/mach-omap1/mailbox.c
rename to drivers/mailbox/mailbox-omap1.c
index efc8f207f6fcd7845065b569f835764ff47e9ba5..44418726d23db6222e910fcb35ddde3a10f4f4de 100644 (file)
@@ -13,7 +13,9 @@
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
-#include <plat/mailbox.h>
+#include <linux/delay.h>
+
+#include "mailbox_internal.h"
 
 #define MAILBOX_ARM2DSP1               0x00
 #define MAILBOX_ARM2DSP1b              0x04
@@ -36,6 +38,8 @@ struct omap_mbox1_fifo {
 struct omap_mbox1_priv {
        struct omap_mbox1_fifo tx_fifo;
        struct omap_mbox1_fifo rx_fifo;
+       u32 data;
+       bool empty_flag;
 };
 
 static inline int mbox_read_reg(size_t ofs)
@@ -49,34 +53,40 @@ static inline void mbox_write_reg(u32 val, size_t ofs)
 }
 
 /* msg */
-static mbox_msg_t omap1_mbox_fifo_read(struct omap_mbox *mbox)
+static void omap1_mbox_fifo_read(struct mailbox *mbox, struct mailbox_msg *msg)
 {
-       struct omap_mbox1_fifo *fifo =
-               &((struct omap_mbox1_priv *)mbox->priv)->rx_fifo;
-       mbox_msg_t msg;
+       struct omap_mbox1_priv *priv = mbox->priv;
+       struct omap_mbox1_fifo *fifo = &priv->rx_fifo;
 
-       msg = mbox_read_reg(fifo->data);
-       msg |= ((mbox_msg_t) mbox_read_reg(fifo->cmd)) << 16;
-
-       return msg;
+       priv->data = mbox_read_reg(fifo->data);
+       priv->data |= ((u32) mbox_read_reg(fifo->cmd)) << 16;
+       MAILBOX_FILL_MSG((*msg), 0, priv->data, 0);
+       priv->empty_flag = false;
 }
 
-static void
-omap1_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
+static int
+omap1_mbox_fifo_write(struct mailbox *mbox, struct mailbox_msg *msg)
 {
        struct omap_mbox1_fifo *fifo =
                &((struct omap_mbox1_priv *)mbox->priv)->tx_fifo;
+       u32 data = (u32)msg->pdata;
+
+       mbox_write_reg(data & 0xffff, fifo->data);
+       mbox_write_reg(data >> 16, fifo->cmd);
 
-       mbox_write_reg(msg & 0xffff, fifo->data);
-       mbox_write_reg(msg >> 16, fifo->cmd);
+       return 0;
 }
 
-static int omap1_mbox_fifo_empty(struct omap_mbox *mbox)
+static int omap1_mbox_fifo_empty(struct mailbox *mbox)
 {
-       return 0;
+       struct omap_mbox1_priv *priv = (struct omap_mbox1_priv *)mbox->priv;
+       if (priv->empty_flag)
+               return 0;
+       else
+               return 1;
 }
 
-static int omap1_mbox_fifo_full(struct omap_mbox *mbox)
+static int omap1_mbox_fifo_full(struct mailbox *mbox)
 {
        struct omap_mbox1_fifo *fifo =
                &((struct omap_mbox1_priv *)mbox->priv)->rx_fifo;
@@ -84,35 +94,52 @@ static int omap1_mbox_fifo_full(struct omap_mbox *mbox)
        return mbox_read_reg(fifo->flag);
 }
 
+static int omap1_mbox_poll_for_space(struct mailbox *mbox)
+{
+       int ret = 0, i = 1000;
+
+       while (omap1_mbox_fifo_full(mbox)) {
+               if (--i == 0)
+                       return -1;
+               udelay(1);
+       }
+       return ret;
+}
+
 /* irq */
 static void
-omap1_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_type_t irq)
+omap1_mbox_enable_irq(struct mailbox *mbox, mailbox_type_t irq)
 {
        if (irq == IRQ_RX)
                enable_irq(mbox->irq);
 }
 
 static void
-omap1_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_type_t irq)
+omap1_mbox_disable_irq(struct mailbox *mbox, mailbox_type_t irq)
 {
        if (irq == IRQ_RX)
                disable_irq(mbox->irq);
 }
 
 static int
-omap1_mbox_is_irq(struct omap_mbox *mbox, omap_mbox_type_t irq)
+omap1_mbox_is_irq(struct mailbox *mbox, mailbox_type_t irq)
 {
+       struct omap_mbox1_priv *priv = (struct omap_mbox1_priv *)mbox->priv;
+
        if (irq == IRQ_TX)
                return 0;
+       if (irq == IRQ_RX)
+               priv->empty_flag = true;
+
        return 1;
 }
 
-static struct omap_mbox_ops omap1_mbox_ops = {
-       .type           = OMAP_MBOX_TYPE1,
-       .fifo_read      = omap1_mbox_fifo_read,
-       .fifo_write     = omap1_mbox_fifo_write,
-       .fifo_empty     = omap1_mbox_fifo_empty,
-       .fifo_full      = omap1_mbox_fifo_full,
+static struct mailbox_ops omap1_mbox_ops = {
+       .type           = MBOX_HW_FIFO1_TYPE,
+       .read           = omap1_mbox_fifo_read,
+       .write          = omap1_mbox_fifo_write,
+       .empty          = omap1_mbox_fifo_empty,
+       .poll_for_space = omap1_mbox_poll_for_space,
        .enable_irq     = omap1_mbox_enable_irq,
        .disable_irq    = omap1_mbox_disable_irq,
        .is_irq         = omap1_mbox_is_irq,
@@ -134,29 +161,32 @@ static struct omap_mbox1_priv omap1_mbox_dsp_priv = {
        },
 };
 
-static struct omap_mbox mbox_dsp_info = {
+static struct mailbox mbox_dsp_info = {
        .name   = "dsp",
        .ops    = &omap1_mbox_ops,
        .priv   = &omap1_mbox_dsp_priv,
 };
 
-static struct omap_mbox *omap1_mboxes[] = { &mbox_dsp_info, NULL };
+static struct mailbox *omap1_mboxes[] = { &mbox_dsp_info, NULL };
 
 static int omap1_mbox_probe(struct platform_device *pdev)
 {
        struct resource *mem;
        int ret;
-       struct omap_mbox **list;
+       struct mailbox **list;
 
        list = omap1_mboxes;
        list[0]->irq = platform_get_irq_byname(pdev, "dsp");
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem)
+               return -ENOMEM;
+
        mbox_base = ioremap(mem->start, resource_size(mem));
        if (!mbox_base)
                return -ENOMEM;
 
-       ret = omap_mbox_register(&pdev->dev, list);
+       ret = mailbox_register(&pdev->dev, list);
        if (ret) {
                iounmap(mbox_base);
                return ret;
@@ -167,7 +197,7 @@ static int omap1_mbox_probe(struct platform_device *pdev)
 
 static int omap1_mbox_remove(struct platform_device *pdev)
 {
-       omap_mbox_unregister();
+       mailbox_unregister();
        iounmap(mbox_base);
        return 0;
 }
similarity index 50%
rename from arch/arm/mach-omap2/mailbox.c
rename to drivers/mailbox/mailbox-omap2.c
index 0b080267b7f6355dac3b9adcd8b732b7cc44f936..4409d02e498a5d6b5624075494f6a35b4a037b6c 100644 (file)
  */
 
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/pm_runtime.h>
+#include <linux/platform_data/mailbox-omap.h>
 
-#include <plat/mailbox.h>
-
-#include "soc.h"
+#include "mailbox_internal.h"
 
 #define MAILBOX_REVISION               0x000
 #define MAILBOX_MESSAGE(m)             (0x040 + 4 * (m))
@@ -59,10 +59,12 @@ struct omap_mbox2_priv {
        u32 notfull_bit;
        u32 ctx[OMAP4_MBOX_NR_REGS];
        unsigned long irqdisable;
+       u32 intr_type;
+       u32 data;
 };
 
-static void omap2_mbox_enable_irq(struct omap_mbox *mbox,
-                                 omap_mbox_type_t irq);
+static void omap2_mbox_enable_irq(struct mailbox *mbox,
+               mailbox_type_t irq);
 
 static inline unsigned int mbox_read_reg(size_t ofs)
 {
@@ -75,7 +77,7 @@ static inline void mbox_write_reg(u32 val, size_t ofs)
 }
 
 /* Mailbox H/W preparations */
-static int omap2_mbox_startup(struct omap_mbox *mbox)
+static int omap2_mbox_startup(struct mailbox *mbox)
 {
        u32 l;
 
@@ -88,44 +90,72 @@ static int omap2_mbox_startup(struct omap_mbox *mbox)
        return 0;
 }
 
-static void omap2_mbox_shutdown(struct omap_mbox *mbox)
+static void omap2_mbox_shutdown(struct mailbox *mbox)
 {
        pm_runtime_put_sync(mbox->dev->parent);
        pm_runtime_disable(mbox->dev->parent);
 }
 
 /* Mailbox FIFO handle functions */
-static mbox_msg_t omap2_mbox_fifo_read(struct omap_mbox *mbox)
+static void omap2_mbox_fifo_read(struct mailbox *mbox, struct mailbox_msg *msg)
 {
-       struct omap_mbox2_fifo *fifo =
-               &((struct omap_mbox2_priv *)mbox->priv)->rx_fifo;
-       return (mbox_msg_t) mbox_read_reg(fifo->msg);
+       struct omap_mbox2_priv *priv = mbox->priv;
+       struct omap_mbox2_fifo *fifo = &priv->rx_fifo;
+       priv->data = mbox_read_reg(fifo->msg);
+       MAILBOX_FILL_MSG((*msg), 0, priv->data, 0);
 }
 
-static void omap2_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
+static int omap2_mbox_fifo_write(struct mailbox *mbox, struct mailbox_msg *msg)
 {
        struct omap_mbox2_fifo *fifo =
                &((struct omap_mbox2_priv *)mbox->priv)->tx_fifo;
-       mbox_write_reg(msg, fifo->msg);
+
+       mbox_write_reg((u32)msg->pdata, fifo->msg);
+
+       return 0;
 }
 
-static int omap2_mbox_fifo_empty(struct omap_mbox *mbox)
+static int omap2_mbox_fifo_empty(struct mailbox *mbox)
 {
        struct omap_mbox2_fifo *fifo =
                &((struct omap_mbox2_priv *)mbox->priv)->rx_fifo;
        return (mbox_read_reg(fifo->msg_stat) == 0);
 }
 
-static int omap2_mbox_fifo_full(struct omap_mbox *mbox)
+static int omap2_mbox_fifo_full(struct mailbox *mbox)
 {
        struct omap_mbox2_fifo *fifo =
                &((struct omap_mbox2_priv *)mbox->priv)->tx_fifo;
        return mbox_read_reg(fifo->fifo_stat);
 }
 
+static int omap2_mbox_needs_flush(struct mailbox *mbox)
+{
+       struct omap_mbox2_priv *priv = mbox->priv;
+       struct omap_mbox2_fifo *fifo = &priv->tx_fifo;
+       return mbox_read_reg(fifo->msg_stat);
+}
+
+static void omap2_mbox_fifo_readback(struct mailbox *mbox,
+                                       struct mailbox_msg *msg)
+{
+       struct omap_mbox2_priv *priv = mbox->priv;
+       struct omap_mbox2_fifo *fifo = &priv->tx_fifo;
+       priv->data = mbox_read_reg(fifo->msg);
+       MAILBOX_FILL_MSG((*msg), 0, priv->data, 0);
+}
+
+static int omap2_mbox_poll_for_space(struct mailbox *mbox)
+{
+       if (omap2_mbox_fifo_full(mbox))
+               return -1;
+
+       return 0;
+}
+
 /* Mailbox IRQ handle functions */
-static void omap2_mbox_enable_irq(struct omap_mbox *mbox,
-               omap_mbox_type_t irq)
+static void omap2_mbox_enable_irq(struct mailbox *mbox,
+               mailbox_type_t irq)
 {
        struct omap_mbox2_priv *p = mbox->priv;
        u32 l, bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
@@ -135,20 +165,20 @@ static void omap2_mbox_enable_irq(struct omap_mbox *mbox,
        mbox_write_reg(l, p->irqenable);
 }
 
-static void omap2_mbox_disable_irq(struct omap_mbox *mbox,
-               omap_mbox_type_t irq)
+static void omap2_mbox_disable_irq(struct mailbox *mbox,
+               mailbox_type_t irq)
 {
        struct omap_mbox2_priv *p = mbox->priv;
        u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
 
-       if (!cpu_is_omap44xx())
+       if (!p->intr_type)
                bit = mbox_read_reg(p->irqdisable) & ~bit;
 
        mbox_write_reg(bit, p->irqdisable);
 }
 
-static void omap2_mbox_ack_irq(struct omap_mbox *mbox,
-               omap_mbox_type_t irq)
+static void omap2_mbox_ack_irq(struct mailbox *mbox,
+               mailbox_type_t irq)
 {
        struct omap_mbox2_priv *p = mbox->priv;
        u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
@@ -159,8 +189,8 @@ static void omap2_mbox_ack_irq(struct omap_mbox *mbox,
        mbox_read_reg(p->irqstatus);
 }
 
-static int omap2_mbox_is_irq(struct omap_mbox *mbox,
-               omap_mbox_type_t irq)
+static int omap2_mbox_is_irq(struct mailbox *mbox,
+               mailbox_type_t irq)
 {
        struct omap_mbox2_priv *p = mbox->priv;
        u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
@@ -170,12 +200,13 @@ static int omap2_mbox_is_irq(struct omap_mbox *mbox,
        return (int)(enable & status & bit);
 }
 
-static void omap2_mbox_save_ctx(struct omap_mbox *mbox)
+static void omap2_mbox_save_ctx(struct mailbox *mbox)
 {
        int i;
        struct omap_mbox2_priv *p = mbox->priv;
        int nr_regs;
-       if (cpu_is_omap44xx())
+
+       if (p->intr_type)
                nr_regs = OMAP4_MBOX_NR_REGS;
        else
                nr_regs = MBOX_NR_REGS;
@@ -183,16 +214,17 @@ static void omap2_mbox_save_ctx(struct omap_mbox *mbox)
                p->ctx[i] = mbox_read_reg(i * sizeof(u32));
 
                dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__,
-                       i, p->ctx[i]);
+                               i, p->ctx[i]);
        }
 }
 
-static void omap2_mbox_restore_ctx(struct omap_mbox *mbox)
+static void omap2_mbox_restore_ctx(struct mailbox *mbox)
 {
        int i;
        struct omap_mbox2_priv *p = mbox->priv;
        int nr_regs;
-       if (cpu_is_omap44xx())
+
+       if (p->intr_type)
                nr_regs = OMAP4_MBOX_NR_REGS;
        else
                nr_regs = MBOX_NR_REGS;
@@ -200,18 +232,20 @@ static void omap2_mbox_restore_ctx(struct omap_mbox *mbox)
                mbox_write_reg(p->ctx[i], i * sizeof(u32));
 
                dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__,
-                       i, p->ctx[i]);
+                               i, p->ctx[i]);
        }
 }
 
-static struct omap_mbox_ops omap2_mbox_ops = {
-       .type           = OMAP_MBOX_TYPE2,
+static struct mailbox_ops omap2_mbox_ops = {
+       .type           = MBOX_HW_FIFO2_TYPE,
        .startup        = omap2_mbox_startup,
        .shutdown       = omap2_mbox_shutdown,
-       .fifo_read      = omap2_mbox_fifo_read,
-       .fifo_write     = omap2_mbox_fifo_write,
-       .fifo_empty     = omap2_mbox_fifo_empty,
-       .fifo_full      = omap2_mbox_fifo_full,
+       .read           = omap2_mbox_fifo_read,
+       .write          = omap2_mbox_fifo_write,
+       .empty          = omap2_mbox_fifo_empty,
+       .needs_flush    = omap2_mbox_needs_flush,
+       .readback       = omap2_mbox_fifo_readback,
+       .poll_for_space = omap2_mbox_poll_for_space,
        .enable_irq     = omap2_mbox_enable_irq,
        .disable_irq    = omap2_mbox_disable_irq,
        .ack_irq        = omap2_mbox_ack_irq,
@@ -220,192 +254,117 @@ static struct omap_mbox_ops omap2_mbox_ops = {
        .restore_ctx    = omap2_mbox_restore_ctx,
 };
 
-/*
- * MAILBOX 0: ARM -> DSP,
- * MAILBOX 1: ARM <- DSP.
- * MAILBOX 2: ARM -> IVA,
- * MAILBOX 3: ARM <- IVA.
- */
-
-/* FIXME: the following structs should be filled automatically by the user id */
-
-#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP2)
-/* DSP */
-static struct omap_mbox2_priv omap2_mbox_dsp_priv = {
-       .tx_fifo = {
-               .msg            = MAILBOX_MESSAGE(0),
-               .fifo_stat      = MAILBOX_FIFOSTATUS(0),
-       },
-       .rx_fifo = {
-               .msg            = MAILBOX_MESSAGE(1),
-               .msg_stat       = MAILBOX_MSGSTATUS(1),
-       },
-       .irqenable      = MAILBOX_IRQENABLE(0),
-       .irqstatus      = MAILBOX_IRQSTATUS(0),
-       .notfull_bit    = MAILBOX_IRQ_NOTFULL(0),
-       .newmsg_bit     = MAILBOX_IRQ_NEWMSG(1),
-       .irqdisable     = MAILBOX_IRQENABLE(0),
-};
-
-struct omap_mbox mbox_dsp_info = {
-       .name   = "dsp",
-       .ops    = &omap2_mbox_ops,
-       .priv   = &omap2_mbox_dsp_priv,
-};
-#endif
-
-#if defined(CONFIG_ARCH_OMAP3)
-struct omap_mbox *omap3_mboxes[] = { &mbox_dsp_info, NULL };
-#endif
-
-#if defined(CONFIG_SOC_OMAP2420)
-/* IVA */
-static struct omap_mbox2_priv omap2_mbox_iva_priv = {
-       .tx_fifo = {
-               .msg            = MAILBOX_MESSAGE(2),
-               .fifo_stat      = MAILBOX_FIFOSTATUS(2),
-       },
-       .rx_fifo = {
-               .msg            = MAILBOX_MESSAGE(3),
-               .msg_stat       = MAILBOX_MSGSTATUS(3),
-       },
-       .irqenable      = MAILBOX_IRQENABLE(3),
-       .irqstatus      = MAILBOX_IRQSTATUS(3),
-       .notfull_bit    = MAILBOX_IRQ_NOTFULL(2),
-       .newmsg_bit     = MAILBOX_IRQ_NEWMSG(3),
-       .irqdisable     = MAILBOX_IRQENABLE(3),
-};
-
-static struct omap_mbox mbox_iva_info = {
-       .name   = "iva",
-       .ops    = &omap2_mbox_ops,
-       .priv   = &omap2_mbox_iva_priv,
-};
-#endif
-
-#ifdef CONFIG_ARCH_OMAP2
-struct omap_mbox *omap2_mboxes[] = {
-       &mbox_dsp_info,
-#ifdef CONFIG_SOC_OMAP2420
-       &mbox_iva_info,
-#endif
-       NULL
-};
-#endif
-
-#if defined(CONFIG_ARCH_OMAP4)
-/* OMAP4 */
-static struct omap_mbox2_priv omap2_mbox_1_priv = {
-       .tx_fifo = {
-               .msg            = MAILBOX_MESSAGE(0),
-               .fifo_stat      = MAILBOX_FIFOSTATUS(0),
-       },
-       .rx_fifo = {
-               .msg            = MAILBOX_MESSAGE(1),
-               .msg_stat       = MAILBOX_MSGSTATUS(1),
-       },
-       .irqenable      = OMAP4_MAILBOX_IRQENABLE(0),
-       .irqstatus      = OMAP4_MAILBOX_IRQSTATUS(0),
-       .notfull_bit    = MAILBOX_IRQ_NOTFULL(0),
-       .newmsg_bit     = MAILBOX_IRQ_NEWMSG(1),
-       .irqdisable     = OMAP4_MAILBOX_IRQENABLE_CLR(0),
-};
-
-struct omap_mbox mbox_1_info = {
-       .name   = "mailbox-1",
-       .ops    = &omap2_mbox_ops,
-       .priv   = &omap2_mbox_1_priv,
-};
-
-static struct omap_mbox2_priv omap2_mbox_2_priv = {
-       .tx_fifo = {
-               .msg            = MAILBOX_MESSAGE(3),
-               .fifo_stat      = MAILBOX_FIFOSTATUS(3),
-       },
-       .rx_fifo = {
-               .msg            = MAILBOX_MESSAGE(2),
-               .msg_stat       = MAILBOX_MSGSTATUS(2),
-       },
-       .irqenable      = OMAP4_MAILBOX_IRQENABLE(0),
-       .irqstatus      = OMAP4_MAILBOX_IRQSTATUS(0),
-       .notfull_bit    = MAILBOX_IRQ_NOTFULL(3),
-       .newmsg_bit     = MAILBOX_IRQ_NEWMSG(2),
-       .irqdisable     = OMAP4_MAILBOX_IRQENABLE_CLR(0),
-};
-
-struct omap_mbox mbox_2_info = {
-       .name   = "mailbox-2",
-       .ops    = &omap2_mbox_ops,
-       .priv   = &omap2_mbox_2_priv,
-};
-
-struct omap_mbox *omap4_mboxes[] = { &mbox_1_info, &mbox_2_info, NULL };
-#endif
-
 static int omap2_mbox_probe(struct platform_device *pdev)
 {
        struct resource *mem;
        int ret;
-       struct omap_mbox **list;
-
-       if (false)
-               ;
-#if defined(CONFIG_ARCH_OMAP3)
-       else if (cpu_is_omap34xx()) {
-               list = omap3_mboxes;
+       struct mailbox **list, *mbox, *mboxblk;
+       struct omap_mbox2_priv *priv, *privblk;
+       struct omap_mbox_pdata *pdata = pdev->dev.platform_data;
+       struct omap_mbox_dev_info *info;
+       int i;
 
-               list[0]->irq = platform_get_irq(pdev, 0);
+       if (!pdata || !pdata->info_cnt || !pdata->info) {
+               pr_err("%s: platform not supported\n", __func__);
+               return -ENODEV;
        }
-#endif
-#if defined(CONFIG_ARCH_OMAP2)
-       else if (cpu_is_omap2430()) {
-               list = omap2_mboxes;
 
-               list[0]->irq = platform_get_irq(pdev, 0);
-       } else if (cpu_is_omap2420()) {
-               list = omap2_mboxes;
+       /* allocate one extra for marking end of list */
+       list = kzalloc((pdata->info_cnt + 1) * sizeof(*list), GFP_KERNEL);
+       if (!list)
+               return -ENOMEM;
 
-               list[0]->irq = platform_get_irq_byname(pdev, "dsp");
-               list[1]->irq = platform_get_irq_byname(pdev, "iva");
+       mboxblk = mbox = kzalloc(pdata->info_cnt * sizeof(*mbox), GFP_KERNEL);
+       if (!mboxblk) {
+               ret = -ENOMEM;
+               goto free_list;
        }
-#endif
-#if defined(CONFIG_ARCH_OMAP4)
-       else if (cpu_is_omap44xx()) {
-               list = omap4_mboxes;
 
-               list[0]->irq = list[1]->irq = platform_get_irq(pdev, 0);
+       privblk = priv = kzalloc(pdata->info_cnt * sizeof(*priv), GFP_KERNEL);
+       if (!privblk) {
+               ret = -ENOMEM;
+               goto free_mboxblk;
        }
-#endif
-       else {
-               pr_err("%s: platform not supported\n", __func__);
-               return -ENODEV;
+
+       info = pdata->info;
+       for (i = 0; i < pdata->info_cnt; i++, info++, priv++) {
+               priv->tx_fifo.msg = MAILBOX_MESSAGE(info->tx_id);
+               priv->tx_fifo.fifo_stat = MAILBOX_FIFOSTATUS(info->tx_id);
+               priv->tx_fifo.msg_stat = MAILBOX_MSGSTATUS(info->tx_id);
+               priv->rx_fifo.msg =  MAILBOX_MESSAGE(info->rx_id);
+               priv->rx_fifo.msg_stat =  MAILBOX_MSGSTATUS(info->rx_id);
+               priv->notfull_bit = MAILBOX_IRQ_NOTFULL(info->tx_id);
+               priv->newmsg_bit = MAILBOX_IRQ_NEWMSG(info->rx_id);
+               if (pdata->intr_type) {
+                       priv->irqenable = OMAP4_MAILBOX_IRQENABLE(info->usr_id);
+                       priv->irqstatus = OMAP4_MAILBOX_IRQSTATUS(info->usr_id);
+                       priv->irqdisable =
+                               OMAP4_MAILBOX_IRQENABLE_CLR(info->usr_id);
+               } else {
+                       priv->irqenable = MAILBOX_IRQENABLE(info->usr_id);
+                       priv->irqstatus = MAILBOX_IRQSTATUS(info->usr_id);
+                       priv->irqdisable = MAILBOX_IRQENABLE(info->usr_id);
+               }
+               priv->intr_type = pdata->intr_type;
+
+               mbox->priv = priv;
+               mbox->name = info->name;
+               mbox->ops = &omap2_mbox_ops;
+               mbox->irq = platform_get_irq(pdev, info->irq_id);
+               list[i] = mbox++;
        }
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       mbox_base = ioremap(mem->start, resource_size(mem));
-       if (!mbox_base)
-               return -ENOMEM;
+       if (!mem) {
+               ret = -ENOMEM;
+               goto free_privblk;
+       }
 
-       ret = omap_mbox_register(&pdev->dev, list);
-       if (ret) {
-               iounmap(mbox_base);
-               return ret;
+       mbox_base = ioremap(mem->start, resource_size(mem));
+       if (!mbox_base) {
+               ret = -ENOMEM;
+               goto free_privblk;
        }
 
+       ret = mailbox_register(&pdev->dev, list);
+       if (ret)
+               goto unmap_mbox;
+       platform_set_drvdata(pdev, list);
+
        return 0;
+
+unmap_mbox:
+       iounmap(mbox_base);
+free_privblk:
+       kfree(privblk);
+free_mboxblk:
+       kfree(mboxblk);
+free_list:
+       kfree(list);
+       return ret;
 }
 
 static int omap2_mbox_remove(struct platform_device *pdev)
 {
-       omap_mbox_unregister();
+       struct omap_mbox2_priv *privblk;
+       struct mailbox **list = platform_get_drvdata(pdev);
+       struct mailbox *mboxblk = list[0];
+
+       privblk = mboxblk->priv;
+       mailbox_unregister();
        iounmap(mbox_base);
+       kfree(privblk);
+       kfree(mboxblk);
+       kfree(list);
+       platform_set_drvdata(pdev, NULL);
+
        return 0;
 }
 
 static struct platform_driver omap2_mbox_driver = {
-       .probe = omap2_mbox_probe,
-       .remove = omap2_mbox_remove,
-       .driver = {
+       .probe  = omap2_mbox_probe,
+       .remove = omap2_mbox_remove,
+       .driver = {
                .name = "omap-mailbox",
        },
 };
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
new file mode 100644 (file)
index 0000000..c46fa36
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ * Mailbox framework
+ *
+ * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
+ * Author: Loic Pallardy <loic.pallardy@st.com> for ST-Ericsson
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/kfifo.h>
+#include <linux/err.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+
+#include "mailbox_internal.h"
+
+static struct mailbox **mboxes;
+
+static int mbox_configured;
+static DEFINE_MUTEX(mbox_configured_lock);
+
+static unsigned int mbox_kfifo_size = CONFIG_MBOX_KFIFO_SIZE;
+module_param(mbox_kfifo_size, uint, S_IRUGO);
+MODULE_PARM_DESC(mbox_kfifo_size, "Size of mailbox kfifo (bytes)");
+
+/* Mailbox FIFO handle functions */
+static inline void mbox_read(struct mailbox *mbox, struct mailbox_msg *msg)
+{
+       mbox->ops->read(mbox, msg);
+}
+static inline int mbox_write(struct mailbox *mbox, struct mailbox_msg *msg)
+{
+       return mbox->ops->write(mbox, msg);
+}
+static inline int mbox_empty(struct mailbox *mbox)
+{
+       return mbox->ops->empty(mbox);
+}
+static inline int mbox_needs_flush(struct mailbox *mbox)
+{
+       return (mbox->ops->needs_flush ? mbox->ops->needs_flush(mbox) : 0);
+}
+static inline void mbox_fifo_readback(struct mailbox *mbox,
+                                       struct mailbox_msg *msg)
+{
+       if (mbox->ops->readback)
+               mbox->ops->readback(mbox, msg);
+}
+
+/* Mailbox IRQ handle functions */
+static inline void ack_mbox_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+       if (mbox->ops->ack_irq)
+               mbox->ops->ack_irq(mbox, irq);
+}
+static inline int is_mbox_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+       return mbox->ops->is_irq(mbox, irq);
+}
+
+/*
+ * message sender
+ */
+static int __mbox_poll_for_space(struct mailbox *mbox)
+{
+       return mbox->ops->poll_for_space(mbox);
+}
+
+int mailbox_msg_send(struct mailbox *mbox, struct mailbox_msg *msg)
+{
+       struct mailbox_queue *mq = mbox->txq;
+       int ret = 0, len;
+
+       mutex_lock(&mq->mlock);
+
+       if (kfifo_avail(&mq->fifo) < (sizeof(*msg) + msg->size)) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (kfifo_is_empty(&mq->fifo) && !__mbox_poll_for_space(mbox)) {
+               ret = mbox_write(mbox, msg);
+               goto out;
+       }
+
+       len = kfifo_in(&mq->fifo, (unsigned char *)msg, sizeof(*msg));
+       WARN_ON(len != sizeof(*msg));
+
+       if (msg->size && msg->pdata) {
+               len = kfifo_in(&mq->fifo, (unsigned char *)msg->pdata,
+                                                               msg->size);
+               WARN_ON(len != msg->size);
+       }
+
+       tasklet_schedule(&mbox->txq->tasklet);
+
+out:
+       mutex_unlock(&mq->mlock);
+       return ret;
+}
+EXPORT_SYMBOL(mailbox_msg_send);
+
+/*
+ * Empty the Tx FIFO by reading back the messages. This function
+ * is mainly for usecases where the receiver is capable of
+ * receiving the interrupt, but does not have access to the
+ * mailbox registers.
+ *
+ * Returns the no. of messages read back
+ */
+int mailbox_empty_tx(struct mailbox *mbox)
+{
+       int ret = 0;
+       struct mailbox_msg msg;
+
+       while (mbox_needs_flush(mbox)) {
+               mbox_fifo_readback(mbox, &msg);
+               ret++;
+       }
+
+       /* no more messages in the fifo, clear IRQ source */
+       if (ret)
+               ack_mbox_irq(mbox, IRQ_RX);
+
+       return ret;
+}
+EXPORT_SYMBOL(mailbox_empty_tx);
+
+#define TRANSFER_TIMEOUT 30000 /* Becomes ~3s timeout */
+
+static struct mailbox_msg no_irq_msg_res;
+
+struct mailbox_msg *mailbox_msg_send_receive_no_irq(struct mailbox *mbox,
+               struct mailbox_msg *msg)
+{
+       int ret = 0;
+       int count = 0;
+
+       BUG_ON(!irqs_disabled());
+
+       if (likely(mbox->ops->write && mbox->ops->read)) {
+               if (__mbox_poll_for_space(mbox)) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+               mbox->ops->write(mbox, msg);
+               while (!is_mbox_irq(mbox, IRQ_RX)) {
+                       udelay(100);
+                       cpu_relax();
+                       count++;
+                       if (count > TRANSFER_TIMEOUT) {
+                               pr_err("%s: Error: transfer timed out\n",
+                                               __func__);
+                               ret = -EINVAL;
+                               goto out;
+                       }
+               }
+               mbox->ops->read(mbox, &no_irq_msg_res);
+               ack_mbox_irq(mbox, IRQ_RX);
+       } else {
+               ret = -EINVAL;
+       }
+
+out:
+       BUG_ON(ret < 0);
+
+       return &no_irq_msg_res;
+}
+EXPORT_SYMBOL(mailbox_msg_send_receive_no_irq);
+
+int mailbox_msg_send_no_irq(struct mailbox *mbox,
+               struct mailbox_msg *msg)
+{
+       int ret = 0;
+
+       BUG_ON(!irqs_disabled());
+
+       if (likely(mbox->ops->write)) {
+               if (__mbox_poll_for_space(mbox)) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+               mbox->ops->write(mbox, msg);
+       } else {
+               ret = -EINVAL;
+       }
+
+out:
+       WARN_ON(ret < 0);
+
+       return ret;
+}
+EXPORT_SYMBOL(mailbox_msg_send_no_irq);
+
+void mailbox_save_ctx(struct mailbox *mbox)
+{
+       if (!mbox->ops->save_ctx) {
+               dev_err(mbox->dev, "%s:\tno save\n", __func__);
+               return;
+       }
+
+       mbox->ops->save_ctx(mbox);
+}
+EXPORT_SYMBOL(mailbox_save_ctx);
+
+void mailbox_restore_ctx(struct mailbox *mbox)
+{
+       if (!mbox->ops->restore_ctx) {
+               dev_err(mbox->dev, "%s:\tno restore\n", __func__);
+               return;
+       }
+
+       mbox->ops->restore_ctx(mbox);
+}
+EXPORT_SYMBOL(mailbox_restore_ctx);
+
+void mailbox_enable_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+       mbox->ops->enable_irq(mbox, irq);
+}
+EXPORT_SYMBOL(mailbox_enable_irq);
+
+void mailbox_disable_irq(struct mailbox *mbox, mailbox_irq_t irq)
+{
+       mbox->ops->disable_irq(mbox, irq);
+}
+EXPORT_SYMBOL(mailbox_disable_irq);
+
+static void mbox_tx_tasklet(unsigned long tx_data)
+{
+       struct mailbox *mbox = (struct mailbox *)tx_data;
+       struct mailbox_queue *mq = mbox->txq;
+       struct mailbox_msg msg;
+       int ret;
+       unsigned char tx_data_buf[CONFIG_MBOX_DATA_SIZE];
+
+       while (kfifo_len(&mq->fifo)) {
+               if (__mbox_poll_for_space(mbox)) {
+                       mailbox_enable_irq(mbox, IRQ_TX);
+                       break;
+               }
+
+               ret = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
+               WARN_ON(ret != sizeof(msg));
+
+               if (msg.size) {
+                       ret = kfifo_out(&mq->fifo, tx_data_buf,
+                                                       sizeof(msg.size));
+                       WARN_ON(ret != msg.size);
+                       msg.pdata = tx_data_buf;
+               }
+
+               ret = mbox_write(mbox, &msg);
+               WARN_ON(ret);
+       }
+}
+
+/*
+ * Message receiver(workqueue)
+ */
+static unsigned char rx_work_data[CONFIG_MBOX_DATA_SIZE];
+
+static void mbox_rx_work(struct work_struct *work)
+{
+       struct mailbox_queue *mq =
+               container_of(work, struct mailbox_queue, work);
+       int len;
+       struct mailbox *mbox = mq->mbox;
+       struct mailbox_msg msg;
+
+       while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
+               len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
+               WARN_ON(len != sizeof(msg));
+
+               if (msg.size) {
+                       len = kfifo_out(&mq->fifo, rx_work_data, msg.size);
+                       WARN_ON(len != msg.size);
+                       msg.pdata = rx_work_data;
+               }
+
+               blocking_notifier_call_chain(&mbox->notifier, len,
+                                                               (void *)&msg);
+               spin_lock_irq(&mq->lock);
+               if (mq->full) {
+                       mq->full = false;
+                       mailbox_enable_irq(mbox, IRQ_RX);
+               }
+               spin_unlock_irq(&mq->lock);
+       }
+}
+
+/*
+ * Mailbox interrupt handler
+ */
+static void __mbox_tx_interrupt(struct mailbox *mbox)
+{
+       mailbox_disable_irq(mbox, IRQ_TX);
+       ack_mbox_irq(mbox, IRQ_TX);
+       tasklet_schedule(&mbox->txq->tasklet);
+}
+
+static void __mbox_rx_interrupt(struct mailbox *mbox)
+{
+       struct mailbox_queue *mq = mbox->rxq;
+       struct mailbox_msg msg;
+       int len;
+
+       while (!mbox_empty(mbox)) {
+               if (unlikely(kfifo_avail(&mq->fifo) <
+                               (sizeof(msg) + CONFIG_MBOX_DATA_SIZE))) {
+                       mailbox_disable_irq(mbox, IRQ_RX);
+                       mq->full = true;
+                       goto nomem;
+               }
+
+               mbox_read(mbox, &msg);
+
+               len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
+               WARN_ON(len != sizeof(msg));
+
+               if (msg.pdata && msg.size) {
+                       len = kfifo_in(&mq->fifo, (unsigned char *)msg.pdata,
+                                       msg.size);
+                       WARN_ON(len != msg.size);
+               }
+       }
+
+       /* no more messages in the fifo. clear IRQ source. */
+       ack_mbox_irq(mbox, IRQ_RX);
+nomem:
+       schedule_work(&mbox->rxq->work);
+}
+
+static irqreturn_t mbox_interrupt(int irq, void *p)
+{
+       struct mailbox *mbox = p;
+
+       if (is_mbox_irq(mbox, IRQ_TX))
+               __mbox_tx_interrupt(mbox);
+
+       if (is_mbox_irq(mbox, IRQ_RX))
+               __mbox_rx_interrupt(mbox);
+
+       return IRQ_HANDLED;
+}
+
+static struct mailbox_queue *mbox_queue_alloc(struct mailbox *mbox,
+               void (*work) (struct work_struct *),
+               void (*tasklet)(unsigned long))
+{
+       struct mailbox_queue *mq;
+
+       mq = kzalloc(sizeof(struct mailbox_queue), GFP_KERNEL);
+       if (!mq)
+               return NULL;
+
+       spin_lock_init(&mq->lock);
+       mutex_init(&mq->mlock);
+
+       if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL))
+               goto error;
+
+       if (work)
+               INIT_WORK(&mq->work, work);
+
+       if (tasklet)
+               tasklet_init(&mq->tasklet, tasklet, (unsigned long)mbox);
+       return mq;
+error:
+       kfree(mq);
+       return NULL;
+}
+
+static void mbox_queue_free(struct mailbox_queue *q)
+{
+       kfifo_free(&q->fifo);
+       kfree(q);
+}
+
+static int mailbox_startup(struct mailbox *mbox)
+{
+       int ret = 0;
+       struct mailbox_queue *mq;
+
+       mutex_lock(&mbox_configured_lock);
+       if (!mbox_configured++) {
+               if (likely(mbox->ops->startup)) {
+                       ret = mbox->ops->startup(mbox);
+                       if (unlikely(ret))
+                               goto fail_startup;
+               } else
+                       goto fail_startup;
+       }
+
+       if (!mbox->use_count++) {
+               mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet);
+               if (!mq) {
+                       ret = -ENOMEM;
+                       goto fail_alloc_txq;
+               }
+               mbox->txq = mq;
+
+               mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL);
+               if (!mq) {
+                       ret = -ENOMEM;
+                       goto fail_alloc_rxq;
+               }
+               mbox->rxq = mq;
+               mq->mbox = mbox;
+               ret = request_irq(mbox->irq, mbox_interrupt,
+                               IRQF_SHARED | IRQF_NO_SUSPEND,
+                               mbox->name, mbox);
+               if (unlikely(ret)) {
+                       pr_err("failed to register mailbox interrupt:%d\n",
+                                       ret);
+                       goto fail_request_irq;
+               }
+
+               mailbox_enable_irq(mbox, IRQ_RX);
+       }
+       mutex_unlock(&mbox_configured_lock);
+       return 0;
+
+fail_request_irq:
+       mbox_queue_free(mbox->rxq);
+fail_alloc_rxq:
+       mbox_queue_free(mbox->txq);
+fail_alloc_txq:
+       if (mbox->ops->shutdown)
+               mbox->ops->shutdown(mbox);
+       mbox->use_count--;
+fail_startup:
+       mbox_configured--;
+       mutex_unlock(&mbox_configured_lock);
+       return ret;
+}
+
+static void mailbox_fini(struct mailbox *mbox)
+{
+       mutex_lock(&mbox_configured_lock);
+
+       if (!--mbox->use_count) {
+               mailbox_disable_irq(mbox, IRQ_RX);
+               free_irq(mbox->irq, mbox);
+               tasklet_kill(&mbox->txq->tasklet);
+               flush_work(&mbox->rxq->work);
+               mbox_queue_free(mbox->txq);
+               mbox_queue_free(mbox->rxq);
+       }
+
+       if (likely(mbox->ops->shutdown)) {
+               if (!--mbox_configured)
+                       mbox->ops->shutdown(mbox);
+       }
+
+       mutex_unlock(&mbox_configured_lock);
+}
+
+struct mailbox *mailbox_get(const char *name, struct notifier_block *nb)
+{
+       struct mailbox *_mbox, *mbox = NULL;
+       int i, ret;
+
+       if (!mboxes)
+               return ERR_PTR(-EINVAL);
+
+       for (i = 0; (_mbox = mboxes[i]); i++) {
+               if (!strcmp(_mbox->name, name)) {
+                       mbox = _mbox;
+                       break;
+               }
+       }
+
+       if (!mbox)
+               return ERR_PTR(-ENOENT);
+
+       if (nb)
+               blocking_notifier_chain_register(&mbox->notifier, nb);
+
+       ret = mailbox_startup(mbox);
+       if (ret) {
+               blocking_notifier_chain_unregister(&mbox->notifier, nb);
+               return ERR_PTR(-ENODEV);
+       }
+
+       return mbox;
+}
+EXPORT_SYMBOL(mailbox_get);
+
+void mailbox_put(struct mailbox *mbox, struct notifier_block *nb)
+{
+       if (nb)
+               blocking_notifier_chain_unregister(&mbox->notifier, nb);
+       mailbox_fini(mbox);
+}
+EXPORT_SYMBOL(mailbox_put);
+
+static struct class mailbox_class = { .name = "mbox", };
+
+int mailbox_register(struct device *parent, struct mailbox **list)
+{
+       int ret;
+       int i;
+
+       mboxes = list;
+       if (!mboxes)
+               return -EINVAL;
+
+       for (i = 0; mboxes[i]; i++) {
+               struct mailbox *mbox = mboxes[i];
+               mbox->dev = device_create(&mailbox_class,
+                               parent, 0, mbox, "%s", mbox->name);
+               if (IS_ERR(mbox->dev)) {
+                       ret = PTR_ERR(mbox->dev);
+                       goto err_out;
+               }
+
+               BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier);
+       }
+       return 0;
+
+err_out:
+       while (i--)
+               device_unregister(mboxes[i]->dev);
+       return ret;
+}
+EXPORT_SYMBOL(mailbox_register);
+
+int mailbox_unregister(void)
+{
+       int i;
+
+       if (!mboxes)
+               return -EINVAL;
+
+       for (i = 0; mboxes[i]; i++)
+               device_unregister(mboxes[i]->dev);
+       mboxes = NULL;
+       return 0;
+}
+EXPORT_SYMBOL(mailbox_unregister);
+
+static int __init mailbox_init(void)
+{
+       int err;
+
+       err = class_register(&mailbox_class);
+       if (err)
+               return err;
+
+       /* kfifo size sanity check: alignment and minimal size */
+       mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(struct mailbox_msg));
+       mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size,
+                       sizeof(struct mailbox_msg) + CONFIG_MBOX_DATA_SIZE);
+       return 0;
+}
+subsys_initcall(mailbox_init);
+
+static void __exit mailbox_exit(void)
+{
+       class_unregister(&mailbox_class);
+}
+module_exit(mailbox_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("mailbox framework: interrupt driven messaging");
+MODULE_AUTHOR("Toshihiro Kobayashi");
+MODULE_AUTHOR("Hiroshi DOYU");
diff --git a/drivers/mailbox/mailbox_internal.h b/drivers/mailbox/mailbox_internal.h
new file mode 100644 (file)
index 0000000..45c4de5
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * mailbox: interprocessor communication module
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef MAILBOX_INTERNAL_H
+#define MAILBOX_INTERNAL_H
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/mailbox.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+typedef int __bitwise mailbox_type_t;
+#define MBOX_HW_FIFO1_TYPE     ((__force mailbox_type_t) 1)
+#define MBOX_HW_FIFO2_TYPE     ((__force mailbox_type_t) 2)
+#define MBOX_SHARED_MEM_TYPE   ((__force mailbox_type_t) 3)
+
+struct mailbox_ops {
+       mailbox_type_t  type;
+       int             (*startup)(struct mailbox *mbox);
+       void            (*shutdown)(struct mailbox *mbox);
+       /* mailbox access */
+       void            (*read)(struct mailbox *mbox, struct mailbox_msg *msg);
+       int             (*write)(struct mailbox *mbox, struct mailbox_msg *msg);
+       int             (*empty)(struct mailbox *mbox);
+       int             (*poll_for_space)(struct mailbox *mbox);
+       int             (*needs_flush)(struct mailbox *mbox);
+       void            (*readback)(struct mailbox *mbox,
+                                       struct mailbox_msg *msg);
+       /* irq */
+       void            (*enable_irq)(struct mailbox *mbox, mailbox_irq_t irq);
+       void            (*disable_irq)(struct mailbox *mbox, mailbox_irq_t irq);
+       void            (*ack_irq)(struct mailbox *mbox, mailbox_irq_t irq);
+       int             (*is_irq)(struct mailbox *mbox, mailbox_irq_t irq);
+       /* ctx */
+       void            (*save_ctx)(struct mailbox *mbox);
+       void            (*restore_ctx)(struct mailbox *mbox);
+};
+
+struct mailbox_queue {
+       spinlock_t              lock;
+       struct mutex            mlock;
+       struct kfifo            fifo;
+       struct work_struct      work;
+       struct tasklet_struct   tasklet;
+       struct mailbox          *mbox;
+       bool full;
+};
+
+struct mailbox {
+       const char              *name;
+       unsigned int            id;
+       unsigned int            irq;
+       struct mailbox_queue    *txq, *rxq;
+       struct mailbox_ops      *ops;
+       struct device           *dev;
+       void                    *priv;
+       int                     use_count;
+       struct blocking_notifier_head   notifier;
+};
+
+void mailbox_init_seq(struct mailbox *);
+
+int mailbox_register(struct device *parent, struct mailbox **);
+int mailbox_unregister(void);
+
+#endif /* MAILBOX_INTERNAL_H */
index 06d31c99e6ac22e166027a5a3666ca810931581b..b7640353ddcb7eabe879037ac62e310cd1bf6b14 100644 (file)
@@ -24,8 +24,9 @@
 #include <linux/module.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/ti_emif.h>
 #include <memory/jedec_ddr.h>
-#include "emif.h"
 #include "of_memory.h"
 
 /**
@@ -255,6 +256,42 @@ static void set_lpmode(struct emif_data *emif, u8 lpmode)
        u32 temp;
        void __iomem *base = emif->base;
 
+       /*
+        * Workaround for errata i743 - LPDDR2 Power-Down State is Not
+        * Efficient
+        *
+        * i743 DESCRIPTION:
+        * The EMIF supports power-down state for low power. The EMIF
+        * automatically puts the SDRAM into power-down after the memory is
+        * not accessed for a defined number of cycles and the
+        * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field is set to 0x4.
+        * As the EMIF supports automatic output impedance calibration, a ZQ
+        * calibration long command is issued every time it exits active
+        * power-down and precharge power-down modes. The EMIF waits and
+        * blocks any other command during this calibration.
+        * The EMIF does not allow selective disabling of ZQ calibration upon
+        * exit of power-down mode. Due to very short periods of power-down
+        * cycles, ZQ calibration overhead creates bandwidth issues and
+        * increases overall system power consumption. On the other hand,
+        * issuing ZQ calibration long commands when exiting self-refresh is
+        * still required.
+        *
+        * WORKAROUND
+        * Because there is no power consumption benefit of the power-down due
+        * to the calibration and there is a performance risk, the guideline
+        * is to not allow power-down state and, therefore, to not have set
+        * the EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field to 0x4.
+        */
+       if (emif->plat_data->ip_rev == EMIF_4D &&
+           EMIF_LP_MODE_PWR_DN == lpmode) {
+               WARN_ONCE(1, "%s: Power-down mode"
+                         " REG_LP_MODE = LP_MODE_PWR_DN(0x4) is prohibited by"
+                         " erratum i743 switch to LP_MODE_SELF_REFRESH(0x2)",
+                         __func__);
+               /* rallback LP_MODE to Self-refresh mode */
+               lpmode = EMIF_LP_MODE_SELF_REFRESH;
+       }
+
        temp = readl(base + EMIF_POWER_MANAGEMENT_CONTROL);
        temp &= ~LP_MODE_MASK;
        temp |= (lpmode << LP_MODE_SHIFT);
@@ -714,14 +751,20 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev)
        u32 timeout_perf        = EMIF_LP_MODE_TIMEOUT_PERFORMANCE;
        u32 timeout_pwr         = EMIF_LP_MODE_TIMEOUT_POWER;
        u32 freq_threshold      = EMIF_LP_MODE_FREQ_THRESHOLD;
+       u32 mask;
+       u8 shift;
 
        struct emif_custom_configs *cust_cfgs = emif->plat_data->custom_configs;
 
        if (cust_cfgs && (cust_cfgs->mask & EMIF_CUSTOM_CONFIG_LPMODE)) {
-               lpmode          = cust_cfgs->lpmode;
-               timeout_perf    = cust_cfgs->lpmode_timeout_performance;
-               timeout_pwr     = cust_cfgs->lpmode_timeout_power;
-               freq_threshold  = cust_cfgs->lpmode_freq_threshold;
+               lpmode  = cust_cfgs->lpmode;
+
+               if (cust_cfgs->lpmode_timeout_performance)
+                       timeout_perf = cust_cfgs->lpmode_timeout_performance;
+               if (cust_cfgs->lpmode_timeout_power)
+                       timeout_pwr = cust_cfgs->lpmode_timeout_power;
+               if (cust_cfgs->lpmode_freq_threshold)
+                       freq_threshold  = cust_cfgs->lpmode_freq_threshold;
        }
 
        /* Timeout based on DDR frequency */
@@ -731,34 +774,52 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev)
        if (timeout < 16) {
                timeout = 0;
        } else {
-               timeout = __fls(timeout) - 3;
                if (timeout & (timeout - 1))
-                       timeout++;
+                       timeout <<= 1;
+               timeout = __fls(timeout) - 3;
        }
 
        switch (lpmode) {
        case EMIF_LP_MODE_CLOCK_STOP:
-               pwr_mgmt_ctrl = (timeout << CS_TIM_SHIFT) |
-                                       SR_TIM_MASK | PD_TIM_MASK;
+               shift = CS_TIM_SHIFT;
+               mask = CS_TIM_MASK;
                break;
        case EMIF_LP_MODE_SELF_REFRESH:
                /* Workaround for errata i735 */
                if (timeout < 6)
                        timeout = 6;
 
-               pwr_mgmt_ctrl = (timeout << SR_TIM_SHIFT) |
-                                       CS_TIM_MASK | PD_TIM_MASK;
+               shift = SR_TIM_SHIFT;
+               mask = SR_TIM_MASK;
                break;
        case EMIF_LP_MODE_PWR_DN:
-               pwr_mgmt_ctrl = (timeout << PD_TIM_SHIFT) |
-                                       CS_TIM_MASK | SR_TIM_MASK;
+               shift = PD_TIM_SHIFT;
+               mask = PD_TIM_MASK;
                break;
        case EMIF_LP_MODE_DISABLE:
        default:
-               pwr_mgmt_ctrl = CS_TIM_MASK |
-                                       PD_TIM_MASK | SR_TIM_MASK;
+               mask = 0;
+               shift = 0;
+               break;
+       }
+       /* Round to maximum in case of overflow, BUT warn! */
+       if (lpmode != EMIF_LP_MODE_DISABLE && timeout > mask >> shift) {
+               pr_err("TIMEOUT Overflow - lpmode=%d perf=%d pwr=%d freq=%d\n",
+                      lpmode,
+                      timeout_perf,
+                      timeout_pwr,
+                      freq_threshold);
+               WARN(1, "timeout=0x%02x greater than 0x%02x. Using max\n",
+                    timeout, mask >> shift);
+               timeout = mask >> shift;
        }
 
+       /* Setup required timing */
+       pwr_mgmt_ctrl = (timeout << shift) & mask;
+       /* setup a default mask for rest of the modes */
+       pwr_mgmt_ctrl |= (SR_TIM_MASK | CS_TIM_MASK | PD_TIM_MASK) &
+                         ~mask;
+
        /* No CS_TIM in EMIF_4D5 */
        if (ip_rev == EMIF_4D5)
                pwr_mgmt_ctrl &= ~CS_TIM_MASK;
@@ -814,6 +875,8 @@ static void setup_registers(struct emif_data *emif, struct emif_regs *regs)
 
        writel(regs->sdram_tim2_shdw, base + EMIF_SDRAM_TIMING_2_SHDW);
        writel(regs->phy_ctrl_1_shdw, base + EMIF_DDR_PHY_CTRL_1_SHDW);
+       writel(regs->pwr_mgmt_ctrl_shdw,
+              base + EMIF_POWER_MANAGEMENT_CTRL_SHDW);
 
        /* Settings specific for EMIF4D5 */
        if (emif->plat_data->ip_rev != EMIF_4D5)
@@ -891,6 +954,7 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
 {
        u32             old_temp_level;
        irqreturn_t     ret = IRQ_HANDLED;
+       struct emif_custom_configs *custom_configs;
 
        spin_lock_irqsave(&emif_lock, irq_state);
        old_temp_level = emif->temperature_level;
@@ -903,6 +967,29 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
                goto out;
        }
 
+       custom_configs = emif->plat_data->custom_configs;
+
+       /*
+        * IF we detect higher than "nominal rating" from DDR sensor
+        * on an unsupported DDR part, shutdown system
+        */
+       if (custom_configs && !(custom_configs->mask &
+                               EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART)) {
+               if (emif->temperature_level >= SDRAM_TEMP_HIGH_DERATE_REFRESH) {
+                       dev_err(emif->dev,
+                               "%s:NOT Extended temperature capable memory."
+                               "Converting MR4=0x%02x as shutdown event\n",
+                               __func__, emif->temperature_level);
+                       /*
+                        * Temperature far too high - do kernel_power_off()
+                        * from thread context
+                        */
+                       emif->temperature_level = SDRAM_TEMP_VERY_HIGH_SHUTDOWN;
+                       ret = IRQ_WAKE_THREAD;
+                       goto out;
+               }
+       }
+
        if (emif->temperature_level < old_temp_level ||
                emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {
                /*
@@ -964,7 +1051,13 @@ static irqreturn_t emif_threaded_isr(int irq, void *dev_id)
 
        if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {
                dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n");
-               kernel_power_off();
+               /* If we have Power OFF ability, use it, else try restarting */
+               if (pm_power_off) {
+                       kernel_power_off();
+               } else {
+                       WARN(1, "FIXME: NO pm_power_off!!! trying restart\n");
+                       kernel_restart("SDRAM Over-temp Emergency restart");
+               }
                return IRQ_HANDLED;
        }
 
@@ -1169,7 +1262,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
 {
        struct emif_custom_configs      *cust_cfgs = NULL;
        int                             len;
-       const int                       *lpmode, *poll_intvl;
+       const __be32                    *lpmode, *poll_intvl;
 
        lpmode = of_get_property(np_emif, "low-power-mode", &len);
        poll_intvl = of_get_property(np_emif, "temp-alert-poll-interval", &len);
@@ -1183,7 +1276,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
 
        if (lpmode) {
                cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_LPMODE;
-               cust_cfgs->lpmode = *lpmode;
+               cust_cfgs->lpmode = be32_to_cpup(lpmode);
                of_property_read_u32(np_emif,
                                "low-power-mode-timeout-performance",
                                &cust_cfgs->lpmode_timeout_performance);
@@ -1198,9 +1291,13 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
        if (poll_intvl) {
                cust_cfgs->mask |=
                                EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL;
-               cust_cfgs->temp_alert_poll_interval_ms = *poll_intvl;
+               cust_cfgs->temp_alert_poll_interval_ms =
+                                               be32_to_cpup(poll_intvl);
        }
 
+       if (of_find_property(np_emif, "extended-temp-part", &len))
+               cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART;
+
        if (!is_custom_config_valid(cust_cfgs, emif->dev)) {
                devm_kfree(emif->dev, cust_cfgs);
                return;
@@ -1406,7 +1503,7 @@ static struct emif_data *__init_or_module get_device_details(
        if (pd->timings) {
                temp = devm_kzalloc(dev, size, GFP_KERNEL);
                if (temp) {
-                       memcpy(temp, pd->timings, sizeof(*pd->timings));
+                       memcpy(temp, pd->timings, size);
                        pd->timings = temp;
                } else {
                        dev_warn(dev, "%s:%d: allocation error\n", __func__,
index ff553babf455025c2a5ce303297a15f404d2d5e3..5f89b85cdf06494ca9dc29bfe0d7f5b87b48998e 100644 (file)
@@ -1082,6 +1082,27 @@ config MFD_PALMAS
          If you say yes here you get support for the Palmas
          series of PMIC chips from Texas Instruments.
 
+config MFD_PALMAS_GPADC
+       bool "Support for the GPADC on the Palmas series chips"
+       depends on MFD_PALMAS
+       help
+         If you say yes here you get support for the GPADC on
+         Palmas series of PMIC chips from Texas Instruments.
+
+config MFD_PALMAS_PWM
+       bool "Support for the PWM on the Palmas series chips"
+       depends on MFD_PALMAS
+       help
+         If you say yes here you get support for the PWM on
+         Palmas series of PMIC chips from Texas Instruments.
+
+config MFD_PALMAS_RESOURCE
+       bool "Support for the misc RESOURCES on the Palmas series chips"
+       depends on MFD_PALMAS
+       help
+         If you say yes here you get support for the misc RESOURCES on
+         Palmas series of PMIC chips from Texas Instruments.
+
 config MFD_VIPERBOARD
         tristate "Support for Nano River Technologies Viperboard"
        select MFD_CORE
index 8b977f8045ae8f52cbc283838fa1cbfe375809f9..3503191f8329d0d1d27072b004cefa68a17bac87 100644 (file)
@@ -140,6 +140,9 @@ obj-$(CONFIG_MFD_TPS65090)  += tps65090.o
 obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
 obj-$(CONFIG_MFD_INTEL_MSIC)   += intel_msic.o
 obj-$(CONFIG_MFD_PALMAS)       += palmas.o
+obj-$(CONFIG_MFD_PALMAS_GPADC) += palmas-gpadc.o
+obj-$(CONFIG_MFD_PALMAS_PWM) += palmas-pwm.o
+obj-$(CONFIG_MFD_PALMAS_RESOURCE) += palmas-resource.o
 obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
 obj-$(CONFIG_MFD_RC5T583)      += rc5t583.o rc5t583-irq.o
 obj-$(CONFIG_MFD_SEC_CORE)     += sec-core.o sec-irq.o
index 05164d7f054b86ccfd6625cc21da545212d5983d..06ba21abe347ecd95c14aa24cce8385b972a7561 100644 (file)
@@ -1,8 +1,9 @@
 /**
  * omap-usb-host.c - The USBHS core driver for OMAP EHCI & OHCI
  *
- * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2011-2013 Texas Instruments Incorporated - http://www.ti.com
  * Author: Keshava Munegowda <keshava_mgowda@ti.com>
+ * Author: Roger Quadros <rogerq@ti.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2  of
 #include <linux/delay.h>
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
-#include <linux/spinlock.h>
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/usb-omap.h>
 #include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
 
 #include "omap-usb.h"
 
 
 
 struct usbhs_hcd_omap {
+       int                             nports;
+       struct clk                      **utmi_clk;
+       struct clk                      **hsic60m_clk;
+       struct clk                      **hsic480m_clk;
+
        struct clk                      *xclk60mhsp1_ck;
        struct clk                      *xclk60mhsp2_ck;
-       struct clk                      *utmi_p1_fck;
-       struct clk                      *usbhost_p1_fck;
-       struct clk                      *utmi_p2_fck;
-       struct clk                      *usbhost_p2_fck;
+       struct clk                      *utmi_p1_gfclk;
+       struct clk                      *utmi_p2_gfclk;
        struct clk                      *init_60m_fclk;
        struct clk                      *ehci_logic_fck;
 
        void __iomem                    *uhh_base;
 
-       struct usbhs_omap_platform_data platdata;
+       struct usbhs_omap_platform_data *pdata;
 
        u32                             usbhs_rev;
-       spinlock_t                      lock;
 };
 /*-------------------------------------------------------------------------*/
 
-const char usbhs_driver_name[] = USBHS_DRIVER_NAME;
+static const char usbhs_driver_name[] = USBHS_DRIVER_NAME;
 static u64 usbhs_dmamask = DMA_BIT_MASK(32);
 
 /*-------------------------------------------------------------------------*/
@@ -136,6 +140,49 @@ static inline u8 usbhs_readb(void __iomem *base, u8 reg)
 
 /*-------------------------------------------------------------------------*/
 
+/**
+ * Map 'enum usbhs_omap_port_mode' found in <linux/platform_data/usb-omap.h>
+ * to the device tree binding portN-mode found in
+ * 'Documentation/devicetree/bindings/mfd/omap-usb-host.txt'
+ */
+static const char *port_modes[] = {
+       [OMAP_USBHS_PORT_MODE_UNUSED]   = "",
+       [OMAP_EHCI_PORT_MODE_PHY]       = "ehci-phy",
+       [OMAP_EHCI_PORT_MODE_TLL]       = "ehci-tll",
+       [OMAP_EHCI_PORT_MODE_HSIC]      = "ehci-hsic",
+       [OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0]   = "ohci-phy-6pin-datse0",
+       [OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM]     = "ohci-phy-6pin-dpdm",
+       [OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0]   = "ohci-phy-3pin-datse0",
+       [OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM]     = "ohci-phy-4pin-dpdm",
+       [OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0]   = "ohci-tll-6pin-datse0",
+       [OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM]     = "ohci-tll-6pin-dpdm",
+       [OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0]   = "ohci-tll-3pin-datse0",
+       [OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM]     = "ohci-tll-4pin-dpdm",
+       [OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0]   = "ohci-tll-2pin-datse0",
+       [OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM]     = "ohci-tll-2pin-dpdm",
+};
+
+/**
+ * omap_usbhs_get_dt_port_mode - Get the 'enum usbhs_omap_port_mode'
+ * from the port mode string.
+ * @mode: The port mode string, usually obtained from device tree.
+ *
+ * The function returns the 'enum usbhs_omap_port_mode' that matches the
+ * provided port mode string as per the port_modes table.
+ * If no match is found it returns -ENODEV
+ */
+static const int omap_usbhs_get_dt_port_mode(const char *mode)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(port_modes); i++) {
+               if (!strcasecmp(mode, port_modes[i]))
+                       return i;
+       }
+
+       return -ENODEV;
+}
+
 static struct platform_device *omap_usbhs_alloc_child(const char *name,
                        struct resource *res, int num_resources, void *pdata,
                        size_t pdata_size, struct device *dev)
@@ -184,19 +231,13 @@ err_end:
 static int omap_usbhs_alloc_children(struct platform_device *pdev)
 {
        struct device                           *dev = &pdev->dev;
-       struct usbhs_hcd_omap                   *omap;
-       struct ehci_hcd_omap_platform_data      *ehci_data;
-       struct ohci_hcd_omap_platform_data      *ohci_data;
+       struct usbhs_omap_platform_data         *pdata = dev->platform_data;
        struct platform_device                  *ehci;
        struct platform_device                  *ohci;
        struct resource                         *res;
        struct resource                         resources[2];
        int                                     ret;
 
-       omap = platform_get_drvdata(pdev);
-       ehci_data = omap->platdata.ehci_data;
-       ohci_data = omap->platdata.ohci_data;
-
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci");
        if (!res) {
                dev_err(dev, "EHCI get resource IORESOURCE_MEM failed\n");
@@ -213,8 +254,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
        }
        resources[1] = *res;
 
-       ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, ehci_data,
-               sizeof(*ehci_data), dev);
+       ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, pdata,
+               sizeof(*pdata), dev);
 
        if (!ehci) {
                dev_err(dev, "omap_usbhs_alloc_child failed\n");
@@ -238,8 +279,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
        }
        resources[1] = *res;
 
-       ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, ohci_data,
-               sizeof(*ohci_data), dev);
+       ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, pdata,
+               sizeof(*pdata), dev);
        if (!ohci) {
                dev_err(dev, "omap_usbhs_alloc_child failed\n");
                ret = -ENOMEM;
@@ -278,31 +319,52 @@ static bool is_ohci_port(enum usbhs_omap_port_mode pmode)
 static int usbhs_runtime_resume(struct device *dev)
 {
        struct usbhs_hcd_omap           *omap = dev_get_drvdata(dev);
-       struct usbhs_omap_platform_data *pdata = &omap->platdata;
-       unsigned long                   flags;
+       struct usbhs_omap_platform_data *pdata = omap->pdata;
+       int i, r;
 
        dev_dbg(dev, "usbhs_runtime_resume\n");
 
-       if (!pdata) {
-               dev_dbg(dev, "missing platform_data\n");
-               return  -ENODEV;
-       }
-
-       omap_tll_enable();
-       spin_lock_irqsave(&omap->lock, flags);
+       omap_tll_enable(pdata);
 
-       if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
+       if (!IS_ERR(omap->ehci_logic_fck))
                clk_enable(omap->ehci_logic_fck);
 
-       if (is_ehci_tll_mode(pdata->port_mode[0]))
-               clk_enable(omap->usbhost_p1_fck);
-       if (is_ehci_tll_mode(pdata->port_mode[1]))
-               clk_enable(omap->usbhost_p2_fck);
-
-       clk_enable(omap->utmi_p1_fck);
-       clk_enable(omap->utmi_p2_fck);
+       for (i = 0; i < omap->nports; i++) {
+               switch (pdata->port_mode[i]) {
+               case OMAP_EHCI_PORT_MODE_HSIC:
+                       if (!IS_ERR(omap->hsic60m_clk[i])) {
+                               r = clk_enable(omap->hsic60m_clk[i]);
+                               if (r) {
+                                       dev_err(dev,
+                                        "Can't enable port %d hsic60m clk:%d\n",
+                                        i, r);
+                               }
+                       }
 
-       spin_unlock_irqrestore(&omap->lock, flags);
+                       if (!IS_ERR(omap->hsic480m_clk[i])) {
+                               r = clk_enable(omap->hsic480m_clk[i]);
+                               if (r) {
+                                       dev_err(dev,
+                                        "Can't enable port %d hsic480m clk:%d\n",
+                                        i, r);
+                               }
+                       }
+               /* Fall through as HSIC mode needs utmi_clk */
+
+               case OMAP_EHCI_PORT_MODE_TLL:
+                       if (!IS_ERR(omap->utmi_clk[i])) {
+                               r = clk_enable(omap->utmi_clk[i]);
+                               if (r) {
+                                       dev_err(dev,
+                                        "Can't enable port %d clk : %d\n",
+                                        i, r);
+                               }
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
 
        return 0;
 }
@@ -310,61 +372,115 @@ static int usbhs_runtime_resume(struct device *dev)
 static int usbhs_runtime_suspend(struct device *dev)
 {
        struct usbhs_hcd_omap           *omap = dev_get_drvdata(dev);
-       struct usbhs_omap_platform_data *pdata = &omap->platdata;
-       unsigned long                   flags;
+       struct usbhs_omap_platform_data *pdata = omap->pdata;
+       int i;
 
        dev_dbg(dev, "usbhs_runtime_suspend\n");
 
-       if (!pdata) {
-               dev_dbg(dev, "missing platform_data\n");
-               return  -ENODEV;
-       }
-
-       spin_lock_irqsave(&omap->lock, flags);
+       for (i = 0; i < omap->nports; i++) {
+               switch (pdata->port_mode[i]) {
+               case OMAP_EHCI_PORT_MODE_HSIC:
+                       if (!IS_ERR(omap->hsic60m_clk[i]))
+                               clk_disable(omap->hsic60m_clk[i]);
 
-       if (is_ehci_tll_mode(pdata->port_mode[0]))
-               clk_disable(omap->usbhost_p1_fck);
-       if (is_ehci_tll_mode(pdata->port_mode[1]))
-               clk_disable(omap->usbhost_p2_fck);
+                       if (!IS_ERR(omap->hsic480m_clk[i]))
+                               clk_disable(omap->hsic480m_clk[i]);
+               /* Fall through as utmi_clks were used in HSIC mode */
 
-       clk_disable(omap->utmi_p2_fck);
-       clk_disable(omap->utmi_p1_fck);
+               case OMAP_EHCI_PORT_MODE_TLL:
+                       if (!IS_ERR(omap->utmi_clk[i]))
+                               clk_disable(omap->utmi_clk[i]);
+                       break;
+               default:
+                       break;
+               }
+       }
 
-       if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
+       if (!IS_ERR(omap->ehci_logic_fck))
                clk_disable(omap->ehci_logic_fck);
 
-       spin_unlock_irqrestore(&omap->lock, flags);
-       omap_tll_disable();
+       omap_tll_disable(pdata);
 
        return 0;
 }
 
-static void omap_usbhs_init(struct device *dev)
+static unsigned omap_usbhs_rev1_hostconfig(struct usbhs_hcd_omap *omap,
+                                               unsigned reg)
 {
-       struct usbhs_hcd_omap           *omap = dev_get_drvdata(dev);
-       struct usbhs_omap_platform_data *pdata = &omap->platdata;
-       unsigned long                   flags;
-       unsigned                        reg;
+       struct usbhs_omap_platform_data *pdata = omap->pdata;
+       int i;
 
-       dev_dbg(dev, "starting TI HSUSB Controller\n");
+       for (i = 0; i < omap->nports; i++) {
+               switch (pdata->port_mode[i]) {
+               case OMAP_USBHS_PORT_MODE_UNUSED:
+                       reg &= ~(OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS << i);
+                       break;
+               case OMAP_EHCI_PORT_MODE_PHY:
+                       if (pdata->single_ulpi_bypass)
+                               break;
 
-       if (pdata->ehci_data->phy_reset) {
-               if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
-                       gpio_request_one(pdata->ehci_data->reset_gpio_port[0],
-                                        GPIOF_OUT_INIT_LOW, "USB1 PHY reset");
+                       if (i == 0)
+                               reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
+                       else
+                               reg &= ~(OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS
+                                                               << (i-1));
+                       break;
+               default:
+                       if (pdata->single_ulpi_bypass)
+                               break;
 
-               if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
-                       gpio_request_one(pdata->ehci_data->reset_gpio_port[1],
-                                        GPIOF_OUT_INIT_LOW, "USB2 PHY reset");
+                       if (i == 0)
+                               reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
+                       else
+                               reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS
+                                                               << (i-1);
+                       break;
+               }
+       }
+
+       if (pdata->single_ulpi_bypass) {
+               /* bypass ULPI only if none of the ports use PHY mode */
+               reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
 
-               /* Hold the PHY in RESET for enough time till DIR is high */
-               udelay(10);
+               for (i = 0; i < omap->nports; i++) {
+                       if (is_ehci_phy_mode(pdata->port_mode[i])) {
+                               reg &= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
+                               break;
+                       }
+               }
        }
 
+       return reg;
+}
+
+static unsigned omap_usbhs_rev2_hostconfig(struct usbhs_hcd_omap *omap,
+                                               unsigned reg)
+{
+       struct usbhs_omap_platform_data *pdata = omap->pdata;
+       int i;
+
+       for (i = 0; i < omap->nports; i++) {
+               /* Clear port mode fields for PHY mode */
+               reg &= ~(OMAP4_P1_MODE_CLEAR << 2 * i);
+
+               if (is_ehci_tll_mode(pdata->port_mode[i]) ||
+                               (is_ohci_port(pdata->port_mode[i])))
+                       reg |= OMAP4_P1_MODE_TLL << 2 * i;
+               else if (is_ehci_hsic_mode(pdata->port_mode[i]))
+                       reg |= OMAP4_P1_MODE_HSIC << 2 * i;
+       }
+
+       return reg;
+}
+
+static void omap_usbhs_init(struct device *dev)
+{
+       struct usbhs_hcd_omap           *omap = dev_get_drvdata(dev);
+       unsigned                        reg;
+
+       dev_dbg(dev, "starting TI HSUSB Controller\n");
+
        pm_runtime_get_sync(dev);
-       spin_lock_irqsave(&omap->lock, flags);
-       omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
-       dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev);
 
        reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
        /* setup ULPI bypass and burst configurations */
@@ -374,92 +490,78 @@ static void omap_usbhs_init(struct device *dev)
        reg |= OMAP4_UHH_HOSTCONFIG_APP_START_CLK;
        reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
 
-       if (is_omap_usbhs_rev1(omap)) {
-               if (pdata->port_mode[0] == OMAP_USBHS_PORT_MODE_UNUSED)
-                       reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
-               if (pdata->port_mode[1] == OMAP_USBHS_PORT_MODE_UNUSED)
-                       reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
-               if (pdata->port_mode[2] == OMAP_USBHS_PORT_MODE_UNUSED)
-                       reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
-
-               /* Bypass the TLL module for PHY mode operation */
-               if (pdata->single_ulpi_bypass) {
-                       dev_dbg(dev, "OMAP3 ES version <= ES2.1\n");
-                       if (is_ehci_phy_mode(pdata->port_mode[0]) ||
-                               is_ehci_phy_mode(pdata->port_mode[1]) ||
-                                       is_ehci_phy_mode(pdata->port_mode[2]))
-                               reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
-                       else
-                               reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
-               } else {
-                       dev_dbg(dev, "OMAP3 ES version > ES2.1\n");
-                       if (is_ehci_phy_mode(pdata->port_mode[0]))
-                               reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
-                       else
-                               reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
-                       if (is_ehci_phy_mode(pdata->port_mode[1]))
-                               reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
-                       else
-                               reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
-                       if (is_ehci_phy_mode(pdata->port_mode[2]))
-                               reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
-                       else
-                               reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
-               }
-       } else if (is_omap_usbhs_rev2(omap)) {
-               /* Clear port mode fields for PHY mode*/
-               reg &= ~OMAP4_P1_MODE_CLEAR;
-               reg &= ~OMAP4_P2_MODE_CLEAR;
+       switch (omap->usbhs_rev) {
+       case OMAP_USBHS_REV1:
+               reg = omap_usbhs_rev1_hostconfig(omap, reg);
+               break;
 
-               if (is_ehci_tll_mode(pdata->port_mode[0]) ||
-                       (is_ohci_port(pdata->port_mode[0])))
-                       reg |= OMAP4_P1_MODE_TLL;
-               else if (is_ehci_hsic_mode(pdata->port_mode[0]))
-                       reg |= OMAP4_P1_MODE_HSIC;
+       case OMAP_USBHS_REV2:
+               reg = omap_usbhs_rev2_hostconfig(omap, reg);
+               break;
 
-               if (is_ehci_tll_mode(pdata->port_mode[1]) ||
-                       (is_ohci_port(pdata->port_mode[1])))
-                       reg |= OMAP4_P2_MODE_TLL;
-               else if (is_ehci_hsic_mode(pdata->port_mode[1]))
-                       reg |= OMAP4_P2_MODE_HSIC;
+       default:        /* newer revisions */
+               reg = omap_usbhs_rev2_hostconfig(omap, reg);
+               break;
        }
 
        usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
        dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
 
-       spin_unlock_irqrestore(&omap->lock, flags);
-
        pm_runtime_put_sync(dev);
-       if (pdata->ehci_data->phy_reset) {
-               /* Hold the PHY in RESET for enough time till
-                * PHY is settled and ready
-                */
-               udelay(10);
-
-               if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
-                       gpio_set_value_cansleep
-                               (pdata->ehci_data->reset_gpio_port[0], 1);
-
-               if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
-                       gpio_set_value_cansleep
-                               (pdata->ehci_data->reset_gpio_port[1], 1);
-       }
 }
 
-static void omap_usbhs_deinit(struct device *dev)
+static int usbhs_omap_get_dt_pdata(struct device *dev,
+                                       struct usbhs_omap_platform_data *pdata)
 {
-       struct usbhs_hcd_omap           *omap = dev_get_drvdata(dev);
-       struct usbhs_omap_platform_data *pdata = &omap->platdata;
+       int ret, i;
+       struct device_node *node = dev->of_node;
+
+       ret = of_property_read_u32(node, "num-ports", &pdata->nports);
+       if (ret)
+               pdata->nports = 0;
+
+       if (pdata->nports > OMAP3_HS_USB_PORTS) {
+               dev_warn(dev, "Too many num_ports <%d> in device tree. "
+                               "Max. suported num_ports is <%d>\n",
+                               pdata->nports, OMAP3_HS_USB_PORTS);
+               return -ENODEV;
+       }
 
-       if (pdata->ehci_data->phy_reset) {
-               if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
-                       gpio_free(pdata->ehci_data->reset_gpio_port[0]);
+       /* get port modes */
+       for (i = 0; i < OMAP3_HS_USB_PORTS; i++) {
+               char prop[11];
+               const char *mode;
 
-               if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
-                       gpio_free(pdata->ehci_data->reset_gpio_port[1]);
+               pdata->port_mode[i] = OMAP_USBHS_PORT_MODE_UNUSED;
+
+               snprintf(prop, sizeof(prop), "port%d-mode", i + 1);
+               ret = of_property_read_string(node, prop, &mode);
+               if (ret < 0)
+                       continue;
+
+               ret = omap_usbhs_get_dt_port_mode(mode);
+               if (ret < 0) {
+                       dev_warn(dev, "Invalid port%d-mode \"%s\" in device tree\n",
+                                       i, mode);
+                       return -ENODEV;
+               }
+
+               dev_dbg(dev, "port%d-mode: %s -> %d\n", i, mode, ret);
+               pdata->port_mode[i] = ret;
        }
+
+       /* get flags */
+       pdata->single_ulpi_bypass = of_property_read_bool(node,
+                                               "single-ulpi-bypass");
+
+       return 0;
 }
 
+static struct of_device_id usbhs_child_match_table[] = {
+       { .compatible = "ti,omap-ehci", },
+       { .compatible = "ti,omap-ohci", },
+       { }
+};
 
 /**
  * usbhs_omap_probe - initialize TI-based HCDs
@@ -474,180 +576,272 @@ static int usbhs_omap_probe(struct platform_device *pdev)
        struct resource                 *res;
        int                             ret = 0;
        int                             i;
+       bool                            need_logic_fck;
+
+       if (dev->of_node) {
+               /* For DT boot we populate platform data from OF node */
+               pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+               if (!pdata)
+                       return -ENOMEM;
+
+               ret = usbhs_omap_get_dt_pdata(dev, pdata);
+               if (ret)
+                       return ret;
+
+               dev->platform_data = pdata;
+       }
 
        if (!pdata) {
                dev_err(dev, "Missing platform data\n");
-               ret = -ENOMEM;
-               goto end_probe;
+               return -ENODEV;
        }
 
-       omap = kzalloc(sizeof(*omap), GFP_KERNEL);
+       if (pdata->nports > OMAP3_HS_USB_PORTS) {
+               dev_info(dev, "Too many num_ports <%d> in platform_data. "
+                               "Max. suported num_ports is <%d>\n",
+                               pdata->nports, OMAP3_HS_USB_PORTS);
+               return -ENODEV;
+       }
+
+       omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
        if (!omap) {
                dev_err(dev, "Memory allocation failed\n");
-               ret = -ENOMEM;
-               goto end_probe;
+               return -ENOMEM;
        }
 
-       spin_lock_init(&omap->lock);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       omap->uhh_base = devm_request_and_ioremap(dev, res);
+       if (!omap->uhh_base) {
+               dev_err(dev, "Resource request/ioremap failed\n");
+               return -EADDRNOTAVAIL;
+       }
 
-       for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
-               omap->platdata.port_mode[i] = pdata->port_mode[i];
+       omap->pdata = pdata;
 
-       omap->platdata.ehci_data = pdata->ehci_data;
-       omap->platdata.ohci_data = pdata->ohci_data;
+       /* Initialize the TLL subsystem */
+       omap_tll_init(pdata);
 
        pm_runtime_enable(dev);
 
+       platform_set_drvdata(pdev, omap);
+       pm_runtime_get_sync(dev);
 
-       for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
-               if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
-                       is_ehci_hsic_mode(i)) {
-                       omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
-                       if (IS_ERR(omap->ehci_logic_fck)) {
-                               ret = PTR_ERR(omap->ehci_logic_fck);
-                               dev_warn(dev, "ehci_logic_fck failed:%d\n",
-                                        ret);
-                       }
+       omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
+
+       /* we need to call runtime suspend before we update omap->nports
+        * to prevent unbalanced clk_disable()
+        */
+       pm_runtime_put_sync(dev);
+
+       /*
+        * If platform data contains nports then use that
+        * else make out number of ports from USBHS revision
+        */
+       if (pdata->nports) {
+               omap->nports = pdata->nports;
+       } else {
+               switch (omap->usbhs_rev) {
+               case OMAP_USBHS_REV1:
+                       omap->nports = 3;
+                       break;
+               case OMAP_USBHS_REV2:
+                       omap->nports = 2;
+                       break;
+               default:
+                       omap->nports = OMAP3_HS_USB_PORTS;
+                       dev_dbg(dev,
+                        "USB HOST Rev:0x%d not recognized, assuming %d ports\n",
+                        omap->usbhs_rev, omap->nports);
                        break;
                }
+               pdata->nports = omap->nports;
+       }
 
-       omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
-       if (IS_ERR(omap->utmi_p1_fck)) {
-               ret = PTR_ERR(omap->utmi_p1_fck);
-               dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
-               goto err_end;
+       i = sizeof(struct clk *) * omap->nports;
+       omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+       omap->hsic480m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+       omap->hsic60m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
+
+       if (!omap->utmi_clk || !omap->hsic480m_clk || !omap->hsic60m_clk) {
+               dev_err(dev, "Memory allocation failed\n");
+               ret = -ENOMEM;
+               goto err_mem;
+       }
+
+       need_logic_fck = false;
+       for (i = 0; i < omap->nports; i++) {
+               if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
+                       is_ehci_hsic_mode(i))
+                               need_logic_fck |= true;
+       }
+
+       omap->ehci_logic_fck = ERR_PTR(-EINVAL);
+       if (need_logic_fck) {
+               omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
+               if (IS_ERR(omap->ehci_logic_fck)) {
+                       ret = PTR_ERR(omap->ehci_logic_fck);
+                       dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret);
+               }
+       }
+
+       omap->utmi_p1_gfclk = clk_get(dev, "utmi_p1_gfclk");
+       if (IS_ERR(omap->utmi_p1_gfclk)) {
+               ret = PTR_ERR(omap->utmi_p1_gfclk);
+               dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
+               goto err_p1_gfclk;
+       }
+
+       omap->utmi_p2_gfclk = clk_get(dev, "utmi_p2_gfclk");
+       if (IS_ERR(omap->utmi_p2_gfclk)) {
+               ret = PTR_ERR(omap->utmi_p2_gfclk);
+               dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
+               goto err_p2_gfclk;
        }
 
        omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
        if (IS_ERR(omap->xclk60mhsp1_ck)) {
                ret = PTR_ERR(omap->xclk60mhsp1_ck);
                dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
-               goto err_utmi_p1_fck;
-       }
-
-       omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk");
-       if (IS_ERR(omap->utmi_p2_fck)) {
-               ret = PTR_ERR(omap->utmi_p2_fck);
-               dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
-               goto err_xclk60mhsp1_ck;
+               goto err_xclk60mhsp1;
        }
 
        omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
        if (IS_ERR(omap->xclk60mhsp2_ck)) {
                ret = PTR_ERR(omap->xclk60mhsp2_ck);
                dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
-               goto err_utmi_p2_fck;
-       }
-
-       omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk");
-       if (IS_ERR(omap->usbhost_p1_fck)) {
-               ret = PTR_ERR(omap->usbhost_p1_fck);
-               dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret);
-               goto err_xclk60mhsp2_ck;
-       }
-
-       omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk");
-       if (IS_ERR(omap->usbhost_p2_fck)) {
-               ret = PTR_ERR(omap->usbhost_p2_fck);
-               dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret);
-               goto err_usbhost_p1_fck;
+               goto err_xclk60mhsp2;
        }
 
        omap->init_60m_fclk = clk_get(dev, "init_60m_fclk");
        if (IS_ERR(omap->init_60m_fclk)) {
                ret = PTR_ERR(omap->init_60m_fclk);
                dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
-               goto err_usbhost_p2_fck;
+               goto err_init60m;
+       }
+
+       for (i = 0; i < omap->nports; i++) {
+               char clkname[30];
+
+               /* clock names are indexed from 1*/
+               snprintf(clkname, sizeof(clkname),
+                               "usb_host_hs_utmi_p%d_clk", i + 1);
+
+               /* If a clock is not found we won't bail out as not all
+                * platforms have all clocks and we can function without
+                * them
+                */
+               omap->utmi_clk[i] = clk_get(dev, clkname);
+               if (IS_ERR(omap->utmi_clk[i]))
+                       dev_dbg(dev, "Failed to get clock : %s : %ld\n",
+                               clkname, PTR_ERR(omap->utmi_clk[i]));
+
+               snprintf(clkname, sizeof(clkname),
+                               "usb_host_hs_hsic480m_p%d_clk", i + 1);
+               omap->hsic480m_clk[i] = clk_get(dev, clkname);
+               if (IS_ERR(omap->hsic480m_clk[i]))
+                       dev_dbg(dev, "Failed to get clock : %s : %ld\n",
+                               clkname, PTR_ERR(omap->hsic480m_clk[i]));
+
+               snprintf(clkname, sizeof(clkname),
+                               "usb_host_hs_hsic60m_p%d_clk", i + 1);
+               omap->hsic60m_clk[i] = clk_get(dev, clkname);
+               if (IS_ERR(omap->hsic60m_clk[i]))
+                       dev_dbg(dev, "Failed to get clock : %s : %ld\n",
+                               clkname, PTR_ERR(omap->hsic60m_clk[i]));
        }
 
        if (is_ehci_phy_mode(pdata->port_mode[0])) {
-               /* for OMAP3 , the clk set paretn fails */
-               ret = clk_set_parent(omap->utmi_p1_fck,
+               /* for OMAP3, clk_set_parent fails */
+               ret = clk_set_parent(omap->utmi_p1_gfclk,
                                        omap->xclk60mhsp1_ck);
                if (ret != 0)
-                       dev_err(dev, "xclk60mhsp1_ck set parent"
-                               "failed error:%d\n", ret);
+                       dev_dbg(dev, "xclk60mhsp1_ck set parent failed: %d\n",
+                                       ret);
        } else if (is_ehci_tll_mode(pdata->port_mode[0])) {
-               ret = clk_set_parent(omap->utmi_p1_fck,
+               ret = clk_set_parent(omap->utmi_p1_gfclk,
                                        omap->init_60m_fclk);
                if (ret != 0)
-                       dev_err(dev, "init_60m_fclk set parent"
-                               "failed error:%d\n", ret);
+                       dev_dbg(dev, "P0 init_60m_fclk set parent failed: %d\n",
+                                       ret);
        }
 
        if (is_ehci_phy_mode(pdata->port_mode[1])) {
-               ret = clk_set_parent(omap->utmi_p2_fck,
+               ret = clk_set_parent(omap->utmi_p2_gfclk,
                                        omap->xclk60mhsp2_ck);
                if (ret != 0)
-                       dev_err(dev, "xclk60mhsp2_ck set parent"
-                                       "failed error:%d\n", ret);
+                       dev_dbg(dev, "xclk60mhsp2_ck set parent failed: %d\n",
+                                       ret);
        } else if (is_ehci_tll_mode(pdata->port_mode[1])) {
-               ret = clk_set_parent(omap->utmi_p2_fck,
+               ret = clk_set_parent(omap->utmi_p2_gfclk,
                                                omap->init_60m_fclk);
                if (ret != 0)
-                       dev_err(dev, "init_60m_fclk set parent"
-                               "failed error:%d\n", ret);
+                       dev_dbg(dev, "P1 init_60m_fclk set parent failed: %d\n",
+                                       ret);
        }
 
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh");
-       if (!res) {
-               dev_err(dev, "UHH EHCI get resource failed\n");
-               ret = -ENODEV;
-               goto err_init_60m_fclk;
-       }
+       omap_usbhs_init(dev);
 
-       omap->uhh_base = ioremap(res->start, resource_size(res));
-       if (!omap->uhh_base) {
-               dev_err(dev, "UHH ioremap failed\n");
-               ret = -ENOMEM;
-               goto err_init_60m_fclk;
-       }
+       if (dev->of_node) {
+               ret = of_platform_populate(dev->of_node,
+                               usbhs_child_match_table, NULL, dev);
 
-       platform_set_drvdata(pdev, omap);
+               if (ret) {
+                       dev_err(dev, "Failed to create DT children: %d\n", ret);
+                       goto err_alloc;
+               }
 
-       omap_usbhs_init(dev);
-       ret = omap_usbhs_alloc_children(pdev);
-       if (ret) {
-               dev_err(dev, "omap_usbhs_alloc_children failed\n");
-               goto err_alloc;
+       } else {
+               ret = omap_usbhs_alloc_children(pdev);
+               if (ret) {
+                       dev_err(dev, "omap_usbhs_alloc_children failed: %d\n",
+                                               ret);
+                       goto err_alloc;
+               }
        }
 
-       goto end_probe;
+       return 0;
 
 err_alloc:
-       omap_usbhs_deinit(&pdev->dev);
-       iounmap(omap->uhh_base);
+       for (i = 0; i < omap->nports; i++) {
+               if (!IS_ERR(omap->utmi_clk[i]))
+                       clk_put(omap->utmi_clk[i]);
+               if (!IS_ERR(omap->hsic60m_clk[i]))
+                       clk_put(omap->hsic60m_clk[i]);
+               if (!IS_ERR(omap->hsic480m_clk[i]))
+                       clk_put(omap->hsic480m_clk[i]);
+       }
 
-err_init_60m_fclk:
        clk_put(omap->init_60m_fclk);
 
-err_usbhost_p2_fck:
-       clk_put(omap->usbhost_p2_fck);
-
-err_usbhost_p1_fck:
-       clk_put(omap->usbhost_p1_fck);
-
-err_xclk60mhsp2_ck:
+err_init60m:
        clk_put(omap->xclk60mhsp2_ck);
 
-err_utmi_p2_fck:
-       clk_put(omap->utmi_p2_fck);
-
-err_xclk60mhsp1_ck:
+err_xclk60mhsp2:
        clk_put(omap->xclk60mhsp1_ck);
 
-err_utmi_p1_fck:
-       clk_put(omap->utmi_p1_fck);
+err_xclk60mhsp1:
+       clk_put(omap->utmi_p2_gfclk);
 
-err_end:
-       clk_put(omap->ehci_logic_fck);
+err_p2_gfclk:
+       clk_put(omap->utmi_p1_gfclk);
+
+err_p1_gfclk:
+       if (!IS_ERR(omap->ehci_logic_fck))
+               clk_put(omap->ehci_logic_fck);
+
+err_mem:
        pm_runtime_disable(dev);
-       kfree(omap);
 
-end_probe:
        return ret;
 }
 
+static int usbhs_omap_remove_child(struct device *dev, void *data)
+{
+       dev_info(dev, "unregistering\n");
+       platform_device_unregister(to_platform_device(dev));
+       return 0;
+}
+
 /**
  * usbhs_omap_remove - shutdown processing for UHH & TLL HCDs
  * @pdev: USB Host Controller being removed
@@ -657,20 +851,30 @@ end_probe:
 static int usbhs_omap_remove(struct platform_device *pdev)
 {
        struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < omap->nports; i++) {
+               if (!IS_ERR(omap->utmi_clk[i]))
+                       clk_put(omap->utmi_clk[i]);
+               if (!IS_ERR(omap->hsic60m_clk[i]))
+                       clk_put(omap->hsic60m_clk[i]);
+               if (!IS_ERR(omap->hsic480m_clk[i]))
+                       clk_put(omap->hsic480m_clk[i]);
+       }
 
-       omap_usbhs_deinit(&pdev->dev);
-       iounmap(omap->uhh_base);
        clk_put(omap->init_60m_fclk);
-       clk_put(omap->usbhost_p2_fck);
-       clk_put(omap->usbhost_p1_fck);
+       clk_put(omap->utmi_p1_gfclk);
+       clk_put(omap->utmi_p2_gfclk);
        clk_put(omap->xclk60mhsp2_ck);
-       clk_put(omap->utmi_p2_fck);
        clk_put(omap->xclk60mhsp1_ck);
-       clk_put(omap->utmi_p1_fck);
-       clk_put(omap->ehci_logic_fck);
+
+       if (!IS_ERR(omap->ehci_logic_fck))
+               clk_put(omap->ehci_logic_fck);
+
        pm_runtime_disable(&pdev->dev);
-       kfree(omap);
 
+       /* remove children */
+       device_for_each_child(&pdev->dev, NULL, usbhs_omap_remove_child);
        return 0;
 }
 
@@ -679,13 +883,22 @@ static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
        .runtime_resume         = usbhs_runtime_resume,
 };
 
+static const struct of_device_id usbhs_omap_dt_ids[] = {
+       { .compatible = "ti,usbhs-host" },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, usbhs_omap_dt_ids);
+
+
 static struct platform_driver usbhs_omap_driver = {
        .driver = {
                .name           = (char *)usbhs_driver_name,
                .owner          = THIS_MODULE,
                .pm             = &usbhsomap_dev_pm_ops,
+               .of_match_table = of_match_ptr(usbhs_omap_dt_ids),
        },
-       .remove         = __exit_p(usbhs_omap_remove),
+       .remove         = usbhs_omap_remove,
 };
 
 MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>");
index eb869153206d27f3d58a45d177f19a1076b3a1dd..681164e9713baf6b4e6eb2b769404247a1598319 100644 (file)
@@ -1,8 +1,9 @@
 /**
  * omap-usb-tll.c - The USB TLL driver for OMAP EHCI & OHCI
  *
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2012-2013 Texas Instruments Incorporated - http://www.ti.com
  * Author: Keshava Munegowda <keshava_mgowda@ti.com>
+ * Author: Roger Quadros <rogerq@ti.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2  of
@@ -27,6 +28,9 @@
 #include <linux/err.h>
 #include <linux/pm_runtime.h>
 #include <linux/platform_data/usb-omap.h>
+#include <linux/of.h>
+
+#include "omap-usb.h"
 
 #define USBTLL_DRIVER_NAME     "usbhs_tll"
 
 
 #define        OMAP_TLL_CHANNEL_CONF(num)                      (0x040 + 0x004 * num)
 #define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT           24
+#define OMAP_TLL_CHANNEL_CONF_DRVVBUS                  (1 << 16)
+#define OMAP_TLL_CHANNEL_CONF_CHRGVBUS                 (1 << 15)
 #define        OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF            (1 << 11)
 #define        OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE         (1 << 10)
 #define        OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE              (1 << 9)
 #define        OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE               (1 << 8)
+#define OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI    (2 << 1)
 #define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS            (1 << 1)
 #define        OMAP_TLL_CHANNEL_CONF_CHANEN                    (1 << 0)
 
 #define OMAP_USBTLL_REV1               0x00000015      /* OMAP3 */
 #define OMAP_USBTLL_REV2               0x00000018      /* OMAP 3630 */
 #define OMAP_USBTLL_REV3               0x00000004      /* OMAP4 */
+#define OMAP_USBTLL_REV4               0x00000006      /* OMAP5 */
 
 #define is_ehci_tll_mode(x)    (x == OMAP_EHCI_PORT_MODE_TLL)
 
+/* only PHY and UNUSED modes don't need TLL */
+#define omap_usb_mode_needs_tll(x)     ((x) != OMAP_USBHS_PORT_MODE_UNUSED &&\
+                                        (x) != OMAP_EHCI_PORT_MODE_PHY)
+
 struct usbtll_omap {
-       struct clk                              *usbtll_p1_fck;
-       struct clk                              *usbtll_p2_fck;
-       struct usbtll_omap_platform_data        platdata;
-       /* secure the register updates */
-       spinlock_t                              lock;
+       int                                     nch;    /* num. of channels */
+       struct clk                              **ch_clk;
+       void __iomem                            *base;
 };
 
 /*-------------------------------------------------------------------------*/
 
-const char usbtll_driver_name[] = USBTLL_DRIVER_NAME;
-struct platform_device *tll_pdev;
+static const char usbtll_driver_name[] = USBTLL_DRIVER_NAME;
+static struct device   *tll_dev;
+static DEFINE_SPINLOCK(tll_lock);      /* serialize access to tll_dev */
 
 /*-------------------------------------------------------------------------*/
 
@@ -203,84 +214,147 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode)
 static int usbtll_omap_probe(struct platform_device *pdev)
 {
        struct device                           *dev =  &pdev->dev;
-       struct usbtll_omap_platform_data        *pdata = dev->platform_data;
-       void __iomem                            *base;
        struct resource                         *res;
        struct usbtll_omap                      *tll;
-       unsigned                                reg;
-       unsigned long                           flags;
        int                                     ret = 0;
-       int                                     i, ver, count;
+       int                                     i, ver;
 
        dev_dbg(dev, "starting TI HSUSB TLL Controller\n");
 
-       tll = kzalloc(sizeof(struct usbtll_omap), GFP_KERNEL);
+       tll = devm_kzalloc(dev, sizeof(struct usbtll_omap), GFP_KERNEL);
        if (!tll) {
                dev_err(dev, "Memory allocation failed\n");
-               ret = -ENOMEM;
-               goto end;
-       }
-
-       spin_lock_init(&tll->lock);
-
-       for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
-               tll->platdata.port_mode[i] = pdata->port_mode[i];
-
-       tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk");
-       if (IS_ERR(tll->usbtll_p1_fck)) {
-               ret = PTR_ERR(tll->usbtll_p1_fck);
-               dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret);
-               goto err_tll;
-       }
-
-       tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk");
-       if (IS_ERR(tll->usbtll_p2_fck)) {
-               ret = PTR_ERR(tll->usbtll_p2_fck);
-               dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret);
-               goto err_usbtll_p1_fck;
+               return -ENOMEM;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "usb tll get resource failed\n");
-               ret = -ENODEV;
-               goto err_usbtll_p2_fck;
-       }
-
-       base = ioremap(res->start, resource_size(res));
-       if (!base) {
-               dev_err(dev, "TLL ioremap failed\n");
-               ret = -ENOMEM;
-               goto err_usbtll_p2_fck;
+       tll->base = devm_request_and_ioremap(dev, res);
+       if (!tll->base) {
+               ret = -EADDRNOTAVAIL;
+               dev_err(dev, "Resource request/ioremap failed:%d\n", ret);
+               return ret;
        }
 
        platform_set_drvdata(pdev, tll);
        pm_runtime_enable(dev);
        pm_runtime_get_sync(dev);
 
-       spin_lock_irqsave(&tll->lock, flags);
-
-       ver =  usbtll_read(base, OMAP_USBTLL_REVISION);
+       ver =  usbtll_read(tll->base, OMAP_USBTLL_REVISION);
        switch (ver) {
        case OMAP_USBTLL_REV1:
-       case OMAP_USBTLL_REV2:
-               count = OMAP_TLL_CHANNEL_COUNT;
+       case OMAP_USBTLL_REV4:
+               tll->nch = OMAP_TLL_CHANNEL_COUNT;
                break;
+       case OMAP_USBTLL_REV2:
        case OMAP_USBTLL_REV3:
-               count = OMAP_REV2_TLL_CHANNEL_COUNT;
+               tll->nch = OMAP_REV2_TLL_CHANNEL_COUNT;
                break;
        default:
-               dev_err(dev, "TLL version failed\n");
-               ret = -ENODEV;
-               goto err_ioremap;
+               tll->nch = OMAP_TLL_CHANNEL_COUNT;
+               dev_dbg(dev,
+                "USB TLL Rev : 0x%x not recognized, assuming %d channels\n",
+                       ver, tll->nch);
+               break;
+       }
+
+       tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk *) * tll->nch,
+                                               GFP_KERNEL);
+       if (!tll->ch_clk) {
+               ret = -ENOMEM;
+               dev_err(dev, "Couldn't allocate memory for channel clocks\n");
+               goto err_clk_alloc;
        }
 
-       if (is_ehci_tll_mode(pdata->port_mode[0]) ||
-           is_ehci_tll_mode(pdata->port_mode[1]) ||
-           is_ehci_tll_mode(pdata->port_mode[2]) ||
-           is_ohci_port(pdata->port_mode[0]) ||
-           is_ohci_port(pdata->port_mode[1]) ||
-           is_ohci_port(pdata->port_mode[2])) {
+       for (i = 0; i < tll->nch; i++) {
+               char clkname[] = "usb_tll_hs_usb_chx_clk";
+
+               snprintf(clkname, sizeof(clkname),
+                                       "usb_tll_hs_usb_ch%d_clk", i);
+               tll->ch_clk[i] = clk_get(dev, clkname);
+
+               if (IS_ERR(tll->ch_clk[i]))
+                       dev_dbg(dev, "can't get clock : %s\n", clkname);
+       }
+
+       pm_runtime_put_sync(dev);
+       /* only after this can omap_tll_enable/disable work */
+       spin_lock(&tll_lock);
+       tll_dev = dev;
+       spin_unlock(&tll_lock);
+
+       return 0;
+
+err_clk_alloc:
+       pm_runtime_put_sync(dev);
+       pm_runtime_disable(dev);
+
+       return ret;
+}
+
+/**
+ * usbtll_omap_remove - shutdown processing for UHH & TLL HCDs
+ * @pdev: USB Host Controller being removed
+ *
+ * Reverses the effect of usbtll_omap_probe().
+ */
+static int usbtll_omap_remove(struct platform_device *pdev)
+{
+       struct usbtll_omap *tll = platform_get_drvdata(pdev);
+       int i;
+
+       spin_lock(&tll_lock);
+       tll_dev = NULL;
+       spin_unlock(&tll_lock);
+
+       for (i = 0; i < tll->nch; i++)
+               if (!IS_ERR(tll->ch_clk[i]))
+                       clk_put(tll->ch_clk[i]);
+
+       pm_runtime_disable(&pdev->dev);
+       return 0;
+}
+
+static const struct of_device_id usbtll_omap_dt_ids[] = {
+       { .compatible = "ti,usbhs-tll" },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, usbtll_omap_dt_ids);
+
+static struct platform_driver usbtll_omap_driver = {
+       .driver = {
+               .name           = (char *)usbtll_driver_name,
+               .owner          = THIS_MODULE,
+               .of_match_table = of_match_ptr(usbtll_omap_dt_ids),
+       },
+       .probe          = usbtll_omap_probe,
+       .remove         = usbtll_omap_remove,
+};
+
+int omap_tll_init(struct usbhs_omap_platform_data *pdata)
+{
+       int i;
+       bool needs_tll;
+       unsigned reg;
+       struct usbtll_omap *tll;
+
+       spin_lock(&tll_lock);
+
+       if (!tll_dev) {
+               spin_unlock(&tll_lock);
+               return -ENODEV;
+       }
+
+       tll = dev_get_drvdata(tll_dev);
+
+       needs_tll = false;
+       for (i = 0; i < tll->nch; i++)
+               needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]);
+
+       pm_runtime_get_sync(tll_dev);
+
+       if (needs_tll) {
+               void __iomem *base = tll->base;
 
                /* Program Common TLL register */
                reg = usbtll_read(base, OMAP_TLL_SHARED_CONF);
@@ -292,7 +366,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
                usbtll_write(base, OMAP_TLL_SHARED_CONF, reg);
 
                /* Enable channels now */
-               for (i = 0; i < count; i++) {
+               for (i = 0; i < tll->nch; i++) {
                        reg = usbtll_read(base, OMAP_TLL_CHANNEL_CONF(i));
 
                        if (is_ohci_port(pdata->port_mode[i])) {
@@ -308,6 +382,15 @@ static int usbtll_omap_probe(struct platform_device *pdev)
                                reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE
                                        | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
                                        | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE);
+                       } else if (pdata->port_mode[i] ==
+                                       OMAP_EHCI_PORT_MODE_HSIC) {
+                               /*
+                                * HSIC Mode requires UTMI port configurations
+                                */
+                               reg |= OMAP_TLL_CHANNEL_CONF_DRVVBUS
+                                | OMAP_TLL_CHANNEL_CONF_CHRGVBUS
+                                | OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI
+                                | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF;
                        } else {
                                continue;
                        }
@@ -320,131 +403,78 @@ static int usbtll_omap_probe(struct platform_device *pdev)
                }
        }
 
-err_ioremap:
-       spin_unlock_irqrestore(&tll->lock, flags);
-       iounmap(base);
-       pm_runtime_put_sync(dev);
-       tll_pdev = pdev;
-       if (!ret)
-               goto end;
-       pm_runtime_disable(dev);
-
-err_usbtll_p2_fck:
-       clk_put(tll->usbtll_p2_fck);
-
-err_usbtll_p1_fck:
-       clk_put(tll->usbtll_p1_fck);
-
-err_tll:
-       kfree(tll);
-
-end:
-       return ret;
-}
+       pm_runtime_put_sync(tll_dev);
 
-/**
- * usbtll_omap_remove - shutdown processing for UHH & TLL HCDs
- * @pdev: USB Host Controller being removed
- *
- * Reverses the effect of usbtll_omap_probe().
- */
-static int usbtll_omap_remove(struct platform_device *pdev)
-{
-       struct usbtll_omap *tll = platform_get_drvdata(pdev);
+       spin_unlock(&tll_lock);
 
-       clk_put(tll->usbtll_p2_fck);
-       clk_put(tll->usbtll_p1_fck);
-       pm_runtime_disable(&pdev->dev);
-       kfree(tll);
        return 0;
 }
+EXPORT_SYMBOL_GPL(omap_tll_init);
 
-static int usbtll_runtime_resume(struct device *dev)
+int omap_tll_enable(struct usbhs_omap_platform_data *pdata)
 {
-       struct usbtll_omap                      *tll = dev_get_drvdata(dev);
-       struct usbtll_omap_platform_data        *pdata = &tll->platdata;
-       unsigned long                           flags;
+       int i;
+       struct usbtll_omap *tll;
 
-       dev_dbg(dev, "usbtll_runtime_resume\n");
+       spin_lock(&tll_lock);
 
-       if (!pdata) {
-               dev_dbg(dev, "missing platform_data\n");
-               return  -ENODEV;
+       if (!tll_dev) {
+               spin_unlock(&tll_lock);
+               return -ENODEV;
        }
 
-       spin_lock_irqsave(&tll->lock, flags);
+       tll = dev_get_drvdata(tll_dev);
+
+       pm_runtime_get_sync(tll_dev);
 
-       if (is_ehci_tll_mode(pdata->port_mode[0]))
-               clk_enable(tll->usbtll_p1_fck);
+       for (i = 0; i < tll->nch; i++) {
+               if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
+                       int r;
+
+                       if (IS_ERR(tll->ch_clk[i]))
+                               continue;
 
-       if (is_ehci_tll_mode(pdata->port_mode[1]))
-               clk_enable(tll->usbtll_p2_fck);
+                       r = clk_enable(tll->ch_clk[i]);
+                       if (r) {
+                               dev_err(tll_dev,
+                                "Error enabling ch %d clock: %d\n", i, r);
+                       }
+               }
+       }
 
-       spin_unlock_irqrestore(&tll->lock, flags);
+       spin_unlock(&tll_lock);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(omap_tll_enable);
 
-static int usbtll_runtime_suspend(struct device *dev)
+int omap_tll_disable(struct usbhs_omap_platform_data *pdata)
 {
-       struct usbtll_omap                      *tll = dev_get_drvdata(dev);
-       struct usbtll_omap_platform_data        *pdata = &tll->platdata;
-       unsigned long                           flags;
+       int i;
+       struct usbtll_omap *tll;
 
-       dev_dbg(dev, "usbtll_runtime_suspend\n");
+       spin_lock(&tll_lock);
 
-       if (!pdata) {
-               dev_dbg(dev, "missing platform_data\n");
-               return  -ENODEV;
+       if (!tll_dev) {
+               spin_unlock(&tll_lock);
+               return -ENODEV;
        }
 
-       spin_lock_irqsave(&tll->lock, flags);
+       tll = dev_get_drvdata(tll_dev);
 
-       if (is_ehci_tll_mode(pdata->port_mode[0]))
-               clk_disable(tll->usbtll_p1_fck);
+       for (i = 0; i < tll->nch; i++) {
+               if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
+                       if (!IS_ERR(tll->ch_clk[i]))
+                               clk_disable(tll->ch_clk[i]);
+               }
+       }
 
-       if (is_ehci_tll_mode(pdata->port_mode[1]))
-               clk_disable(tll->usbtll_p2_fck);
+       pm_runtime_put_sync(tll_dev);
 
-       spin_unlock_irqrestore(&tll->lock, flags);
+       spin_unlock(&tll_lock);
 
        return 0;
 }
-
-static const struct dev_pm_ops usbtllomap_dev_pm_ops = {
-       SET_RUNTIME_PM_OPS(usbtll_runtime_suspend,
-                          usbtll_runtime_resume,
-                          NULL)
-};
-
-static struct platform_driver usbtll_omap_driver = {
-       .driver = {
-               .name           = (char *)usbtll_driver_name,
-               .owner          = THIS_MODULE,
-               .pm             = &usbtllomap_dev_pm_ops,
-       },
-       .probe          = usbtll_omap_probe,
-       .remove         = usbtll_omap_remove,
-};
-
-int omap_tll_enable(void)
-{
-       if (!tll_pdev) {
-               pr_err("missing omap usbhs tll platform_data\n");
-               return  -ENODEV;
-       }
-       return pm_runtime_get_sync(&tll_pdev->dev);
-}
-EXPORT_SYMBOL_GPL(omap_tll_enable);
-
-int omap_tll_disable(void)
-{
-       if (!tll_pdev) {
-               pr_err("missing omap usbhs tll platform_data\n");
-               return  -ENODEV;
-       }
-       return pm_runtime_put_sync(&tll_pdev->dev);
-}
 EXPORT_SYMBOL_GPL(omap_tll_disable);
 
 MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>");
index 972aa961b06489e60752941668ef79548167f451..2a508b6aeac8d9595eaf7dff4e07003eb943289e 100644 (file)
@@ -1,2 +1,3 @@
-extern int omap_tll_enable(void);
-extern int omap_tll_disable(void);
+extern int omap_tll_init(struct usbhs_omap_platform_data *pdata);
+extern int omap_tll_enable(struct usbhs_omap_platform_data *pdata);
+extern int omap_tll_disable(struct usbhs_omap_platform_data *pdata);
diff --git a/drivers/mfd/palmas-gpadc.c b/drivers/mfd/palmas-gpadc.c
new file mode 100644 (file)
index 0000000..1c9c21d
--- /dev/null
@@ -0,0 +1,1150 @@
+/*
+ * PALMAS GPADC module driver
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ * Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/palmas.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+struct palmas_ideal_code {
+       s16 code1;
+       s16 code2;
+       s16 v1;
+       s16 v2;
+};
+
+static struct palmas_ideal_code palmas_ideal[PALMAS_MAX_CHANNELS] = {
+       { /* Channel 0 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 630,
+               .v2 = 950,
+       },
+       { /* Channel 1 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 630,
+               .v2 = 950,
+       },
+       { /* Channel 2 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 1260,
+               .v2 = 1900,
+       },
+       { /* Channel 3 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 630,
+               .v2 = 950,
+       },
+       { /* Channel 4 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 630,
+               .v2 = 950,
+       },
+       { /* Channel 5 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 630,
+               .v2 = 950,
+       },
+       { /* Channel 6 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 2520,
+               .v2 = 3800,
+       },
+       { /* Channel 7 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 2520,
+               .v2 = 3800,
+       },
+       { /* Channel 8 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 3150,
+               .v2 = 4750,
+       },
+       { /* Channel 9 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 5670,
+               .v2 = 8550,
+       },
+       { /* Channel 10 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 3465,
+               .v2 = 5225,
+       },
+       { /* Channel 11 */
+       },
+       { /* Channel 12 */
+       },
+       { /* Channel 13 */
+       },
+       { /* Channel 14 */
+               .code1 = 2064,
+               .code2 = 3112,
+               .v1 = 3645,
+               .v2 = 5225,
+       },
+       { /* Channel 15 */
+       },
+};
+
+static int palmas_gpadc_read(struct palmas *palmas, unsigned int reg,
+               unsigned int *dest)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_GPADC_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_GPADC_BASE, reg);
+
+       return regmap_read(palmas->regmap[slave], addr, dest);
+}
+
+static int palmas_gpadc_write(struct palmas *palmas, unsigned int reg,
+               unsigned int data)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_GPADC_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_GPADC_BASE, reg);
+
+       return regmap_write(palmas->regmap[slave], addr, data);
+}
+
+static int palmas_gpadc_trim_read_block(struct palmas *palmas, unsigned int reg,
+               u8 *dest, size_t count)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_TRIM_GPADC_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_TRIM_GPADC_BASE, reg);
+
+       return regmap_bulk_read(palmas->regmap[slave], addr, dest, count);
+}
+
+static irqreturn_t palmas_gpadc_irq_handler(int irq, void *_gpadc)
+{
+       struct palmas_gpadc *gpadc = _gpadc;
+
+       complete(&gpadc->irq_complete);
+
+       return IRQ_HANDLED;
+}
+
+static void palmas_gpadc_calculate_values(struct palmas_gpadc *gpadc, int channel,
+                               s32 raw_code, struct palmas_gpadc_result *res)
+{
+       int channel_value = 0;
+       s32 corrected_code;
+
+       res->raw_code = raw_code;
+
+       /* Do TRIM corrections */
+       /* No correction for channels 11-13,15 */
+       if (((channel >= 11) && (channel <= 13)) || (channel == 15)) {
+               corrected_code = raw_code;
+               channel_value = raw_code;
+       } else {
+               corrected_code = ((raw_code * 1000) -
+               gpadc->palmas_cal_tbl[channel].offset_error) /
+                       gpadc->palmas_cal_tbl[channel].gain_error;
+
+               channel_value = corrected_code *
+                               gpadc->palmas_cal_tbl[channel].gain;
+
+               /* Shift back into mV range */
+               channel_value /= 1000;
+       }
+
+       dev_dbg(gpadc->dev, "GPADC raw: %d", raw_code);
+       dev_dbg(gpadc->dev, "GPADC cor: %d", corrected_code);
+       dev_dbg(gpadc->dev, "GPADC val: %d", channel_value);
+
+       res->corrected_code = corrected_code;
+       res->result = channel_value;
+}
+
+static s32 palmas_gpadc_calculate_reverse(struct palmas_gpadc *gpadc,
+               int channel, int value)
+{
+       s32 corrected_code, raw_code;
+
+       /* Do reverse TRIM corrections */
+       /* No correction for channels 11-13 */
+       if ((channel >= 11) && (channel <= 13)) {
+               raw_code =  value;
+               corrected_code = value;
+       } else {
+               value *= 1000;
+               corrected_code = value / gpadc->palmas_cal_tbl[channel].gain;
+               raw_code = ((corrected_code *
+                               gpadc->palmas_cal_tbl[channel].gain_error) +
+                               gpadc->palmas_cal_tbl[channel].offset_error) /
+                               1000;
+       }
+
+       dev_dbg(gpadc->dev, "GPADC raw: %d", raw_code);
+       dev_dbg(gpadc->dev, "GPADC cor: %d", corrected_code);
+       dev_dbg(gpadc->dev, "GPADC val: %d", value);
+
+       return raw_code;
+}
+
+int palmas_gpadc_conversion(struct palmas_gpadc *gpadc, int channel,
+                               struct palmas_gpadc_result *res)
+{
+       unsigned int reg;
+       int ret = 0;
+       s32 raw_code;
+
+       if (unlikely(!gpadc)) {
+               printk(KERN_ERR "palmas-gpadc: no GPADC exists yet!\n");
+               return -EINVAL;
+       }
+
+       if (unlikely(!res))
+               return -EINVAL;
+
+       mutex_lock(&gpadc->reading_lock);
+
+       /* Setup Conversion */
+       reg = PALMAS_GPADC_SW_SELECT_SW_CONV_EN |
+                       PALMAS_GPADC_SW_SELECT_SW_START_CONV0 |
+                       (channel & PALMAS_GPADC_SW_SELECT_SW_CONV0_SEL_MASK);
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_SW_SELECT, reg);
+       if (ret)
+               return ret;
+
+       /* Wait for IRQ to finish*/
+       wait_for_completion_interruptible_timeout(&gpadc->irq_complete,
+                                               msecs_to_jiffies(5000));
+
+       /* Read the results */
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_SW_CONV0_LSB,
+                               &reg);
+       if (ret)
+               return ret;
+
+       raw_code = reg;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_SW_CONV0_MSB,
+                               &reg);
+       if (ret)
+               return ret;
+
+       raw_code += reg << 8;
+
+       palmas_gpadc_calculate_values(gpadc, channel, raw_code, res);
+
+       mutex_unlock(&gpadc->reading_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(palmas_gpadc_conversion);
+
+static ssize_t show_channel(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct palmas_gpadc *gpadc = dev_get_drvdata(dev);
+       int ret;
+       struct palmas_gpadc_result result;
+
+       ret = palmas_gpadc_conversion(gpadc, attr->index, &result);
+
+       return sprintf(buf, "%d\n", result.result);
+}
+
+#define in_channel(index) \
+static SENSOR_DEVICE_ATTR(in##index##_channel, S_IRUGO, show_channel, \
+       NULL, index)
+
+in_channel(0);
+in_channel(1);
+in_channel(2);
+in_channel(3);
+in_channel(4);
+in_channel(5);
+in_channel(6);
+in_channel(7);
+in_channel(8);
+in_channel(9);
+in_channel(10);
+in_channel(11);
+in_channel(12);
+in_channel(13);
+in_channel(14);
+
+#define IN_ATTRS_CHANNEL(X)\
+       (&sensor_dev_attr_in##X##_channel.dev_attr.attr)
+
+static struct attribute *palmas_gpadc_attributes[] = {
+       IN_ATTRS_CHANNEL(0),
+       IN_ATTRS_CHANNEL(1),
+       IN_ATTRS_CHANNEL(2),
+       IN_ATTRS_CHANNEL(3),
+       IN_ATTRS_CHANNEL(4),
+       IN_ATTRS_CHANNEL(5),
+       IN_ATTRS_CHANNEL(6),
+       IN_ATTRS_CHANNEL(7),
+       IN_ATTRS_CHANNEL(8),
+       IN_ATTRS_CHANNEL(9),
+       IN_ATTRS_CHANNEL(10),
+       IN_ATTRS_CHANNEL(11),
+       IN_ATTRS_CHANNEL(12),
+       IN_ATTRS_CHANNEL(13),
+       IN_ATTRS_CHANNEL(14),
+       NULL
+};
+
+static const struct attribute_group palmas_gpadc_group = {
+       .attrs = palmas_gpadc_attributes,
+};
+
+/* @brief Current Source selection for GPADC Channel 0 input
+ *
+ * The current is selected from the following values.
+ * 0: 0uA
+ * 1: 5uA
+ * 2: 15uA
+ * 3: 20uA
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param cursel value from table to select current
+ * @return error
+ */
+int palmas_gpadc_current_src_ch0(struct palmas_gpadc *gpadc, int cursel)
+{
+       unsigned int reg;
+       int ret = 0;
+
+       palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_CTRL1, &reg);
+       if (ret)
+               return ret;
+
+       reg &= ~PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_MASK;
+       cursel <<= PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_SHIFT;
+       cursel &= PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_MASK;
+       reg |= cursel;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_CTRL1, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_current_src_ch0);
+
+/* @brief Current Source selection for GPADC Channel 3 input
+ *
+ * The current is selected from the following values.
+ * 0: 0uA
+ * 1: 10uA
+ * 2: 400uA
+ * 3: 800uA
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param cursel value from table to select current
+ * @return error
+ */
+int palmas_gpadc_current_src_ch3(struct palmas_gpadc *gpadc, int cursel)
+{
+       unsigned int reg;
+       int ret = 0;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_CTRL1, &reg);
+       if (ret)
+               return ret;
+
+       reg &= ~PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_MASK;
+       cursel <<= PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_SHIFT;
+       cursel &= PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_MASK;
+       reg |= cursel;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_CTRL1, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_current_src_ch3);
+
+/* @brief GPADC Force control
+ *
+ * Enabling FORCE will lower the latency for GPADC conversions but will draw
+ * more power as GPADC unit is always on.
+ *
+ * 0 : GPADC power is controlled by conversion
+ * 1 : GPADC is always on.
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param force boolean for enable/disable
+ * @return error
+ */
+int palmas_gpadc_force(struct palmas_gpadc *gpadc, int force)
+{
+       unsigned int reg;
+       int ret = 0;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_CTRL1, &reg);
+       if (ret)
+               return ret;
+
+       if (force)
+               reg |= PALMAS_GPADC_CTRL1_GPADC_FORCE;
+       else
+               reg &= ~PALMAS_GPADC_CTRL1_GPADC_FORCE;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_CTRL1, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_force);
+
+/* @brief conversion timeslot selection
+ *
+ * This selects the time between auto conversions in GPADC in binary
+ * the value are given as follows
+ *
+ * 0000: 1/32s
+ * 0001: 1/16s
+ * 0010: 1/8s
+ * 1110: 512s
+ * 1111: 1024s
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param timeslot value from the table
+ * @return error
+ */
+int palmas_gpadc_counter_conv(struct palmas_gpadc *gpadc, int timeslot)
+{
+       unsigned int reg;
+       int ret = 0;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_AUTO_CTRL, &reg);
+       if (ret)
+               return ret;
+
+       reg &= ~PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK;
+       timeslot <<= PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_SHIFT;
+       timeslot &= PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK;
+       reg |= timeslot;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_AUTO_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_counter_conv);
+
+/* @brief set the channels for auto conversion
+ *
+ * This sets the two channels for the auto conversion in the GPADC
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param ch0 the channel for AUTO_CONV0
+ * @return error
+ */
+int palmas_gpadc_auto_conv0_channel(struct palmas_gpadc *gpadc, int ch0)
+{
+       unsigned int reg;
+       int ret;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_AUTO_SELECT, &reg);
+       if (ret)
+               return ret;
+
+       reg |= ch0 & PALMAS_GPADC_AUTO_SELECT_AUTO_CONV0_SEL_MASK;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_AUTO_SELECT, reg);
+
+       gpadc->conv0_channel = ch0;
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_auto_conv0_channel);
+
+/* @brief set the channels for auto conversion
+ *
+ * This sets the two channels for the auto conversion in the GPADC
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param ch1 the channel for AUTO_CONV1
+ * @return error
+ */
+int palmas_gpadc_auto_conv1_channel(struct palmas_gpadc *gpadc, int ch1)
+{
+       unsigned int reg;
+       int ret;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_AUTO_SELECT, &reg);
+       if (ret)
+               return ret;
+
+       reg &= PALMAS_GPADC_AUTO_SELECT_AUTO_CONV1_SEL_MASK;
+       reg |= ch1 << PALMAS_GPADC_AUTO_SELECT_AUTO_CONV1_SEL_SHIFT;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_AUTO_SELECT, reg);
+
+       gpadc->conv1_channel = ch1;
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_auto_conv1_channel);
+
+/* @brief set the threshold for auto conversion
+ *
+ * This sets the two channels for the auto conversion in the GPADC
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param th0 the channel for AUTO_CONV0
+ * @param lt set if IRQ is generated for less than, otherwise greater than
+ * @return error
+ */
+int palmas_gpadc_auto_conv0_threshold(struct palmas_gpadc *gpadc, int th0,
+               int lt)
+{
+       unsigned int reg;
+       int ret;
+       s32 raw_code;
+
+       raw_code = palmas_gpadc_calculate_reverse(gpadc, gpadc->conv0_channel,
+                       th0);
+
+       reg = raw_code & PALMAS_GPADC_THRES_CONV0_LSB_THRES_CONV0_LSB_MASK;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_THRES_CONV0_LSB,
+                       reg);
+       if (ret)
+               return ret;
+
+       reg = (raw_code >> 8) &
+                       PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_MSB_MASK;
+       reg |= lt << PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_POL_SHIFT;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_THRES_CONV0_LSB,
+                       reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_auto_conv0_threshold);
+
+/* @brief set the threshold for auto conversion
+ *
+ * This sets the two channels for the auto conversion in the GPADC
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param th1 the channel for AUTO_CONV0
+ * @param lt set if IRQ is generated for less than, otherwise greater than
+ * @return error
+ */
+int palmas_gpadc_auto_conv1_threshold(struct palmas_gpadc *gpadc, int th1,
+               int lt)
+{
+       unsigned int reg;
+       int ret;
+       s32 raw_code;
+
+       raw_code = palmas_gpadc_calculate_reverse(gpadc, gpadc->conv1_channel,
+                       th1);
+
+       reg = raw_code & PALMAS_GPADC_THRES_CONV1_LSB_THRES_CONV1_LSB_MASK;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_THRES_CONV1_LSB,
+                       reg);
+       if (ret)
+               return ret;
+
+       reg = (raw_code >> 8) &
+                       PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_MSB_MASK;
+       reg |= lt << PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_POL_SHIFT;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_THRES_CONV0_LSB,
+                       reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_auto_conv1_threshold);
+
+/* @brief enable the auto conv0
+ *
+ * This function starts or stops the conv0 auto conversion.
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param enable boolean to set enable state
+ * @return error
+ */
+int palmas_gpadc_auto_conv0_enable(struct palmas_gpadc *gpadc, int enable)
+{
+       unsigned int reg;
+       int ret;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_AUTO_CTRL, &reg);
+       if (ret)
+               return ret;
+
+       if (enable)
+               reg |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN;
+       else
+               reg &= ~PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_AUTO_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_auto_conv0_enable);
+
+/* @brief enable the auto conv1
+ *
+ * This function starts or stops the conv1 auto conversion.
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param enable boolean to set enable state
+ * @return error
+ */
+int palmas_gpadc_auto_conv1_enable(struct palmas_gpadc *gpadc, int enable)
+{
+       unsigned int reg;
+       int ret;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_AUTO_CTRL, &reg);
+       if (ret)
+               return ret;
+
+       if (enable)
+               reg |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN;
+       else
+               reg &= ~PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_AUTO_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_auto_conv1_enable);
+
+/* @brief return the result of auto conv0 conversion
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param res pointer to the result structure
+ * @return error or result
+ */
+int palmas_gpadc_auto_conv0_result(struct palmas_gpadc *gpadc,
+               struct palmas_gpadc_result *res)
+{
+       unsigned int reg;
+       int ret;
+       s32 raw_code;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_AUTO_CONV0_LSB,
+                       &reg);
+       if (ret)
+               return ret;
+
+       raw_code = reg;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_AUTO_CONV0_LSB,
+                       &reg);
+       if (ret)
+               return ret;
+
+       raw_code += (reg &
+                       PALMAS_GPADC_AUTO_CONV0_MSB_AUTO_CONV0_MSB_MASK) << 8;
+
+       palmas_gpadc_calculate_values(gpadc, gpadc->conv0_channel,
+                       raw_code, res);
+
+       return 0;
+}
+EXPORT_SYMBOL(palmas_gpadc_auto_conv0_result);
+
+/* @brief return the result of auto conv0 conversion
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param res pointer to the result structure
+ * @return error or result
+ */
+int palmas_gpadc_auto_conv1_result(struct palmas_gpadc *gpadc,
+               struct palmas_gpadc_result *res)
+{
+       unsigned int reg;
+       int ret;
+       s32 raw_code;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_AUTO_CONV1_LSB,
+                       &reg);
+       if (ret)
+               return ret;
+
+       raw_code = reg;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_AUTO_CONV1_LSB,
+                       &reg);
+       if (ret)
+               return ret;
+
+       raw_code += (reg &
+                       PALMAS_GPADC_AUTO_CONV1_MSB_AUTO_CONV1_MSB_MASK) << 8;
+
+       palmas_gpadc_calculate_values(gpadc, gpadc->conv1_channel,
+                       raw_code, res);
+
+       return 0;
+}
+EXPORT_SYMBOL(palmas_gpadc_auto_conv1_result);
+
+/* @brief select channel for realtime conversion
+ *
+ * This function selects the channel for the realtime conversion.
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param ch select channel for realtime conversion
+ * @return error
+ */
+int palmas_gpadc_rt_channel(struct palmas_gpadc *gpadc, int ch)
+{
+       unsigned int reg;
+       int ret;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_RT_SELECT, &reg);
+       if (ret)
+               return ret;
+
+       reg &= ~PALMAS_GPADC_RT_SELECT_RT_CONV0_SEL_MASK;
+       reg |= (ch & PALMAS_GPADC_RT_SELECT_RT_CONV0_SEL_MASK);
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_RT_SELECT, reg);
+
+       gpadc->rt_channel = ch;
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_rt_channel);
+
+/* @brief enable the realtime conversion
+ *
+ * This function starts or stops the realtime conversion.
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param enable boolean to set enable state
+ * @return error
+ */
+int palmas_gpadc_rt_enable(struct palmas_gpadc *gpadc, int enable)
+{
+       unsigned int reg;
+       int ret;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_RT_SELECT, &reg);
+       if (ret)
+               return ret;
+
+       if (enable)
+               reg |= PALMAS_GPADC_RT_SELECT_RT_CONV_EN;
+       else
+               reg &= ~PALMAS_GPADC_RT_SELECT_RT_CONV_EN;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_RT_SELECT, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_gpadc_rt_enable);
+
+/* @brief return the result of realtime conversion
+ *
+ * @param gpadc pointer to the gpadc device data
+ * @param res pointer to the result structure
+ * @return error or result
+ */
+int palmas_gpadc_rt_result(struct palmas_gpadc *gpadc,
+               struct palmas_gpadc_result *res)
+{
+       unsigned int reg;
+       int ret;
+       s32 raw_code;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_RT_CONV0_LSB, &reg);
+       if (ret)
+               return ret;
+
+       raw_code = reg;
+
+       ret = palmas_gpadc_read(gpadc->palmas, PALMAS_GPADC_RT_CONV0_MSB, &reg);
+       if (ret)
+               return ret;
+
+       raw_code += (reg & PALMAS_GPADC_RT_CONV0_MSB_RT_CONV0_MSB_MASK) << 8;
+
+       palmas_gpadc_calculate_values(gpadc, gpadc->conv1_channel,
+                       raw_code, NULL);
+
+       return 0;
+}
+EXPORT_SYMBOL(palmas_gpadc_rt_result);
+
+
+/* @brief configure the current monitoring for GPADC channel 11
+ *
+ * @param gpadc pointer to the gpadc device data.
+ * @param enable enable/desable the feature.
+ * @param rext enable/disable the external resistor feature.
+ * @param channel select the SMPS to be monitored.
+ * @return error or resul
+ */
+int palmas_gpadc_configure_ilmonitor(struct palmas_gpadc *gpadc,
+               int enable, int rext, int channel)
+{
+       unsigned int reg = 0;
+       int ret;
+
+       if (channel > 6 || channel < 0)
+               return -EINVAL;
+
+       if (enable)
+               reg |= PALMAS_GPADC_SMPS_ILMONITOR_EN_SMPS_ILMON_EN;
+
+       if (rext)
+               reg |= PALMAS_GPADC_SMPS_ILMONITOR_EN_SMPS_ILMON_REXT;
+
+       reg |= channel;
+
+       ret = palmas_gpadc_write(gpadc->palmas, PALMAS_GPADC_SMPS_ILMONITOR_EN,
+                       reg);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL(palmas_gpadc_configure_ilmonitor);
+
+static struct miscdevice palmas_gpadc_device = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "palmas-gpadc",
+};
+
+static int palmas_gpadc_calibrate(struct palmas_gpadc *gpadc)
+{
+       int chn, d1 = 0, d2 = 0, b, k, gain, x1, x2, v1, v2;
+       u8 trim_regs[17];
+       int ret;
+
+       ret = palmas_gpadc_trim_read_block(gpadc->palmas,
+                       PALMAS_GPADC_TRIM1, trim_regs + 1,
+                       PALMAS_MAX_CHANNELS);
+       if (ret < 0) {
+               dev_err(gpadc->dev, "Error reading trim registers\n");
+               return ret;
+       }
+
+       /*
+        * Loop to calculate the value needed for returning voltages from
+        * GPADC not values.
+        *
+        * gain is calculated to 3 decimal places fixed point.
+        */
+       for (chn = 0; chn < PALMAS_MAX_CHANNELS; chn++) {
+               switch (chn) {
+               case 0:
+               case 1:
+               case 3:
+               case 4:
+               case 5:
+                       /* D1 */
+                       d1 = trim_regs[1];
+
+                       /* D2 */
+                       d2 = trim_regs[2];
+                       break;
+               case 2:
+                       /* D1 */
+                       d1 = trim_regs[3];
+
+                       /* D2 */
+                       d2 = trim_regs[4];
+                       break;
+               case 6:
+                       /* D1 */
+                       d1 = trim_regs[5];
+
+                       /* D2 */
+                       d2 = trim_regs[6];
+                       break;
+               case 7:
+                       /* D1 */
+                       d1 = trim_regs[7];
+
+                       /* D2 */
+                       d2 = trim_regs[8];
+                       break;
+               case 8:
+                       /* D1 */
+                       d1 = trim_regs[9];
+
+                       /* D2 */
+                       d2 = trim_regs[10];
+                       break;
+               case 9:
+                       /* D1 */
+                       d1 = trim_regs[11];
+
+                       /* D2 */
+                       d2 = trim_regs[12];
+                       break;
+               case 10:
+                       /* D1 */
+                       d1 = trim_regs[13];
+
+                       /* D2 */
+                       d2 = trim_regs[14];
+                       break;
+               case 14:
+                       /* D1 */
+                       d1 = trim_regs[15];
+
+                       /* D2 */
+                       d2 = trim_regs[16];
+                       break;
+               default:
+                       /* there is no calibration for other channels */
+                       continue;
+               }
+
+               /* if bit 8 is set then number is negative */
+               if (d1 & 0x80)
+                       d1 = 0 - (d1 & 0x7F);
+               if (d2 & 0x80)
+                       d2 = 0 - (d2 & 0x7F);
+
+               dev_dbg(gpadc->dev, "GPADC d1   for Chn: %d = %d", chn, d1);
+               dev_dbg(gpadc->dev, "GPADC d2   for Chn: %d = %d", chn, d2);
+
+               x1 = palmas_ideal[chn].code1;
+               x2 = palmas_ideal[chn].code2;
+
+               dev_dbg(gpadc->dev, "GPADC x1   for Chn: %d = %d", chn, x1);
+               dev_dbg(gpadc->dev, "GPADC x2   for Chn: %d = %d", chn, x2);
+
+               v1 = palmas_ideal[chn].v1;
+               v2 = palmas_ideal[chn].v2;
+
+               dev_dbg(gpadc->dev, "GPADC v1   for Chn: %d = %d", chn, v1);
+               dev_dbg(gpadc->dev, "GPADC v2   for Chn: %d = %d", chn, v2);
+
+               /* Gain */
+               gain = ((v2 - v1) * 1000) / (x2 - x1);
+
+               /* k */
+               k = 1000 + (((d2 - d1) * 1000) / (x2 - x1));
+
+               /* b */
+               b = (d1 * 1000) - (k - 1000) * x1;
+
+               gpadc->palmas_cal_tbl[chn].gain = gain;
+               gpadc->palmas_cal_tbl[chn].gain_error = k;
+               gpadc->palmas_cal_tbl[chn].offset_error = b;
+
+               dev_dbg(gpadc->dev, "GPADC Gain for Chn: %d = %d", chn, gain);
+               dev_dbg(gpadc->dev, "GPADC k    for Chn: %d = %d", chn, k);
+               dev_dbg(gpadc->dev, "GPADC b    for Chn: %d = %d", chn, b);
+
+       }
+       return 0;
+}
+
+static void palmas_dt_to_pdata(struct device_node *node,
+               struct palmas_gpadc_platform_data *pdata)
+{
+       u32 prop;
+       int ret;
+
+       ret = of_property_read_u32(node, "ti,ch3_current", &prop);
+       if (!ret) {
+               pdata->ch3_current = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,ch0_current", &prop);
+       if (!ret) {
+               pdata->ch0_current = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,bat_removal", &prop);
+       if (!ret) {
+               pdata->bat_removal = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,start_polarity", &prop);
+       if (!ret) {
+               pdata->start_polarity = prop;
+       }
+}
+
+static int palmas_gpadc_probe(struct platform_device *pdev)
+{
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+       struct palmas_gpadc_platform_data *pdata = pdev->dev.platform_data;
+       struct device_node *node = pdev->dev.of_node;
+       struct palmas_gpadc *gpadc;
+       int ret, irq;
+
+       if(node && !pdata) {
+               pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+
+               if (!pdata)
+                       return -ENOMEM;
+
+               palmas_dt_to_pdata(node, pdata);
+       }
+
+       if (!pdata)
+               return -EINVAL;
+
+       gpadc = kzalloc(sizeof(struct palmas_gpadc), GFP_KERNEL);
+       if (!gpadc)
+               return -ENOMEM;
+
+       gpadc->palmas_cal_tbl = kzalloc(
+                               sizeof(struct palmas_gpadc_calibration) *
+                               PALMAS_MAX_CHANNELS,
+                               GFP_KERNEL);
+       if (!gpadc->palmas_cal_tbl) {
+               kfree(gpadc);
+               return -ENOMEM;
+       }
+
+       gpadc->dev = &pdev->dev;
+       gpadc->palmas = palmas;
+       palmas->gpadc = gpadc;
+
+       ret = misc_register(&palmas_gpadc_device);
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not register misc_device\n");
+               goto err_misc;
+       }
+
+       irq = regmap_irq_get_virq(palmas->irq_data, PALMAS_GPADC_EOC_SW_IRQ);
+       ret = request_threaded_irq(irq, NULL, palmas_gpadc_irq_handler,
+               0, "palmas_gpadc", gpadc);
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not request irq\n");
+               goto err_irq;
+       }
+
+       gpadc->eoc_sw_irq = irq;
+
+       platform_set_drvdata(pdev, gpadc);
+
+       mutex_init(&gpadc->reading_lock);
+       init_completion(&gpadc->irq_complete);
+
+       /* Initialise CTRL1 from pdata */
+       if (pdata->ch3_current) {
+               ret = palmas_gpadc_current_src_ch3(gpadc, pdata->ch3_current);
+               if (ret) {
+                       dev_err(gpadc->dev, "CH3 Current Setting Error\n");
+                       goto err_ctrl;
+               }
+       }
+       if (pdata->ch0_current) {
+               ret = palmas_gpadc_current_src_ch0(gpadc, pdata->ch0_current);
+               if (ret) {
+                       dev_err(gpadc->dev, "CH0 Current Setting Error\n");
+                       goto err_ctrl;
+               }
+       }
+
+       palmas_gpadc_calibrate(gpadc);
+
+       ret = sysfs_create_group(&pdev->dev.kobj, &palmas_gpadc_group);
+       if (ret)
+               dev_err(&pdev->dev, "could not create sysfs files\n");
+
+       return 0;
+
+err_ctrl:
+       free_irq(irq, gpadc);
+err_irq:
+       misc_deregister(&palmas_gpadc_device);
+err_misc:
+       kfree(gpadc);
+
+       return ret;
+}
+
+static int palmas_gpadc_remove(struct platform_device *pdev)
+{
+       struct palmas_gpadc *gpadc = platform_get_drvdata(pdev);
+
+       disable_irq(gpadc->eoc_sw_irq);
+       free_irq(gpadc->eoc_sw_irq, gpadc);
+       sysfs_remove_group(&pdev->dev.kobj, &palmas_gpadc_group);
+       kfree(gpadc->palmas_cal_tbl);
+       kfree(gpadc);
+       misc_deregister(&palmas_gpadc_device);
+
+       return 0;
+}
+
+static struct of_device_id of_palmas_match_tbl[] = {
+       { .compatible = "ti,palmas-gpadc", },
+       { /* end */ }
+};
+
+static struct platform_driver palmas_gpadc_driver = {
+       .driver = {
+               .name = "palmas-gpadc",
+               .of_match_table = of_palmas_match_tbl,
+               .owner = THIS_MODULE,
+       },
+       .probe = palmas_gpadc_probe,
+       .remove = palmas_gpadc_remove,
+};
+
+static int __init palmas_gpadc_init(void)
+{
+       return platform_driver_register(&palmas_gpadc_driver);
+}
+module_init(palmas_gpadc_init);
+
+static void __exit palmas_gpadc_exit(void)
+{
+       platform_driver_unregister(&palmas_gpadc_driver);
+}
+module_exit(palmas_gpadc_exit);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("Palmas General Purpose ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:palmas-gpadc");
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
diff --git a/drivers/mfd/palmas-pwm.c b/drivers/mfd/palmas-pwm.c
new file mode 100644 (file)
index 0000000..feffe56
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Driver for Watchdog part of Palmas PMIC Chips
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ * Based on twl6030_pwm.c
+ *
+ * Author: Hemanth V <hemanthv@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/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/palmas.h>
+#include <linux/slab.h>
+
+struct pwm_device {
+       const char *label;
+       unsigned int pwm_id;
+};
+
+struct palmas *the_palmas;
+
+#define PWM_DUTY_MAX   255
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+       struct palmas *palmas = the_palmas;
+       unsigned int addr, reg;
+       int ret, slave;
+
+       if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+               return -EINVAL;
+
+       switch (period_ns) {
+       case 8000000:
+               reg = 0;
+               break;
+       case 16000000:
+               reg = PALMAS_PWM_CTRL1_PWM_FREQ_SEL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_LED_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_LED_BASE, PALMAS_PWM_CTRL1);
+
+       ret = regmap_update_bits(palmas->regmap[slave], addr,
+                       PALMAS_PWM_CTRL1_PWM_FREQ_SEL, reg);
+       if (ret)
+               goto out;
+
+       /* Calculate the duty cycle value */
+       reg = (duty_ns * PWM_DUTY_MAX) / period_ns;
+
+       addr = PALMAS_BASE_TO_REG(PALMAS_LED_BASE, PALMAS_PWM_CTRL2);
+
+       ret = regmap_write(palmas->regmap[slave], addr, reg);
+       if (ret)
+               goto out;
+
+       return 0;
+out:
+       pr_err("%s: Failed to configure PWM, Error %d\n",
+                       pwm->label, ret);
+       return ret;
+
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+       struct palmas *palmas = the_palmas;
+       unsigned int addr;
+       int ret, slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_LED_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_LED_BASE, PALMAS_PWM_CTRL1);
+
+       ret = regmap_update_bits(palmas->regmap[slave], addr,
+                       PALMAS_PWM_CTRL1_PWM_FREQ_EN,
+                       PALMAS_PWM_CTRL1_PWM_FREQ_EN);
+       if (ret < 0) {
+               pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret);
+               return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+       struct palmas *palmas = the_palmas;
+       unsigned int addr;
+       int ret, slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_LED_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_LED_BASE, PALMAS_PWM_CTRL1);
+
+       ret = regmap_update_bits(palmas->regmap[slave], addr,
+                       PALMAS_PWM_CTRL1_PWM_FREQ_EN, 0);
+       if (ret < 0)
+               pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret);
+
+       return;
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+       struct pwm_device *pwm;
+
+       /* We are not ready to write to Palmas device yet */
+       if (!the_palmas)
+               return NULL;
+
+       pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+       if (pwm == NULL) {
+               pr_err("%s: failed to allocate memory\n", label);
+               return NULL;
+       }
+
+       pwm->label = label;
+       pwm->pwm_id = pwm_id;
+
+       return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+       pwm_disable(pwm);
+       kfree(pwm);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static int palmas_pwm_probe(struct platform_device *pdev)
+{
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+
+       /* Only one PWM access can be allowed by PWM subsystem */
+       if (the_palmas)
+               return -EINVAL;
+
+       /* no need to allow pwm_request if PWM is not muxed */
+       if (palmas->led_muxed) {
+               the_palmas = palmas;
+       } else {
+               dev_err(&pdev->dev, "there are no PWMs muxed\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int palmas_pwm_remove(struct platform_device *pdev)
+{
+       the_palmas = NULL;
+
+       return 0;
+}
+
+static struct of_device_id of_palmas_match_tbl[] = {
+       { .compatible = "ti,palmas-pwm", },
+       { /* end */ }
+};
+
+static struct platform_driver palmas_pwm_driver = {
+       .probe = palmas_pwm_probe,
+       .remove = palmas_pwm_remove,
+       .driver = {
+               .name = "palmas-pwm",
+               .of_match_table = of_palmas_match_tbl,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init palmas_pwm_init(void)
+{
+       return platform_driver_register(&palmas_pwm_driver);
+}
+module_init(palmas_pwm_init);
+
+static void __exit palmas_pwm_exit(void)
+{
+       platform_driver_unregister(&palmas_pwm_driver);
+}
+module_exit(palmas_pwm_exit);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("Palmas PWM driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:palmas-pwm");
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
diff --git a/drivers/mfd/palmas-resource.c b/drivers/mfd/palmas-resource.c
new file mode 100644 (file)
index 0000000..faa1066
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * PALMAS resource module driver
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ * Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/mfd/palmas.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+static int palmas_resource_read(struct palmas *palmas, unsigned int reg,
+               unsigned int *dest)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_RESOURCE_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_RESOURCE_BASE, reg);
+
+       return regmap_read(palmas->regmap[slave], addr, dest);
+}
+
+static int palmas_resource_write(struct palmas *palmas, unsigned int reg,
+               unsigned int data)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_RESOURCE_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_RESOURCE_BASE, reg);
+
+       return regmap_write(palmas->regmap[slave], addr, data);
+}
+
+int palmas_enable_clk32kgaudio(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_CLK32KGAUDIO_CTRL, &reg);
+
+       if (ret)
+               return ret;
+
+       reg |= PALMAS_CLK32KGAUDIO_CTRL_MODE_ACTIVE;
+
+       ret = palmas_resource_write(resource->palmas,
+                       PALMAS_CLK32KGAUDIO_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_enable_clk32kgaudio);
+
+int palmas_enable_regen1(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_REGEN1_CTRL, &reg);
+
+       if (ret)
+               return ret;
+
+       reg |= PALMAS_REGEN1_CTRL_MODE_ACTIVE;
+
+       ret = palmas_resource_write(resource->palmas,
+                       PALMAS_REGEN1_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_enable_regen1);
+
+int palmas_disable_regen1(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_REGEN1_CTRL, &reg);
+
+       if (ret)
+               return ret;
+
+       reg &= ~PALMAS_REGEN1_CTRL_MODE_ACTIVE;
+
+       ret = palmas_resource_write(resource->palmas,
+                       PALMAS_REGEN1_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_disable_regen1);
+
+int palmas_is_enabled_regen1(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_REGEN1_CTRL, &reg);
+
+
+       return !!(reg & PALMAS_REGEN1_CTRL_STATUS);
+}
+EXPORT_SYMBOL(palmas_is_enabled_regen1);
+
+int palmas_enable_regen2(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_REGEN2_CTRL, &reg);
+
+       if (ret)
+               return ret;
+
+       reg |= PALMAS_REGEN2_CTRL_MODE_ACTIVE;
+
+       ret = palmas_resource_write(resource->palmas,
+                       PALMAS_REGEN2_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_enable_regen2);
+
+int palmas_disable_regen2(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_REGEN2_CTRL, &reg);
+
+       if (ret)
+               return ret;
+
+       reg &= ~PALMAS_REGEN2_CTRL_MODE_ACTIVE;
+
+       ret = palmas_resource_write(resource->palmas,
+                       PALMAS_REGEN2_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_disable_regen2);
+
+int palmas_is_enabled_regen2(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_REGEN2_CTRL, &reg);
+
+
+       return !!(reg & PALMAS_REGEN2_CTRL_STATUS);
+}
+EXPORT_SYMBOL(palmas_is_enabled_regen2);
+
+int palmas_enable_sysen1(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_SYSEN1_CTRL, &reg);
+
+       if (ret)
+               return ret;
+
+       reg |= PALMAS_SYSEN1_CTRL_MODE_ACTIVE;
+
+       ret = palmas_resource_write(resource->palmas,
+                       PALMAS_SYSEN1_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_enable_sysen1);
+
+int palmas_disable_sysen1(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_SYSEN1_CTRL, &reg);
+
+       if (ret)
+               return ret;
+
+       reg &= ~PALMAS_SYSEN1_CTRL_MODE_ACTIVE;
+
+       ret = palmas_resource_write(resource->palmas,
+                       PALMAS_SYSEN1_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_disable_sysen1);
+
+int palmas_is_enabled_sysen1(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_SYSEN1_CTRL, &reg);
+
+
+       return !!(reg & PALMAS_SYSEN1_CTRL_STATUS);
+}
+EXPORT_SYMBOL(palmas_is_enabled_sysen1);
+
+int palmas_enable_sysen2(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_SYSEN2_CTRL, &reg);
+
+       if (ret)
+               return ret;
+
+       reg |= PALMAS_SYSEN2_CTRL_MODE_ACTIVE;
+
+       ret = palmas_resource_write(resource->palmas,
+                       PALMAS_SYSEN2_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_enable_sysen2);
+
+int palmas_disable_sysen2(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_SYSEN2_CTRL, &reg);
+
+       if (ret)
+               return ret;
+
+       reg &= ~PALMAS_SYSEN2_CTRL_MODE_ACTIVE;
+
+       ret = palmas_resource_write(resource->palmas,
+                       PALMAS_SYSEN2_CTRL, reg);
+
+       return ret;
+}
+EXPORT_SYMBOL(palmas_disable_sysen2);
+
+int palmas_is_enabled_sysen2(struct palmas_resource *resource)
+{
+       int ret;
+       unsigned int reg;
+
+       ret = palmas_resource_read(resource->palmas,
+                       PALMAS_SYSEN2_CTRL, &reg);
+
+
+       return !!(reg & PALMAS_SYSEN2_CTRL_STATUS);
+}
+EXPORT_SYMBOL(palmas_is_enabled_sysen2);
+
+static int palmas_initialise_resource(struct palmas_resource *resource,
+               struct palmas_resource_platform_data *pdata)
+{
+       int ret;
+       unsigned int reg;
+
+       if (pdata->regen1_mode_sleep) {
+               ret = palmas_resource_read(resource->palmas,
+                               PALMAS_REGEN1_CTRL, &reg);
+               if (ret)
+                       return ret;
+
+               reg |= pdata->regen1_mode_sleep <<
+                               PALMAS_REGEN1_CTRL_MODE_SLEEP_SHIFT;
+
+               ret = palmas_resource_write(resource->palmas,
+                               PALMAS_REGEN1_CTRL, reg);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->regen2_mode_sleep) {
+               ret = palmas_resource_read(resource->palmas,
+                               PALMAS_REGEN2_CTRL, &reg);
+               if (ret)
+                       return ret;
+
+               reg |= pdata->regen2_mode_sleep <<
+                               PALMAS_REGEN2_CTRL_MODE_SLEEP_SHIFT;
+
+               ret = palmas_resource_write(resource->palmas,
+                               PALMAS_REGEN2_CTRL, reg);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->sysen1_mode_sleep) {
+               ret = palmas_resource_read(resource->palmas,
+                               PALMAS_SYSEN1_CTRL, &reg);
+               if (ret)
+                       return ret;
+
+               reg |= pdata->sysen1_mode_sleep <<
+                               PALMAS_SYSEN1_CTRL_MODE_SLEEP_SHIFT;
+
+               ret = palmas_resource_write(resource->palmas,
+                               PALMAS_SYSEN1_CTRL, reg);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->sysen2_mode_sleep) {
+               ret = palmas_resource_read(resource->palmas,
+                               PALMAS_SYSEN2_CTRL, &reg);
+               if (ret)
+                       return ret;
+
+               reg |= pdata->sysen2_mode_sleep <<
+                               PALMAS_SYSEN2_CTRL_MODE_SLEEP_SHIFT;
+
+               ret = palmas_resource_write(resource->palmas,
+                               PALMAS_SYSEN2_CTRL, reg);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->nsleep_res) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_NSLEEP_RES_ASSIGN, pdata->nsleep_res);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->nsleep_smps) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_NSLEEP_SMPS_ASSIGN, pdata->nsleep_smps);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->nsleep_ldo1) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_NSLEEP_LDO_ASSIGN1, pdata->nsleep_ldo1);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->nsleep_ldo2) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_NSLEEP_LDO_ASSIGN2, pdata->nsleep_ldo2);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->enable1_res) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_ENABLE1_RES_ASSIGN, pdata->enable1_res);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->enable1_smps) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_ENABLE1_SMPS_ASSIGN, pdata->enable1_smps);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->enable1_ldo1) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_ENABLE1_LDO_ASSIGN1, pdata->enable1_ldo1);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->enable1_ldo2) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_ENABLE1_LDO_ASSIGN2, pdata->enable1_ldo2);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->enable2_res) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_ENABLE2_RES_ASSIGN, pdata->enable2_res);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->enable2_smps) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_ENABLE2_SMPS_ASSIGN, pdata->enable2_smps);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->enable2_ldo1) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_ENABLE2_LDO_ASSIGN1, pdata->enable2_ldo1);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata->enable2_ldo2) {
+               ret = palmas_resource_write(resource->palmas,
+                       PALMAS_ENABLE2_LDO_ASSIGN2, pdata->enable2_ldo2);
+               if (ret)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static struct miscdevice palmas_resource_device = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "palmas-resource",
+};
+
+static void palmas_dt_to_pdata(struct device_node *node,
+               struct palmas_resource_platform_data *pdata)
+{
+       int ret;
+       u32 prop;
+
+       ret = of_property_read_u32(node, "ti,regen1_mode_sleep", &prop);
+       if (!ret) {
+               pdata->regen1_mode_sleep = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,regen2_mode_sleep", &prop);
+       if (!ret) {
+               pdata->regen2_mode_sleep = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,sysen1_mode_sleep", &prop);
+       if (!ret) {
+               pdata->regen1_mode_sleep = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,sysen2_mode_sleep", &prop);
+       if (!ret) {
+               pdata->regen2_mode_sleep = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,nsleep_res", &prop);
+       if (!ret) {
+               pdata->nsleep_res = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,nsleep_smps", &prop);
+       if (!ret) {
+               pdata->nsleep_smps = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,nsleep_ldo1", &prop);
+       if (!ret) {
+               pdata->nsleep_ldo1 = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,nsleep_ldo2", &prop);
+       if (!ret) {
+               pdata->nsleep_ldo2 = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,enable1_res", &prop);
+       if (!ret) {
+               pdata->enable1_res = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,enable1_smps", &prop);
+       if (!ret) {
+               pdata->enable1_smps = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,enable1_ldo1", &prop);
+       if (!ret) {
+               pdata->enable1_ldo1 = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,enable1_ldo2", &prop);
+       if (!ret) {
+               pdata->enable1_ldo2 = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,enable2_res", &prop);
+       if (!ret) {
+               pdata->enable2_res = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,enable2_smps", &prop);
+       if (!ret) {
+               pdata->enable2_smps = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,enable2_ldo1", &prop);
+       if (!ret) {
+               pdata->enable2_ldo1 = prop;
+       }
+
+       ret = of_property_read_u32(node, "ti,enable2_ldo2", &prop);
+       if (!ret) {
+               pdata->enable2_ldo2 = prop;
+       }
+}
+
+static int palmas_resource_probe(struct platform_device *pdev)
+{
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+       struct palmas_resource_platform_data *pdata = pdev->dev.platform_data;
+       struct device_node *node = pdev->dev.of_node;
+       struct palmas_resource *resource;
+       int ret;
+
+       if(node && !pdata) {
+               pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+
+               if (!pdata)
+                       return -ENOMEM;
+
+               palmas_dt_to_pdata(node, pdata);
+       }
+
+       if (!pdata)
+               return -EINVAL;
+
+       resource = kzalloc(sizeof(struct palmas_resource), GFP_KERNEL);
+       if (!resource)
+               return -ENOMEM;
+
+       resource->dev = &pdev->dev;
+       resource->palmas = palmas;
+       palmas->resource = resource;
+
+       ret = misc_register(&palmas_resource_device);
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not register misc_device\n");
+               goto err_misc;
+       }
+
+       platform_set_drvdata(pdev, resource);
+
+       ret = palmas_initialise_resource(resource, pdata);
+
+       if (ret)
+               goto err_int;
+
+       /* Test */
+       palmas_enable_clk32kgaudio(resource);
+
+       return 0;
+
+err_int:
+       misc_deregister(&palmas_resource_device);
+err_misc:
+       kfree(resource);
+
+       return ret;
+}
+
+static int palmas_resource_remove(struct platform_device *pdev)
+{
+       struct palmas_resource *resource = platform_get_drvdata(pdev);
+
+       kfree(resource);
+       misc_deregister(&palmas_resource_device);
+
+       return 0;
+}
+
+static struct of_device_id of_palmas_match_tbl[] = {
+       { .compatible = "ti,palmas-resource", },
+       { /* end */ }
+};
+
+static struct platform_driver palmas_resource_driver = {
+       .probe = palmas_resource_probe,
+       .remove = palmas_resource_remove,
+       .driver = {
+               .name = "palmas-resource",
+               .of_match_table = of_palmas_match_tbl,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init palmas_resource_init(void)
+{
+       return platform_driver_register(&palmas_resource_driver);
+}
+module_init(palmas_resource_init);
+
+static void __exit palmas_resource_exit(void)
+{
+       platform_driver_unregister(&palmas_resource_driver);
+}
+module_exit(palmas_resource_exit);
+
+MODULE_ALIAS("platform:palmas-resource");
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("Palmas General Resource driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
index 6ffd7a2affdc2df808bad3a75b9389741f415cbc..b077bedda03ef18d211c6d30446ac6fc1679d61d 100644 (file)
@@ -275,6 +275,64 @@ static void palmas_dt_to_pdata(struct device_node *node,
                                        PALMAS_POWER_CTRL_ENABLE2_MASK;
 }
 
+static int palmas_print_info(struct palmas *palmas)
+{
+       int ret;
+       unsigned int reg, addr;
+       int slave;
+
+       /* Read varient info from the device */
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_ID_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_ID_BASE, PALMAS_PRODUCT_ID_LSB);
+       ret = regmap_read(palmas->regmap[slave], addr, &reg);
+       if (ret < 0) {
+               dev_err(palmas->dev, "Unable to read ID err: %d\n", ret);
+               goto err;
+       }
+
+       palmas->id = reg;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_ID_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_ID_BASE, PALMAS_PRODUCT_ID_MSB);
+       ret = regmap_read(palmas->regmap[slave], addr, &reg);
+       if (ret < 0) {
+               dev_err(palmas->dev, "Unable to read ID err: %d\n", ret);
+               goto err;
+       }
+
+       palmas->id |= reg << 8;
+
+       dev_info(palmas->dev, "Product ID %x\n", palmas->id);
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_DESIGNREV_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_DESIGNREV_BASE, PALMAS_DESIGNREV);
+       ret = regmap_read(palmas->regmap[slave], addr, &reg);
+       if (ret < 0) {
+               dev_err(palmas->dev, "Unable to read DESIGNREV err: %d\n", ret);
+               goto err;
+       }
+
+       palmas->designrev = reg & PALMAS_DESIGNREV_DESIGNREV_MASK;
+
+       dev_info(palmas->dev, "Product Design Rev %x\n", palmas->designrev);
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_PMU_CONTROL_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE, PALMAS_SW_REVISION);
+       ret = regmap_read(palmas->regmap[slave], addr, &reg);
+       if (ret < 0) {
+               dev_err(palmas->dev, "Unable to read SW_REVISION err: %d\n",
+                       ret);
+               goto err;
+       }
+
+       palmas->sw_revision = reg;
+
+       dev_info(palmas->dev, "Product SW Rev %x\n", palmas->sw_revision);
+       return 0;
+err:
+       return ret;
+}
+
 static int palmas_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
@@ -342,7 +400,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
        regmap_write(palmas->regmap[slave], addr, reg);
 
        ret = regmap_add_irq_chip(palmas->regmap[slave], palmas->irq,
-                       IRQF_ONESHOT | IRQF_TRIGGER_LOW, 0, &palmas_irq_chip,
+                       IRQF_ONESHOT, 0, &palmas_irq_chip,
                        &palmas->irq_data);
        if (ret < 0)
                goto err;
@@ -419,6 +477,8 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
        if (ret)
                goto err_irq;
 
+       palmas_print_info(palmas);
+
        /*
         * If we are probing with DT do this the DT way and return here
         * otherwise continue and add devices using mfd helpers.
@@ -458,6 +518,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
                              children, ARRAY_SIZE(palmas_children),
                              NULL, regmap_irq_chip_get_base(palmas->irq_data),
                              NULL);
+
        kfree(children);
 
        if (ret < 0)
index 4f3baadd0038e04f44d0a88856218edee23e5646..557f9ee5ec181f38e12050403146bb584ebbba86 100644 (file)
 
 /* Triton Core internal information (BEGIN) */
 
-#define TWL_NUM_SLAVES         4
-
-#define SUB_CHIP_ID0 0
-#define SUB_CHIP_ID1 1
-#define SUB_CHIP_ID2 2
-#define SUB_CHIP_ID3 3
-#define SUB_CHIP_ID_INVAL 0xff
-
-#define TWL_MODULE_LAST TWL4030_MODULE_LAST
-
 /* Base Address defns for twl4030_map[] */
 
 /* subchip/slave 0 - USB ID */
 #define TWL4030_BASEADD_MADC           0x0000
 #define TWL4030_BASEADD_MAIN_CHARGE    0x0074
 #define TWL4030_BASEADD_PRECHARGE      0x00AA
-#define TWL4030_BASEADD_PWM0           0x00F8
-#define TWL4030_BASEADD_PWM1           0x00FB
-#define TWL4030_BASEADD_PWMA           0x00EF
-#define TWL4030_BASEADD_PWMB           0x00F1
+#define TWL4030_BASEADD_PWM            0x00F8
 #define TWL4030_BASEADD_KEYPAD         0x00D2
 
 #define TWL5031_BASEADD_ACCESSORY      0x0074 /* Replaces Main Charge */
 
 /* subchip/slave 0 0x48 - POWER */
 #define TWL6030_BASEADD_RTC            0x0000
-#define TWL6030_BASEADD_MEM            0x0017
+#define TWL6030_BASEADD_SECURED_REG    0x0017
 #define TWL6030_BASEADD_PM_MASTER      0x001F
 #define TWL6030_BASEADD_PM_SLAVE_MISC  0x0030 /* PM_RECEIVER */
 #define TWL6030_BASEADD_PM_MISC                0x00E2
 #define TWL6030_BASEADD_PIH            0x00D0
 #define TWL6030_BASEADD_CHARGER                0x00E0
 #define TWL6025_BASEADD_CHARGER                0x00DA
+#define TWL6030_BASEADD_LED            0x00F4
 
 /* subchip/slave 2 0x4A - DFT */
 #define TWL6030_BASEADD_DIEID          0x00C0
 
 /*----------------------------------------------------------------------*/
 
-/* is driver active, bound to a chip? */
-static bool inuse;
-
-/* TWL IDCODE Register value */
-static u32 twl_idcode;
-
-static unsigned int twl_id;
-unsigned int twl_rev(void)
-{
-       return twl_id;
-}
-EXPORT_SYMBOL(twl_rev);
-
 /* Structure for each TWL4030/TWL6030 Slave */
 struct twl_client {
        struct i2c_client *client;
        struct regmap *regmap;
 };
 
-static struct twl_client twl_modules[TWL_NUM_SLAVES];
-
 /* mapping the module id to slave id and base address */
 struct twl_mapping {
        unsigned char sid;      /* Slave ID */
        unsigned char base;     /* base address */
 };
-static struct twl_mapping *twl_map;
+
+struct twl_private {
+       bool ready; /* The core driver is ready to be used */
+       u32 twl_idcode; /* TWL IDCODE Register value */
+       unsigned int twl_id;
+
+       struct twl_mapping *twl_map;
+       struct twl_client *twl_modules;
+};
+
+static struct twl_private *twl_priv;
 
 static struct twl_mapping twl4030_map[] = {
        /*
@@ -188,34 +171,33 @@ static struct twl_mapping twl4030_map[] = {
         * so they continue to match the order in this table.
         */
 
+       /* Common IPs */
        { 0, TWL4030_BASEADD_USB },
+       { 1, TWL4030_BASEADD_PIH },
+       { 2, TWL4030_BASEADD_MAIN_CHARGE },
+       { 3, TWL4030_BASEADD_PM_MASTER },
+       { 3, TWL4030_BASEADD_PM_RECEIVER },
+
+       { 3, TWL4030_BASEADD_RTC },
+       { 2, TWL4030_BASEADD_PWM },
+       { 2, TWL4030_BASEADD_LED },
+       { 3, TWL4030_BASEADD_SECURED_REG },
+
+       /* TWL4030 specific IPs */
        { 1, TWL4030_BASEADD_AUDIO_VOICE },
        { 1, TWL4030_BASEADD_GPIO },
        { 1, TWL4030_BASEADD_INTBR },
-       { 1, TWL4030_BASEADD_PIH },
-
        { 1, TWL4030_BASEADD_TEST },
        { 2, TWL4030_BASEADD_KEYPAD },
+
        { 2, TWL4030_BASEADD_MADC },
        { 2, TWL4030_BASEADD_INTERRUPTS },
-       { 2, TWL4030_BASEADD_LED },
-
-       { 2, TWL4030_BASEADD_MAIN_CHARGE },
        { 2, TWL4030_BASEADD_PRECHARGE },
-       { 2, TWL4030_BASEADD_PWM0 },
-       { 2, TWL4030_BASEADD_PWM1 },
-       { 2, TWL4030_BASEADD_PWMA },
-
-       { 2, TWL4030_BASEADD_PWMB },
-       { 2, TWL5031_BASEADD_ACCESSORY },
-       { 2, TWL5031_BASEADD_INTERRUPTS },
        { 3, TWL4030_BASEADD_BACKUP },
        { 3, TWL4030_BASEADD_INT },
 
-       { 3, TWL4030_BASEADD_PM_MASTER },
-       { 3, TWL4030_BASEADD_PM_RECEIVER },
-       { 3, TWL4030_BASEADD_RTC },
-       { 3, TWL4030_BASEADD_SECURED_REG },
+       { 2, TWL5031_BASEADD_ACCESSORY },
+       { 2, TWL5031_BASEADD_INTERRUPTS },
 };
 
 static struct regmap_config twl4030_regmap_config[4] = {
@@ -251,35 +233,25 @@ static struct twl_mapping twl6030_map[] = {
         * <linux/i2c/twl.h> defines for TWL4030_MODULE_*
         * so they continue to match the order in this table.
         */
-       { SUB_CHIP_ID1, TWL6030_BASEADD_USB },
-       { SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO },
-       { SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },
-       { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-       { SUB_CHIP_ID1, TWL6030_BASEADD_PIH },
-
-       { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-       { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-       { SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL },
-       { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-       { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-
-       { SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER },
-       { SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE },
-       { SUB_CHIP_ID1, TWL6030_BASEADD_PWM },
-       { SUB_CHIP_ID0, TWL6030_BASEADD_ZERO },
-       { SUB_CHIP_ID1, TWL6030_BASEADD_ZERO },
-
-       { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
-       { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
-       { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-       { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-       { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
-
-       { SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER },
-       { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC },
-       { SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
-       { SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
-       { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER },
+
+       /* Common IPs */
+       { 1, TWL6030_BASEADD_USB },
+       { 1, TWL6030_BASEADD_PIH },
+       { 1, TWL6030_BASEADD_CHARGER },
+       { 0, TWL6030_BASEADD_PM_MASTER },
+       { 0, TWL6030_BASEADD_PM_SLAVE_MISC },
+
+       { 0, TWL6030_BASEADD_RTC },
+       { 1, TWL6030_BASEADD_PWM },
+       { 1, TWL6030_BASEADD_LED },
+       { 0, TWL6030_BASEADD_SECURED_REG },
+
+       /* TWL6030 specific IPs */
+       { 0, TWL6030_BASEADD_ZERO },
+       { 1, TWL6030_BASEADD_ZERO },
+       { 2, TWL6030_BASEADD_ZERO },
+       { 1, TWL6030_BASEADD_GPADC_CTRL },
+       { 1, TWL6030_BASEADD_GASGAUGE },
 };
 
 static struct regmap_config twl6030_regmap_config[3] = {
@@ -305,8 +277,30 @@ static struct regmap_config twl6030_regmap_config[3] = {
 
 /*----------------------------------------------------------------------*/
 
+static inline int twl_get_num_slaves(void)
+{
+       if (twl_class_is_4030())
+               return 4; /* TWL4030 class have four slave address */
+       else
+               return 3; /* TWL6030 class have three slave address */
+}
+
+static inline int twl_get_last_module(void)
+{
+       if (twl_class_is_4030())
+               return TWL4030_MODULE_LAST;
+       else
+               return TWL6030_MODULE_LAST;
+}
+
 /* Exported Functions */
 
+unsigned int twl_rev(void)
+{
+       return twl_priv ? twl_priv->twl_id : 0;
+}
+EXPORT_SYMBOL(twl_rev);
+
 /**
  * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0
  * @mod_no: module number
@@ -314,9 +308,6 @@ static struct regmap_config twl6030_regmap_config[3] = {
  * @reg: register address (just offset will do)
  * @num_bytes: number of bytes to transfer
  *
- * IMPORTANT: for 'value' parameter: Allocate value num_bytes+1 and
- * valid data starts at Offset 1.
- *
  * Returns the result of operation - 0 is success
  */
 int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
@@ -325,24 +316,21 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
        int sid;
        struct twl_client *twl;
 
-       if (unlikely(mod_no >= TWL_MODULE_LAST)) {
+       if (unlikely(mod_no >= twl_get_last_module())) {
                pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
                return -EPERM;
        }
-       if (unlikely(!inuse)) {
+       if (unlikely(!twl_priv->ready)) {
                pr_err("%s: not initialized\n", DRIVER_NAME);
                return -EPERM;
        }
-       sid = twl_map[mod_no].sid;
-       if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
-               pr_err("%s: module %d is not part of the pmic\n",
-                      DRIVER_NAME, mod_no);
-               return -EINVAL;
-       }
-       twl = &twl_modules[sid];
 
-       ret = regmap_bulk_write(twl->regmap, twl_map[mod_no].base + reg,
-                               value, num_bytes);
+       sid = twl_priv->twl_map[mod_no].sid;
+       twl = &twl_priv->twl_modules[sid];
+
+       ret = regmap_bulk_write(twl->regmap,
+                               twl_priv->twl_map[mod_no].base + reg, value,
+                               num_bytes);
 
        if (ret)
                pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n",
@@ -367,24 +355,21 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
        int sid;
        struct twl_client *twl;
 
-       if (unlikely(mod_no >= TWL_MODULE_LAST)) {
+       if (unlikely(mod_no >= twl_get_last_module())) {
                pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
                return -EPERM;
        }
-       if (unlikely(!inuse)) {
+       if (unlikely(!twl_priv->ready)) {
                pr_err("%s: not initialized\n", DRIVER_NAME);
                return -EPERM;
        }
-       sid = twl_map[mod_no].sid;
-       if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
-               pr_err("%s: module %d is not part of the pmic\n",
-                      DRIVER_NAME, mod_no);
-               return -EINVAL;
-       }
-       twl = &twl_modules[sid];
 
-       ret = regmap_bulk_read(twl->regmap, twl_map[mod_no].base + reg,
-                              value, num_bytes);
+       sid = twl_priv->twl_map[mod_no].sid;
+       twl = &twl_priv->twl_modules[sid];
+
+       ret = regmap_bulk_read(twl->regmap,
+                              twl_priv->twl_map[mod_no].base + reg, value,
+                              num_bytes);
 
        if (ret)
                pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n",
@@ -394,34 +379,6 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
 }
 EXPORT_SYMBOL(twl_i2c_read);
 
-/**
- * twl_i2c_write_u8 - Writes a 8 bit register in TWL4030/TWL5030/TWL60X0
- * @mod_no: module number
- * @value: the value to be written 8 bit
- * @reg: register address (just offset will do)
- *
- * Returns result of operation - 0 is success
- */
-int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
-{
-       return twl_i2c_write(mod_no, &value, reg, 1);
-}
-EXPORT_SYMBOL(twl_i2c_write_u8);
-
-/**
- * twl_i2c_read_u8 - Reads a 8 bit register from TWL4030/TWL5030/TWL60X0
- * @mod_no: module number
- * @value: the value read 8 bit
- * @reg: register address (just offset will do)
- *
- * Returns result of operation - 0 is success
- */
-int twl_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
-{
-       return twl_i2c_read(mod_no, value, reg, 1);
-}
-EXPORT_SYMBOL(twl_i2c_read_u8);
-
 /*----------------------------------------------------------------------*/
 
 /**
@@ -440,7 +397,7 @@ static int twl_read_idcode_register(void)
                goto fail;
        }
 
-       err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_idcode),
+       err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_priv->twl_idcode),
                                                REG_IDCODE_7_0, 4);
        if (err) {
                pr_err("TWL4030: unable to read IDCODE -%d\n", err);
@@ -461,7 +418,7 @@ fail:
  */
 int twl_get_type(void)
 {
-       return TWL_SIL_TYPE(twl_idcode);
+       return TWL_SIL_TYPE(twl_priv->twl_idcode);
 }
 EXPORT_SYMBOL_GPL(twl_get_type);
 
@@ -472,7 +429,7 @@ EXPORT_SYMBOL_GPL(twl_get_type);
  */
 int twl_get_version(void)
 {
-       return TWL_SIL_REV(twl_idcode);
+       return TWL_SIL_REV(twl_priv->twl_idcode);
 }
 EXPORT_SYMBOL_GPL(twl_get_version);
 
@@ -509,13 +466,20 @@ int twl_get_hfclk_rate(void)
 EXPORT_SYMBOL_GPL(twl_get_hfclk_rate);
 
 static struct device *
-add_numbered_child(unsigned chip, const char *name, int num,
+add_numbered_child(unsigned mod_no, const char *name, int num,
                void *pdata, unsigned pdata_len,
                bool can_wakeup, int irq0, int irq1)
 {
        struct platform_device  *pdev;
-       struct twl_client       *twl = &twl_modules[chip];
-       int                     status;
+       struct twl_client       *twl;
+       int                     status, sid;
+
+       if (unlikely(mod_no >= twl_get_last_module())) {
+               pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
+               return ERR_PTR(-EPERM);
+       }
+       sid = twl_priv->twl_map[mod_no].sid;
+       twl = &twl_priv->twl_modules[sid];
 
        pdev = platform_device_alloc(name, num);
        if (!pdev) {
@@ -560,11 +524,11 @@ err:
        return &pdev->dev;
 }
 
-static inline struct device *add_child(unsigned chip, const char *name,
+static inline struct device *add_child(unsigned mod_no, const char *name,
                void *pdata, unsigned pdata_len,
                bool can_wakeup, int irq0, int irq1)
 {
-       return add_numbered_child(chip, name, -1, pdata, pdata_len,
+       return add_numbered_child(mod_no, name, -1, pdata, pdata_len,
                can_wakeup, irq0, irq1);
 }
 
@@ -573,7 +537,6 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
                struct regulator_consumer_supply *consumers,
                unsigned num_consumers, unsigned long features)
 {
-       unsigned sub_chip_id;
        struct twl_regulator_driver_data drv_data;
 
        /* regulator framework demands init_data ... */
@@ -600,8 +563,7 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
        }
 
        /* NOTE:  we currently ignore regulator IRQs, e.g. for short circuits */
-       sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid;
-       return add_numbered_child(sub_chip_id, "twl_reg", num,
+       return add_numbered_child(TWL_MODULE_PM_MASTER, "twl_reg", num,
                pdata, sizeof(*pdata), false, 0, 0);
 }
 
@@ -623,10 +585,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
                unsigned long features)
 {
        struct device   *child;
-       unsigned sub_chip_id;
 
        if (IS_ENABLED(CONFIG_GPIO_TWL4030) && pdata->gpio) {
-               child = add_child(SUB_CHIP_ID1, "twl4030_gpio",
+               child = add_child(TWL4030_MODULE_GPIO, "twl4030_gpio",
                                pdata->gpio, sizeof(*pdata->gpio),
                                false, irq_base + GPIO_INTR_OFFSET, 0);
                if (IS_ERR(child))
@@ -634,7 +595,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
        }
 
        if (IS_ENABLED(CONFIG_KEYBOARD_TWL4030) && pdata->keypad) {
-               child = add_child(SUB_CHIP_ID2, "twl4030_keypad",
+               child = add_child(TWL4030_MODULE_KEYPAD, "twl4030_keypad",
                                pdata->keypad, sizeof(*pdata->keypad),
                                true, irq_base + KEYPAD_INTR_OFFSET, 0);
                if (IS_ERR(child))
@@ -643,7 +604,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
 
        if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc &&
            twl_class_is_4030()) {
-               child = add_child(SUB_CHIP_ID2, "twl4030_madc",
+               child = add_child(TWL4030_MODULE_MADC, "twl4030_madc",
                                pdata->madc, sizeof(*pdata->madc),
                                true, irq_base + MADC_INTR_OFFSET, 0);
                if (IS_ERR(child))
@@ -658,22 +619,21 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
                 * Eventually, Linux might become more aware of such
                 * HW security concerns, and "least privilege".
                 */
-               sub_chip_id = twl_map[TWL_MODULE_RTC].sid;
-               child = add_child(sub_chip_id, "twl_rtc", NULL, 0,
+               child = add_child(TWL_MODULE_RTC, "twl_rtc", NULL, 0,
                                true, irq_base + RTC_INTR_OFFSET, 0);
                if (IS_ERR(child))
                        return PTR_ERR(child);
        }
 
        if (IS_ENABLED(CONFIG_PWM_TWL)) {
-               child = add_child(SUB_CHIP_ID1, "twl-pwm", NULL, 0,
+               child = add_child(TWL_MODULE_PWM, "twl-pwm", NULL, 0,
                                  false, 0, 0);
                if (IS_ERR(child))
                        return PTR_ERR(child);
        }
 
        if (IS_ENABLED(CONFIG_PWM_TWL_LED)) {
-               child = add_child(SUB_CHIP_ID1, "twl-pwmled", NULL, 0,
+               child = add_child(TWL_MODULE_LED, "twl-pwmled", NULL, 0,
                                  false, 0, 0);
                if (IS_ERR(child))
                        return PTR_ERR(child);
@@ -725,7 +685,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
 
                }
 
-               child = add_child(SUB_CHIP_ID0, "twl4030_usb",
+               child = add_child(TWL_MODULE_USB, "twl4030_usb",
                                pdata->usb, sizeof(*pdata->usb), true,
                                /* irq0 = USB_PRES, irq1 = USB */
                                irq_base + USB_PRES_INTR_OFFSET,
@@ -774,7 +734,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
 
                pdata->usb->features = features;
 
-               child = add_child(SUB_CHIP_ID0, "twl6030_usb",
+               child = add_child(TWL_MODULE_USB, "twl6030_usb",
                        pdata->usb, sizeof(*pdata->usb), true,
                        /* irq1 = VBUS_PRES, irq0 = USB ID */
                        irq_base + USBOTG_INTR_OFFSET,
@@ -799,22 +759,22 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
        }
 
        if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) {
-               child = add_child(SUB_CHIP_ID3, "twl4030_wdt", NULL, 0,
-                                 false, 0, 0);
+               child = add_child(TWL_MODULE_PM_RECEIVER, "twl4030_wdt", NULL,
+                                 0, false, 0, 0);
                if (IS_ERR(child))
                        return PTR_ERR(child);
        }
 
        if (IS_ENABLED(CONFIG_INPUT_TWL4030_PWRBUTTON) && twl_class_is_4030()) {
-               child = add_child(SUB_CHIP_ID3, "twl4030_pwrbutton", NULL, 0,
-                                 true, irq_base + 8 + 0, 0);
+               child = add_child(TWL_MODULE_PM_MASTER, "twl4030_pwrbutton",
+                                 NULL, 0, true, irq_base + 8 + 0, 0);
                if (IS_ERR(child))
                        return PTR_ERR(child);
        }
 
        if (IS_ENABLED(CONFIG_MFD_TWL4030_AUDIO) && pdata->audio &&
            twl_class_is_4030()) {
-               child = add_child(SUB_CHIP_ID1, "twl4030-audio",
+               child = add_child(TWL4030_MODULE_AUDIO_VOICE, "twl4030-audio",
                                pdata->audio, sizeof(*pdata->audio),
                                false, 0, 0);
                if (IS_ERR(child))
@@ -1054,7 +1014,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
 
        if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci &&
                        !(features & (TPS_SUBSET | TWL5031))) {
-               child = add_child(SUB_CHIP_ID3, "twl4030_bci",
+               child = add_child(TWL_MODULE_MAIN_CHARGE, "twl4030_bci",
                                pdata->bci, sizeof(*pdata->bci), false,
                                /* irq0 = CHG_PRES, irq1 = BCI */
                                irq_base + BCI_PRES_INTR_OFFSET,
@@ -1145,25 +1105,23 @@ static int twl_remove(struct i2c_client *client)
        unsigned i, num_slaves;
        int status;
 
-       if (twl_class_is_4030()) {
+       if (twl_class_is_4030())
                status = twl4030_exit_irq();
-               num_slaves = TWL_NUM_SLAVES;
-       } else {
+       else
                status = twl6030_exit_irq();
-               num_slaves = TWL_NUM_SLAVES - 1;
-       }
 
        if (status < 0)
                return status;
 
+       num_slaves = twl_get_num_slaves();
        for (i = 0; i < num_slaves; i++) {
-               struct twl_client       *twl = &twl_modules[i];
+               struct twl_client       *twl = &twl_priv->twl_modules[i];
 
                if (twl->client && twl->client != client)
                        i2c_unregister_device(twl->client);
-               twl_modules[i].client = NULL;
+               twl->client = NULL;
        }
-       inuse = false;
+       twl_priv->ready = false;
        return 0;
 }
 
@@ -1179,6 +1137,17 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
        int                             status;
        unsigned                        i, num_slaves;
 
+       if (!node && !pdata) {
+               dev_err(&client->dev, "no platform data\n");
+               return -EINVAL;
+       }
+
+       if (twl_priv) {
+               dev_dbg(&client->dev, "only one instance of %s allowed\n",
+                       DRIVER_NAME);
+               return -EBUSY;
+       }
+
        pdev = platform_device_alloc(DRIVER_NAME, -1);
        if (!pdev) {
                dev_err(&client->dev, "can't alloc pdev\n");
@@ -1191,54 +1160,44 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
                return status;
        }
 
-       if (node && !pdata) {
-               /*
-                * XXX: Temporary pdata until the information is correctly
-                * retrieved by every TWL modules from DT.
-                */
-               pdata = devm_kzalloc(&client->dev,
-                                    sizeof(struct twl4030_platform_data),
-                                    GFP_KERNEL);
-               if (!pdata) {
-                       status = -ENOMEM;
-                       goto free;
-               }
-       }
-
-       if (!pdata) {
-               dev_dbg(&client->dev, "no platform data?\n");
-               status = -EINVAL;
-               goto free;
-       }
-
-       platform_set_drvdata(pdev, pdata);
-
        if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
                dev_dbg(&client->dev, "can't talk I2C?\n");
                status = -EIO;
                goto free;
        }
 
-       if (inuse) {
-               dev_dbg(&client->dev, "driver is already in use\n");
-               status = -EBUSY;
+       twl_priv = devm_kzalloc(&client->dev, sizeof(struct twl_private),
+                               GFP_KERNEL);
+       if (!twl_priv) {
+               status = -ENOMEM;
                goto free;
        }
 
        if ((id->driver_data) & TWL6030_CLASS) {
-               twl_id = TWL6030_CLASS_ID;
-               twl_map = &twl6030_map[0];
+               twl_priv->twl_id = TWL6030_CLASS_ID;
+               twl_priv->twl_map = &twl6030_map[0];
+               /* The charger base address is different in twl6025 */
+               if ((id->driver_data) & TWL6025_SUBCLASS)
+                       twl_priv->twl_map[TWL_MODULE_MAIN_CHARGE].base =
+                                                       TWL6025_BASEADD_CHARGER;
                twl_regmap_config = twl6030_regmap_config;
-               num_slaves = TWL_NUM_SLAVES - 1;
        } else {
-               twl_id = TWL4030_CLASS_ID;
-               twl_map = &twl4030_map[0];
+               twl_priv->twl_id = TWL4030_CLASS_ID;
+               twl_priv->twl_map = &twl4030_map[0];
                twl_regmap_config = twl4030_regmap_config;
-               num_slaves = TWL_NUM_SLAVES;
+       }
+
+       num_slaves = twl_get_num_slaves();
+       twl_priv->twl_modules = devm_kzalloc(&client->dev,
+                                        sizeof(struct twl_client) * num_slaves,
+                                        GFP_KERNEL);
+       if (!twl_priv->twl_modules) {
+               status = -ENOMEM;
+               goto free;
        }
 
        for (i = 0; i < num_slaves; i++) {
-               struct twl_client *twl = &twl_modules[i];
+               struct twl_client *twl = &twl_priv->twl_modules[i];
 
                if (i == 0) {
                        twl->client = client;
@@ -1264,19 +1223,19 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
                }
        }
 
-       inuse = true;
+       twl_priv->ready = true;
 
        /* setup clock framework */
-       clocks_init(&pdev->dev, pdata->clock);
+       clocks_init(&pdev->dev, pdata ? pdata->clock : NULL);
 
        /* read TWL IDCODE Register */
-       if (twl_id == TWL4030_CLASS_ID) {
+       if (twl_class_is_4030()) {
                status = twl_read_idcode_register();
                WARN(status < 0, "Error: reading twl_idcode register value\n");
        }
 
        /* load power event scripts */
-       if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata->power)
+       if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata && pdata->power)
                twl4030_power_init(pdata->power);
 
        /* Maybe init the T2 Interrupt subsystem */
@@ -1308,10 +1267,9 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
                twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
        }
 
-       status = -ENODEV;
        if (node)
                status = of_platform_populate(node, NULL, NULL, &client->dev);
-       if (status)
+       else
                status = add_children(pdata, irq_base, id->driver_data);
 
 fail:
index b151b7c1bd59d9fe0f7cad745802572a6f7156ea..8f59d88897e7287354354b1c2f7cf807e97b18b3 100644 (file)
@@ -192,7 +192,7 @@ config ICS932S401
 
 config ATMEL_SSC
        tristate "Device driver for Atmel SSC peripheral"
-       depends on AVR32 || ARCH_AT91
+       depends on HAS_IOMEM
        ---help---
          This option enables device driver support for Atmel Synchronized
          Serial Communication peripheral (SSC).
index 3c09cbb70b1dc01e45f1d6f4fdb93d7e351ad880..bd90dd23242e882e6e11fa1481a002e7689450d8 100644 (file)
@@ -175,7 +175,7 @@ static int ssc_probe(struct platform_device *pdev)
 
        /* disable all interrupts */
        clk_enable(ssc->clk);
-       ssc_writel(ssc->regs, IDR, ~0UL);
+       ssc_writel(ssc->regs, IDR, -1);
        ssc_readl(ssc->regs, SR);
        clk_disable(ssc->clk);
 
index eaddfe9db149a8f070ec11e5ae78c4e8821f175d..736c7714f5657000a5b82380f2928a1b6c89fe7c 100644 (file)
@@ -546,7 +546,7 @@ static noinline int fpga_program_dma(struct fpga_dev *priv)
                goto out_dma_unmap;
        }
 
-       dma_async_memcpy_issue_pending(chan);
+       dma_async_issue_pending(chan);
 
        /* Set the total byte count */
        fpga_set_byte_count(priv->regs, priv->bytes);
index 8835eabb3b8730d45d4deffb533bb6bf5ff332c4..7508cafff1039a4d984fae009eec21fdc5863763 100644 (file)
@@ -631,6 +631,8 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf)
        struct dma_async_tx_descriptor *tx;
        dma_cookie_t cookie;
        dma_addr_t dst, src;
+       unsigned long dma_flags = DMA_COMPL_SKIP_DEST_UNMAP |
+                                 DMA_COMPL_SKIP_SRC_UNMAP;
 
        dst_sg = buf->vb.sglist;
        dst_nents = buf->vb.sglen;
@@ -666,7 +668,7 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf)
        src = SYS_FPGA_BLOCK;
        tx = chan->device->device_prep_dma_memcpy(chan, dst, src,
                                                  REG_BLOCK_SIZE,
-                                                 0);
+                                                 dma_flags);
        if (!tx) {
                dev_err(priv->dev, "unable to prep SYS-FPGA DMA\n");
                return -ENOMEM;
@@ -749,7 +751,7 @@ static irqreturn_t data_irq(int irq, void *dev_id)
        submitted = true;
 
        /* Start the DMA Engine */
-       dma_async_memcpy_issue_pending(priv->chan);
+       dma_async_issue_pending(priv->chan);
 
 out:
        /* If no DMA was submitted, re-enable interrupts */
index 20636772c09bd2aa2b794f44c3029e1f57559578..d1efacc9c23c48030c4e5bf03425bb859f236fd5 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/edma.h>
 #include <linux/mmc/mmc.h>
 
+#include <linux/platform_data/edma.h>
 #include <linux/platform_data/mmc-davinci.h>
 
 /*
 /* MMCSD Init clock in Hz in opendrain mode */
 #define MMCSD_INIT_CLOCK               200000
 
-/*
- * One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units,
- * and we handle up to MAX_NR_SG segments.  MMC_BLOCK_BOUNCE kicks in only
- * for drivers with max_segs == 1, making the segments bigger (64KB)
- * than the page or two that's otherwise typical. nr_sg (passed from
- * platform data) == 16 gives at least the same throughput boost, using
- * EDMA transfer linkage instead of spending CPU time copying pages.
- */
-#define MAX_CCNT       ((1 << 16) - 1)
-
-#define MAX_NR_SG      16
-
 static unsigned rw_threshold = 32;
 module_param(rw_threshold, uint, S_IRUGO);
 MODULE_PARM_DESC(rw_threshold,
@@ -216,8 +205,6 @@ struct mmc_davinci_host {
        u8 version;
        /* for ns in one cycle calculation */
        unsigned ns_in_one_cycle;
-       /* Number of sg segments */
-       u8 nr_sg;
 #ifdef CONFIG_CPU_FREQ
        struct notifier_block   freq_transition;
 #endif
@@ -421,16 +408,7 @@ static int mmc_davinci_send_dma_request(struct mmc_davinci_host *host,
        int ret = 0;
 
        if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
-               struct dma_slave_config dma_tx_conf = {
-                       .direction = DMA_MEM_TO_DEV,
-                       .dst_addr = host->mem_res->start + DAVINCI_MMCDXR,
-                       .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
-                       .dst_maxburst =
-                               rw_threshold / DMA_SLAVE_BUSWIDTH_4_BYTES,
-               };
                chan = host->dma_tx;
-               dmaengine_slave_config(host->dma_tx, &dma_tx_conf);
-
                desc = dmaengine_prep_slave_sg(host->dma_tx,
                                data->sg,
                                host->sg_len,
@@ -443,16 +421,7 @@ static int mmc_davinci_send_dma_request(struct mmc_davinci_host *host,
                        goto out;
                }
        } else {
-               struct dma_slave_config dma_rx_conf = {
-                       .direction = DMA_DEV_TO_MEM,
-                       .src_addr = host->mem_res->start + DAVINCI_MMCDRR,
-                       .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
-                       .src_maxburst =
-                               rw_threshold / DMA_SLAVE_BUSWIDTH_4_BYTES,
-               };
                chan = host->dma_rx;
-               dmaengine_slave_config(host->dma_rx, &dma_rx_conf);
-
                desc = dmaengine_prep_slave_sg(host->dma_rx,
                                data->sg,
                                host->sg_len,
@@ -1165,6 +1134,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
        struct resource *r, *mem = NULL;
        int ret = 0, irq = 0;
        size_t mem_size;
+       struct dmaengine_chan_caps *dma_chan_caps;
 
        /* REVISIT:  when we're fully converted, fail if pdata is NULL */
 
@@ -1214,12 +1184,6 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
 
        init_mmcsd_host(host);
 
-       if (pdata->nr_sg)
-               host->nr_sg = pdata->nr_sg - 1;
-
-       if (host->nr_sg > MAX_NR_SG || !host->nr_sg)
-               host->nr_sg = MAX_NR_SG;
-
        host->use_dma = use_dma;
        host->mmc_irq = irq;
        host->sdio_irq = platform_get_irq(pdev, 1);
@@ -1248,14 +1212,27 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
                mmc->caps |= pdata->caps;
        mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
 
-       /* With no iommu coalescing pages, each phys_seg is a hw_seg.
-        * Each hw_seg uses one EDMA parameter RAM slot, always one
-        * channel and then usually some linked slots.
-        */
-       mmc->max_segs           = MAX_NR_SG;
+       {
+               struct dma_slave_config dma_txrx_conf = {
+                       .src_addr = host->mem_res->start + DAVINCI_MMCDRR,
+                       .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+                       .src_maxburst =
+                               rw_threshold / DMA_SLAVE_BUSWIDTH_4_BYTES,
+                       .dst_addr = host->mem_res->start + DAVINCI_MMCDXR,
+                       .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+                       .dst_maxburst =
+                               rw_threshold / DMA_SLAVE_BUSWIDTH_4_BYTES,
+               };
+               dmaengine_slave_config(host->dma_tx, &dma_txrx_conf);
+               dmaengine_slave_config(host->dma_rx, &dma_txrx_conf);
+       }
 
-       /* EDMA limit per hw segment (one or two MBytes) */
-       mmc->max_seg_size       = MAX_CCNT * rw_threshold;
+       /* Just check one channel for the DMA SG limits */
+       dma_chan_caps = dma_get_channel_caps(host->dma_tx, DMA_MEM_TO_DEV);
+       if (dma_chan_caps) {
+               mmc->max_segs = dma_chan_caps->seg_nr;
+               mmc->max_seg_size = dma_chan_caps->seg_len;
+       }
 
        /* MMC/SD controller limits for multiblock requests */
        mmc->max_blk_size       = 4095;  /* BLEN is 12 bits */
index bc5807873b2cc61031e943f855c63f09086e5c31..5487bc9e33c5bb8087dcb96c3cb3f3a60933f517 100644 (file)
@@ -405,6 +405,9 @@ static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata)
                ret = gpio_direction_input(pdata->slots[0].switch_pin);
                if (ret)
                        goto err_free_sp;
+               ret = gpio_set_debounce(pdata->slots[0].switch_pin, 50000);
+               if (ret)
+                       goto err_free_sp;
        } else
                pdata->slots[0].switch_pin = -EINVAL;
 
@@ -1769,6 +1772,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
        const struct of_device_id *match;
        dma_cap_mask_t mask;
        unsigned tx_req, rx_req;
+       struct dmaengine_chan_caps *dma_chan_caps;
        struct pinctrl *pinctrl;
 
        match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
@@ -1915,20 +1919,31 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
        dma_cap_zero(mask);
        dma_cap_set(DMA_SLAVE, mask);
 
-       host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req);
+       host->rx_chan =
+               dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
+                                                &rx_req, &pdev->dev, "rx");
+
        if (!host->rx_chan) {
                dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
                ret = -ENXIO;
                goto err_irq;
        }
 
-       host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req);
+       host->tx_chan =
+               dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
+                                                &tx_req, &pdev->dev, "tx");
+
        if (!host->tx_chan) {
                dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
                ret = -ENXIO;
                goto err_irq;
        }
 
+       /* Some DMA Engines only handle a limited number of SG segments */
+       dma_chan_caps = dma_get_channel_caps(host->rx_chan, DMA_DEV_TO_MEM);
+       if (dma_chan_caps && dma_chan_caps->seg_nr)
+               mmc->max_segs = dma_chan_caps->seg_nr;
+
        /* Request IRQ for MMC operations */
        ret = request_irq(host->irq, omap_hsmmc_irq, 0,
                        mmc_hostname(mmc), host);
index 67e62d3d495cce93a4d633937466c4c6a9524177..4244bbf6300c2ed218916acb495eab8e4edfa557 100644 (file)
@@ -573,23 +573,22 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
        dma_dev = chan->device;
        dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction);
 
+       flags |= DMA_COMPL_SKIP_SRC_UNMAP | DMA_COMPL_SKIP_DEST_UNMAP;
+
        if (direction == DMA_TO_DEVICE) {
                dma_src = dma_addr;
                dma_dst = host->data_pa;
-               flags |= DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_SKIP_DEST_UNMAP;
        } else {
                dma_src = host->data_pa;
                dma_dst = dma_addr;
-               flags |= DMA_COMPL_DEST_UNMAP_SINGLE | DMA_COMPL_SKIP_SRC_UNMAP;
        }
 
        tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src,
                        len, flags);
-
        if (!tx) {
                dev_err(host->dev, "device_prep_dma_memcpy error\n");
-               dma_unmap_single(dma_dev->dev, dma_addr, len, direction);
-               return -EIO;
+               ret = -EIO;
+               goto unmap_dma;
        }
 
        tx->callback = dma_complete;
@@ -599,7 +598,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
        ret = dma_submit_error(cookie);
        if (ret) {
                dev_err(host->dev, "dma_submit_error %d\n", cookie);
-               return ret;
+               goto unmap_dma;
        }
 
        dma_async_issue_pending(chan);
@@ -610,10 +609,17 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
        if (ret <= 0) {
                chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
                dev_err(host->dev, "wait_for_completion_timeout\n");
-               return ret ? ret : -ETIMEDOUT;
+               if (!ret)
+                       ret = -ETIMEDOUT;
+               goto unmap_dma;
        }
 
-       return 0;
+       ret = 0;
+
+unmap_dma:
+       dma_unmap_single(dma_dev->dev, dma_addr, len, direction);
+
+       return ret;
 }
 
 /*
index 40aff684aa23af64fc3ff03f0c5a54df798c0484..69e088515c8437e4e58392e2beb1c784f7da2672 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/of.h>
 #include <linux/of_net.h>
 #include <linux/of_device.h>
+#include <linux/if_vlan.h>
 
 #include <linux/platform_data/cpsw.h>
 
@@ -118,6 +119,20 @@ do {                                                               \
 #define TX_PRIORITY_MAPPING    0x33221100
 #define CPDMA_TX_PRIORITY_MAP  0x76543210
 
+#define CPSW_VLAN_AWARE                BIT(1)
+#define CPSW_ALE_VLAN_AWARE    1
+
+#define CPSW_FIFO_NORMAL_MODE          (0 << 15)
+#define CPSW_FIFO_DUAL_MAC_MODE                (1 << 15)
+#define CPSW_FIFO_RATE_LIMIT_MODE      (2 << 15)
+
+#define CPSW_INTPACEEN         (0x3f << 16)
+#define CPSW_INTPRESCALE_MASK  (0x7FF << 0)
+#define CPSW_CMINTMAX_CNT      63
+#define CPSW_CMINTMIN_CNT      2
+#define CPSW_CMINTMAX_INTVL    (1000 / CPSW_CMINTMIN_CNT)
+#define CPSW_CMINTMIN_INTVL    ((1000 / CPSW_CMINTMAX_CNT) + 1)
+
 #define cpsw_enable_irq(priv)  \
        do {                    \
                u32 i;          \
@@ -131,6 +146,10 @@ do {                                                               \
                        disable_irq_nosync(priv->irqs_table[i]); \
        } while (0);
 
+#define cpsw_slave_index(priv)                         \
+               ((priv->data.dual_emac) ? priv->emac_port :     \
+               priv->data.active_slave)
+
 static int debug_level;
 module_param(debug_level, int, 0);
 MODULE_PARM_DESC(debug_level, "cpsw debug level (NETIF_MSG bits)");
@@ -152,6 +171,15 @@ struct cpsw_wr_regs {
        u32     rx_en;
        u32     tx_en;
        u32     misc_en;
+       u32     mem_allign1[8];
+       u32     rx_thresh_stat;
+       u32     rx_stat;
+       u32     tx_stat;
+       u32     misc_stat;
+       u32     mem_allign2[8];
+       u32     rx_imax;
+       u32     tx_imax;
+
 };
 
 struct cpsw_ss_regs {
@@ -250,7 +278,7 @@ struct cpsw_ss_regs {
 struct cpsw_host_regs {
        u32     max_blks;
        u32     blk_cnt;
-       u32     flow_thresh;
+       u32     tx_in_ctl;
        u32     port_vlan;
        u32     tx_pri_map;
        u32     cpdma_tx_pri_map;
@@ -277,6 +305,9 @@ struct cpsw_slave {
        u32                             mac_control;
        struct cpsw_slave_data          *data;
        struct phy_device               *phy;
+       struct net_device               *ndev;
+       u32                             port_vlan;
+       u32                             open_stat;
 };
 
 static inline u32 slave_read(struct cpsw_slave *slave, u32 offset)
@@ -303,6 +334,8 @@ struct cpsw_priv {
        struct cpsw_host_regs __iomem   *host_port_regs;
        u32                             msg_enable;
        u32                             version;
+       u32                             coal_intvl;
+       u32                             bus_freq_mhz;
        struct net_device_stats         stats;
        int                             rx_packet_max;
        int                             host_port;
@@ -315,17 +348,65 @@ struct cpsw_priv {
        /* snapshot of IRQ numbers */
        u32 irqs_table[4];
        u32 num_irqs;
-       struct cpts cpts;
+       struct cpts *cpts;
+       u32 emac_port;
 };
 
 #define napi_to_priv(napi)     container_of(napi, struct cpsw_priv, napi)
-#define for_each_slave(priv, func, arg...)                     \
-       do {                                                    \
-               int idx;                                        \
-               for (idx = 0; idx < (priv)->data.slaves; idx++) \
-                       (func)((priv)->slaves + idx, ##arg);    \
+#define for_each_slave(priv, func, arg...)                             \
+       do {                                                            \
+               int idx;                                                \
+               if (priv->data.dual_emac)                               \
+                       (func)((priv)->slaves + priv->emac_port, ##arg);\
+               else                                                    \
+                       for (idx = 0; idx < (priv)->data.slaves; idx++) \
+                               (func)((priv)->slaves + idx, ##arg);    \
+       } while (0)
+#define cpsw_get_slave_ndev(priv, __slave_no__)                                \
+       (priv->slaves[__slave_no__].ndev)
+#define cpsw_get_slave_priv(priv, __slave_no__)                                \
+       ((priv->slaves[__slave_no__].ndev) ?                            \
+               netdev_priv(priv->slaves[__slave_no__].ndev) : NULL)    \
+
+#define cpsw_dual_emac_src_port_detect(status, priv, ndev, skb)                \
+       do {                                                            \
+               if (!priv->data.dual_emac)                              \
+                       break;                                          \
+               if (CPDMA_RX_SOURCE_PORT(status) == 1) {                \
+                       ndev = cpsw_get_slave_ndev(priv, 0);            \
+                       priv = netdev_priv(ndev);                       \
+                       skb->dev = ndev;                                \
+               } else if (CPDMA_RX_SOURCE_PORT(status) == 2) {         \
+                       ndev = cpsw_get_slave_ndev(priv, 1);            \
+                       priv = netdev_priv(ndev);                       \
+                       skb->dev = ndev;                                \
+               }                                                       \
+       } while (0)
+#define cpsw_add_mcast(priv, addr)                                     \
+       do {                                                            \
+               if (priv->data.dual_emac) {                             \
+                       struct cpsw_slave *slave = priv->slaves +       \
+                                               priv->emac_port;        \
+                       int slave_port = cpsw_get_slave_port(priv,      \
+                                               slave->slave_num);      \
+                       cpsw_ale_add_mcast(priv->ale, addr,             \
+                               1 << slave_port | 1 << priv->host_port, \
+                               ALE_VLAN, slave->port_vlan, 0);         \
+               } else {                                                \
+                       cpsw_ale_add_mcast(priv->ale, addr,             \
+                               ALE_ALL_PORTS << priv->host_port,       \
+                               0, 0, 0);                               \
+               }                                                       \
        } while (0)
 
+static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)
+{
+       if (priv->host_port == 0)
+               return slave_num + 1;
+       else
+               return slave_num;
+}
+
 static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
 {
        struct cpsw_priv *priv = netdev_priv(ndev);
@@ -344,8 +425,7 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
 
                /* program multicast address list into ALE register */
                netdev_for_each_mc_addr(ha, ndev) {
-                       cpsw_ale_add_mcast(priv->ale, (u8 *)ha->addr,
-                               ALE_ALL_PORTS << priv->host_port, 0, 0);
+                       cpsw_add_mcast(priv, (u8 *)ha->addr);
                }
        }
 }
@@ -374,9 +454,12 @@ void cpsw_tx_handler(void *token, int len, int status)
        struct net_device       *ndev = skb->dev;
        struct cpsw_priv        *priv = netdev_priv(ndev);
 
+       /* Check whether the queue is stopped due to stalled tx dma, if the
+        * queue is stopped then start the queue as we have free desc for tx
+        */
        if (unlikely(netif_queue_stopped(ndev)))
                netif_start_queue(ndev);
-       cpts_tx_timestamp(&priv->cpts, skb);
+       cpts_tx_timestamp(priv->cpts, skb);
        priv->stats.tx_packets++;
        priv->stats.tx_bytes += len;
        dev_kfree_skb_any(skb);
@@ -389,6 +472,8 @@ void cpsw_rx_handler(void *token, int len, int status)
        struct cpsw_priv        *priv = netdev_priv(ndev);
        int                     ret = 0;
 
+       cpsw_dual_emac_src_port_detect(status, priv, ndev, skb);
+
        /* free and bail if we are shutting down */
        if (unlikely(!netif_running(ndev)) ||
                        unlikely(!netif_carrier_ok(ndev))) {
@@ -397,7 +482,7 @@ void cpsw_rx_handler(void *token, int len, int status)
        }
        if (likely(status >= 0)) {
                skb_put(skb, len);
-               cpts_rx_timestamp(&priv->cpts, skb);
+               cpts_rx_timestamp(priv->cpts, skb);
                skb->protocol = eth_type_trans(skb, ndev);
                netif_receive_skb(skb);
                priv->stats.rx_bytes += len;
@@ -417,7 +502,7 @@ void cpsw_rx_handler(void *token, int len, int status)
                        return;
 
                ret = cpdma_chan_submit(priv->rxch, skb, skb->data,
-                                       skb_tailroom(skb), GFP_KERNEL);
+                                       skb_tailroom(skb), 0, GFP_KERNEL);
        }
        WARN_ON(ret < 0);
 }
@@ -430,37 +515,38 @@ static irqreturn_t cpsw_interrupt(int irq, void *dev_id)
                cpsw_intr_disable(priv);
                cpsw_disable_irq(priv);
                napi_schedule(&priv->napi);
+       } else {
+               priv = cpsw_get_slave_priv(priv, 1);
+               if (likely(priv) && likely(netif_running(priv->ndev))) {
+                       cpsw_intr_disable(priv);
+                       cpsw_disable_irq(priv);
+                       napi_schedule(&priv->napi);
+               }
        }
        return IRQ_HANDLED;
 }
 
-static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)
-{
-       if (priv->host_port == 0)
-               return slave_num + 1;
-       else
-               return slave_num;
-}
-
 static int cpsw_poll(struct napi_struct *napi, int budget)
 {
        struct cpsw_priv        *priv = napi_to_priv(napi);
        int                     num_tx, num_rx;
 
        num_tx = cpdma_chan_process(priv->txch, 128);
-       num_rx = cpdma_chan_process(priv->rxch, budget);
-
-       if (num_rx || num_tx)
-               cpsw_dbg(priv, intr, "poll %d rx, %d tx pkts\n",
-                        num_rx, num_tx);
+       if (num_tx)
+               cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX);
 
+       num_rx = cpdma_chan_process(priv->rxch, budget);
        if (num_rx < budget) {
                napi_complete(napi);
                cpsw_intr_enable(priv);
-               cpdma_ctlr_eoi(priv->dma);
+               cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX);
                cpsw_enable_irq(priv);
        }
 
+       if (num_rx || num_tx)
+               cpsw_dbg(priv, intr, "poll %d rx, %d tx pkts\n",
+                        num_rx, num_tx);
+
        return num_rx;
 }
 
@@ -548,6 +634,77 @@ static void cpsw_adjust_link(struct net_device *ndev)
        }
 }
 
+static int cpsw_get_coalesce(struct net_device *ndev,
+                               struct ethtool_coalesce *coal)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+
+       coal->rx_coalesce_usecs = priv->coal_intvl;
+       return 0;
+}
+
+static int cpsw_set_coalesce(struct net_device *ndev,
+                               struct ethtool_coalesce *coal)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+       u32 int_ctrl;
+       u32 num_interrupts = 0;
+       u32 prescale = 0;
+       u32 addnl_dvdr = 1;
+       u32 coal_intvl = 0;
+
+       if (!coal->rx_coalesce_usecs)
+               return -EINVAL;
+
+       coal_intvl = coal->rx_coalesce_usecs;
+
+       int_ctrl =  readl(&priv->wr_regs->int_control);
+       prescale = priv->bus_freq_mhz * 4;
+
+       if (coal_intvl < CPSW_CMINTMIN_INTVL)
+               coal_intvl = CPSW_CMINTMIN_INTVL;
+
+       if (coal_intvl > CPSW_CMINTMAX_INTVL) {
+               /* Interrupt pacer works with 4us Pulse, we can
+                * throttle further by dilating the 4us pulse.
+                */
+               addnl_dvdr = CPSW_INTPRESCALE_MASK / prescale;
+
+               if (addnl_dvdr > 1) {
+                       prescale *= addnl_dvdr;
+                       if (coal_intvl > (CPSW_CMINTMAX_INTVL * addnl_dvdr))
+                               coal_intvl = (CPSW_CMINTMAX_INTVL
+                                               * addnl_dvdr);
+               } else {
+                       addnl_dvdr = 1;
+                       coal_intvl = CPSW_CMINTMAX_INTVL;
+               }
+       }
+
+       num_interrupts = (1000 * addnl_dvdr) / coal_intvl;
+       writel(num_interrupts, &priv->wr_regs->rx_imax);
+       writel(num_interrupts, &priv->wr_regs->tx_imax);
+
+       int_ctrl |= CPSW_INTPACEEN;
+       int_ctrl &= (~CPSW_INTPRESCALE_MASK);
+       int_ctrl |= (prescale & CPSW_INTPRESCALE_MASK);
+       writel(int_ctrl, &priv->wr_regs->int_control);
+
+       cpsw_notice(priv, timer, "Set coalesce to %d usecs.\n", coal_intvl);
+       if (priv->data.dual_emac) {
+               int i;
+
+               for (i = 0; i < priv->data.slaves; i++) {
+                       priv = netdev_priv(priv->slaves[i].ndev);
+                       priv->coal_intvl = coal_intvl;
+               }
+       } else {
+               priv->coal_intvl = coal_intvl;
+       }
+
+       return 0;
+}
+
 static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val)
 {
        static char *leader = "........................................";
@@ -559,6 +716,54 @@ static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val)
                                leader + strlen(name), val);
 }
 
+static int cpsw_common_res_usage_state(struct cpsw_priv *priv)
+{
+       u32 i;
+       u32 usage_count = 0;
+
+       if (!priv->data.dual_emac)
+               return 0;
+
+       for (i = 0; i < priv->data.slaves; i++)
+               if (priv->slaves[i].open_stat)
+                       usage_count++;
+
+       return usage_count;
+}
+
+static inline int cpsw_tx_packet_submit(struct net_device *ndev,
+                       struct cpsw_priv *priv, struct sk_buff *skb)
+{
+       if (!priv->data.dual_emac)
+               return cpdma_chan_submit(priv->txch, skb, skb->data,
+                                 skb->len, 0, GFP_KERNEL);
+
+       if (ndev == cpsw_get_slave_ndev(priv, 0))
+               return cpdma_chan_submit(priv->txch, skb, skb->data,
+                                 skb->len, 1, GFP_KERNEL);
+       else
+               return cpdma_chan_submit(priv->txch, skb, skb->data,
+                                 skb->len, 2, GFP_KERNEL);
+}
+
+static inline void cpsw_add_dual_emac_def_ale_entries(
+               struct cpsw_priv *priv, struct cpsw_slave *slave,
+               u32 slave_port)
+{
+       u32 port_mask = 1 << slave_port | 1 << priv->host_port;
+
+       if (priv->version == CPSW_VERSION_1)
+               slave_write(slave, slave->port_vlan, CPSW1_PORT_VLAN);
+       else
+               slave_write(slave, slave->port_vlan, CPSW2_PORT_VLAN);
+       cpsw_ale_add_vlan(priv->ale, slave->port_vlan, port_mask,
+                         port_mask, port_mask, 0);
+       cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast,
+                          port_mask, ALE_VLAN, slave->port_vlan, 0);
+       cpsw_ale_add_ucast(priv->ale, priv->mac_addr,
+               priv->host_port, ALE_VLAN, slave->port_vlan);
+}
+
 static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
 {
        char name[32];
@@ -588,8 +793,11 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
 
        slave_port = cpsw_get_slave_port(priv, slave->slave_num);
 
-       cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast,
-                          1 << slave_port, 0, ALE_MCAST_FWD_2);
+       if (priv->data.dual_emac)
+               cpsw_add_dual_emac_def_ale_entries(priv, slave, slave_port);
+       else
+               cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast,
+                                  1 << slave_port, 0, 0, ALE_MCAST_FWD_2);
 
        slave->phy = phy_connect(priv->ndev, slave->data->phy_id,
                                 &cpsw_adjust_link, 0, slave->data->phy_if);
@@ -604,14 +812,44 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
        }
 }
 
+static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
+{
+       const int vlan = priv->data.default_vlan;
+       const int port = priv->host_port;
+       u32 reg;
+       int i;
+
+       reg = (priv->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN :
+              CPSW2_PORT_VLAN;
+
+       writel(vlan, &priv->host_port_regs->port_vlan);
+
+       for (i = 0; i < priv->data.slaves; i++)
+               slave_write(priv->slaves + i, vlan, reg);
+
+       cpsw_ale_add_vlan(priv->ale, vlan, ALE_ALL_PORTS << port,
+                         ALE_ALL_PORTS << port, ALE_ALL_PORTS << port,
+                         (ALE_PORT_1 | ALE_PORT_2) << port);
+}
+
 static void cpsw_init_host_port(struct cpsw_priv *priv)
 {
+       u32 control_reg;
+       u32 fifo_mode;
+
        /* soft reset the controller and initialize ale */
        soft_reset("cpsw", &priv->regs->soft_reset);
        cpsw_ale_start(priv->ale);
 
        /* switch to vlan unaware mode */
-       cpsw_ale_control_set(priv->ale, 0, ALE_VLAN_AWARE, 0);
+       cpsw_ale_control_set(priv->ale, priv->host_port, ALE_VLAN_AWARE,
+                            CPSW_ALE_VLAN_AWARE);
+       control_reg = readl(&priv->regs->control);
+       control_reg |= CPSW_VLAN_AWARE;
+       writel(control_reg, &priv->regs->control);
+       fifo_mode = (priv->data.dual_emac) ? CPSW_FIFO_DUAL_MAC_MODE :
+                    CPSW_FIFO_NORMAL_MODE;
+       writel(fifo_mode, &priv->host_port_regs->tx_in_ctl);
 
        /* setup host port priority mapping */
        __raw_writel(CPDMA_TX_PRIORITY_MAP,
@@ -621,9 +859,12 @@ static void cpsw_init_host_port(struct cpsw_priv *priv)
        cpsw_ale_control_set(priv->ale, priv->host_port,
                             ALE_PORT_STATE, ALE_PORT_STATE_FORWARD);
 
-       cpsw_ale_add_ucast(priv->ale, priv->mac_addr, priv->host_port, 0);
-       cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast,
-                          1 << priv->host_port, 0, ALE_MCAST_FWD_2);
+       if (!priv->data.dual_emac) {
+               cpsw_ale_add_ucast(priv->ale, priv->mac_addr, priv->host_port,
+                                  0, 0);
+               cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast,
+                                  1 << priv->host_port, 0, 0, ALE_MCAST_FWD_2);
+       }
 }
 
 static int cpsw_ndo_open(struct net_device *ndev)
@@ -632,7 +873,8 @@ static int cpsw_ndo_open(struct net_device *ndev)
        int i, ret;
        u32 reg;
 
-       cpsw_intr_disable(priv);
+       if (!cpsw_common_res_usage_state(priv))
+               cpsw_intr_disable(priv);
        netif_carrier_off(ndev);
 
        pm_runtime_get_sync(&priv->pdev->dev);
@@ -644,43 +886,63 @@ static int cpsw_ndo_open(struct net_device *ndev)
                 CPSW_RTL_VERSION(reg));
 
        /* initialize host and slave ports */
-       cpsw_init_host_port(priv);
+       if (!cpsw_common_res_usage_state(priv))
+               cpsw_init_host_port(priv);
        for_each_slave(priv, cpsw_slave_open, priv);
 
-       /* setup tx dma to fixed prio and zero offset */
-       cpdma_control_set(priv->dma, CPDMA_TX_PRIO_FIXED, 1);
-       cpdma_control_set(priv->dma, CPDMA_RX_BUFFER_OFFSET, 0);
+       /* Add default VLAN */
+       if (!priv->data.dual_emac)
+               cpsw_add_default_vlan(priv);
 
-       /* disable priority elevation and enable statistics on all ports */
-       __raw_writel(0, &priv->regs->ptype);
+       if (!cpsw_common_res_usage_state(priv)) {
+               /* setup tx dma to fixed prio and zero offset */
+               cpdma_control_set(priv->dma, CPDMA_TX_PRIO_FIXED, 1);
+               cpdma_control_set(priv->dma, CPDMA_RX_BUFFER_OFFSET, 0);
 
-       /* enable statistics collection only on the host port */
-       __raw_writel(0x7, &priv->regs->stat_port_en);
+               /* disable priority elevation */
+               __raw_writel(0, &priv->regs->ptype);
 
-       if (WARN_ON(!priv->data.rx_descs))
-               priv->data.rx_descs = 128;
+               /* enable statistics collection only on all ports */
+               __raw_writel(0x7, &priv->regs->stat_port_en);
 
-       for (i = 0; i < priv->data.rx_descs; i++) {
-               struct sk_buff *skb;
+               if (WARN_ON(!priv->data.rx_descs))
+                       priv->data.rx_descs = 128;
 
-               ret = -ENOMEM;
-               skb = netdev_alloc_skb_ip_align(priv->ndev,
-                                               priv->rx_packet_max);
-               if (!skb)
-                       break;
-               ret = cpdma_chan_submit(priv->rxch, skb, skb->data,
-                                       skb_tailroom(skb), GFP_KERNEL);
-               if (WARN_ON(ret < 0))
-                       break;
+               for (i = 0; i < priv->data.rx_descs; i++) {
+                       struct sk_buff *skb;
+
+                       ret = -ENOMEM;
+                       skb = netdev_alloc_skb_ip_align(priv->ndev,
+                                                       priv->rx_packet_max);
+                       if (!skb)
+                               break;
+                       ret = cpdma_chan_submit(priv->rxch, skb, skb->data,
+                                       skb_tailroom(skb), 0, GFP_KERNEL);
+                       if (WARN_ON(ret < 0))
+                               break;
+               }
+               /* continue even if we didn't manage to submit all
+                * receive descs
+                */
+               cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i);
+       }
+
+       /* Enable Interrupt pacing if configured */
+       if (priv->coal_intvl != 0) {
+               struct ethtool_coalesce coal;
+
+               coal.rx_coalesce_usecs = (priv->coal_intvl << 4);
+               cpsw_set_coalesce(ndev, &coal);
        }
-       /* continue even if we didn't manage to submit all receive descs */
-       cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i);
 
        cpdma_ctlr_start(priv->dma);
        cpsw_intr_enable(priv);
        napi_enable(&priv->napi);
-       cpdma_ctlr_eoi(priv->dma);
+       cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX);
+       cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX);
 
+       if (priv->data.dual_emac)
+               priv->slaves[priv->emac_port].open_stat = true;
        return 0;
 }
 
@@ -701,12 +963,17 @@ static int cpsw_ndo_stop(struct net_device *ndev)
        netif_stop_queue(priv->ndev);
        napi_disable(&priv->napi);
        netif_carrier_off(priv->ndev);
-       cpsw_intr_disable(priv);
-       cpdma_ctlr_int_ctrl(priv->dma, false);
-       cpdma_ctlr_stop(priv->dma);
-       cpsw_ale_stop(priv->ale);
+
+       if (cpsw_common_res_usage_state(priv) <= 1) {
+               cpsw_intr_disable(priv);
+               cpdma_ctlr_int_ctrl(priv->dma, false);
+               cpdma_ctlr_stop(priv->dma);
+               cpsw_ale_stop(priv->ale);
+       }
        for_each_slave(priv, cpsw_slave_stop, priv);
        pm_runtime_put_sync(&priv->pdev->dev);
+       if (priv->data.dual_emac)
+               priv->slaves[priv->emac_port].open_stat = false;
        return 0;
 }
 
@@ -724,18 +991,24 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
                return NETDEV_TX_OK;
        }
 
-       if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && priv->cpts.tx_enable)
+       if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
+                               priv->cpts->tx_enable)
                skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
 
        skb_tx_timestamp(skb);
 
-       ret = cpdma_chan_submit(priv->txch, skb, skb->data,
-                               skb->len, GFP_KERNEL);
+       ret = cpsw_tx_packet_submit(ndev, priv, skb);
        if (unlikely(ret != 0)) {
                cpsw_err(priv, tx_err, "desc submit failed\n");
                goto fail;
        }
 
+       /* If there is no more tx desc left free then we need to
+        * tell the kernel to stop sending us tx frames.
+        */
+       if (unlikely(!cpdma_check_free_tx_desc(priv->txch)))
+               netif_stop_queue(ndev);
+
        return NETDEV_TX_OK;
 fail:
        priv->stats.tx_dropped++;
@@ -770,10 +1043,10 @@ static void cpsw_ndo_change_rx_flags(struct net_device *ndev, int flags)
 
 static void cpsw_hwtstamp_v1(struct cpsw_priv *priv)
 {
-       struct cpsw_slave *slave = &priv->slaves[priv->data.cpts_active_slave];
+       struct cpsw_slave *slave = &priv->slaves[priv->data.active_slave];
        u32 ts_en, seq_id;
 
-       if (!priv->cpts.tx_enable && !priv->cpts.rx_enable) {
+       if (!priv->cpts->tx_enable && !priv->cpts->rx_enable) {
                slave_write(slave, 0, CPSW1_TS_CTL);
                return;
        }
@@ -781,10 +1054,10 @@ static void cpsw_hwtstamp_v1(struct cpsw_priv *priv)
        seq_id = (30 << CPSW_V1_SEQ_ID_OFS_SHIFT) | ETH_P_1588;
        ts_en = EVENT_MSG_BITS << CPSW_V1_MSG_TYPE_OFS;
 
-       if (priv->cpts.tx_enable)
+       if (priv->cpts->tx_enable)
                ts_en |= CPSW_V1_TS_TX_EN;
 
-       if (priv->cpts.rx_enable)
+       if (priv->cpts->rx_enable)
                ts_en |= CPSW_V1_TS_RX_EN;
 
        slave_write(slave, ts_en, CPSW1_TS_CTL);
@@ -793,16 +1066,21 @@ static void cpsw_hwtstamp_v1(struct cpsw_priv *priv)
 
 static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
 {
-       struct cpsw_slave *slave = &priv->slaves[priv->data.cpts_active_slave];
+       struct cpsw_slave *slave;
        u32 ctrl, mtype;
 
+       if (priv->data.dual_emac)
+               slave = &priv->slaves[priv->emac_port];
+       else
+               slave = &priv->slaves[priv->data.active_slave];
+
        ctrl = slave_read(slave, CPSW2_CONTROL);
        ctrl &= ~CTRL_ALL_TS_MASK;
 
-       if (priv->cpts.tx_enable)
+       if (priv->cpts->tx_enable)
                ctrl |= CTRL_TX_TS_BITS;
 
-       if (priv->cpts.rx_enable)
+       if (priv->cpts->rx_enable)
                ctrl |= CTRL_RX_TS_BITS;
 
        mtype = (30 << TS_SEQ_ID_OFFSET_SHIFT) | EVENT_MSG_BITS;
@@ -815,7 +1093,7 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
 static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
 {
        struct cpsw_priv *priv = netdev_priv(dev);
-       struct cpts *cpts = &priv->cpts;
+       struct cpts *cpts = priv->cpts;
        struct hwtstamp_config cfg;
 
        if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
@@ -879,14 +1157,26 @@ static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
 
 static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
 {
+       struct cpsw_priv *priv = netdev_priv(dev);
+       struct mii_ioctl_data *data = if_mii(req);
+       int slave_no = cpsw_slave_index(priv);
+
        if (!netif_running(dev))
                return -EINVAL;
 
+       switch (cmd) {
 #ifdef CONFIG_TI_CPTS
-       if (cmd == SIOCSHWTSTAMP)
+       case SIOCSHWTSTAMP:
                return cpsw_hwtstamp_ioctl(dev, req);
 #endif
-       return -ENOTSUPP;
+       case SIOCGMIIPHY:
+               data->phy_id = priv->slaves[slave_no].phy->addr;
+               break;
+       default:
+               return -ENOTSUPP;
+       }
+
+       return 0;
 }
 
 static void cpsw_ndo_tx_timeout(struct net_device *ndev)
@@ -901,7 +1191,9 @@ static void cpsw_ndo_tx_timeout(struct net_device *ndev)
        cpdma_chan_start(priv->txch);
        cpdma_ctlr_int_ctrl(priv->dma, true);
        cpsw_intr_enable(priv);
-       cpdma_ctlr_eoi(priv->dma);
+       cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX);
+       cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX);
+
 }
 
 static struct net_device_stats *cpsw_ndo_get_stats(struct net_device *ndev)
@@ -920,10 +1212,79 @@ static void cpsw_ndo_poll_controller(struct net_device *ndev)
        cpsw_interrupt(ndev->irq, priv);
        cpdma_ctlr_int_ctrl(priv->dma, true);
        cpsw_intr_enable(priv);
-       cpdma_ctlr_eoi(priv->dma);
+       cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX);
+       cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX);
+
 }
 #endif
 
+static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv,
+                               unsigned short vid)
+{
+       int ret;
+
+       ret = cpsw_ale_add_vlan(priv->ale, vid,
+                               ALE_ALL_PORTS << priv->host_port,
+                               0, ALE_ALL_PORTS << priv->host_port,
+                               (ALE_PORT_1 | ALE_PORT_2) << priv->host_port);
+       if (ret != 0)
+               return ret;
+
+       ret = cpsw_ale_add_ucast(priv->ale, priv->mac_addr,
+                                priv->host_port, ALE_VLAN, vid);
+       if (ret != 0)
+               goto clean_vid;
+
+       ret = cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast,
+                                ALE_ALL_PORTS << priv->host_port,
+                                ALE_VLAN, vid, 0);
+       if (ret != 0)
+               goto clean_vlan_ucast;
+       return 0;
+
+clean_vlan_ucast:
+       cpsw_ale_del_ucast(priv->ale, priv->mac_addr,
+                           priv->host_port, ALE_VLAN, vid);
+clean_vid:
+       cpsw_ale_del_vlan(priv->ale, vid, 0);
+       return ret;
+}
+
+static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev,
+               unsigned short vid)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+
+       if (vid == priv->data.default_vlan)
+               return 0;
+
+       dev_info(priv->dev, "Adding vlanid %d to vlan filter\n", vid);
+       return cpsw_add_vlan_ale_entry(priv, vid);
+}
+
+static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev,
+               unsigned short vid)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+       int ret;
+
+       if (vid == priv->data.default_vlan)
+               return 0;
+
+       dev_info(priv->dev, "removing vlanid %d from vlan filter\n", vid);
+       ret = cpsw_ale_del_vlan(priv->ale, vid, 0);
+       if (ret != 0)
+               return ret;
+
+       ret = cpsw_ale_del_ucast(priv->ale, priv->mac_addr,
+                                priv->host_port, ALE_VLAN, vid);
+       if (ret != 0)
+               return ret;
+
+       return cpsw_ale_del_mcast(priv->ale, priv->ndev->broadcast,
+                                 0, ALE_VLAN, vid);
+}
+
 static const struct net_device_ops cpsw_netdev_ops = {
        .ndo_open               = cpsw_ndo_open,
        .ndo_stop               = cpsw_ndo_stop,
@@ -938,6 +1299,8 @@ static const struct net_device_ops cpsw_netdev_ops = {
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = cpsw_ndo_poll_controller,
 #endif
+       .ndo_vlan_rx_add_vid    = cpsw_ndo_vlan_rx_add_vid,
+       .ndo_vlan_rx_kill_vid   = cpsw_ndo_vlan_rx_kill_vid,
 };
 
 static void cpsw_get_drvinfo(struct net_device *ndev,
@@ -974,7 +1337,7 @@ static int cpsw_get_ts_info(struct net_device *ndev,
                SOF_TIMESTAMPING_RX_SOFTWARE |
                SOF_TIMESTAMPING_SOFTWARE |
                SOF_TIMESTAMPING_RAW_HARDWARE;
-       info->phc_index = priv->cpts.phc_index;
+       info->phc_index = priv->cpts->phc_index;
        info->tx_types =
                (1 << HWTSTAMP_TX_OFF) |
                (1 << HWTSTAMP_TX_ON);
@@ -993,12 +1356,39 @@ static int cpsw_get_ts_info(struct net_device *ndev,
        return 0;
 }
 
+static int cpsw_get_settings(struct net_device *ndev,
+                            struct ethtool_cmd *ecmd)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+       int slave_no = cpsw_slave_index(priv);
+
+       if (priv->slaves[slave_no].phy)
+               return phy_ethtool_gset(priv->slaves[slave_no].phy, ecmd);
+       else
+               return -EOPNOTSUPP;
+}
+
+static int cpsw_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+       int slave_no = cpsw_slave_index(priv);
+
+       if (priv->slaves[slave_no].phy)
+               return phy_ethtool_sset(priv->slaves[slave_no].phy, ecmd);
+       else
+               return -EOPNOTSUPP;
+}
+
 static const struct ethtool_ops cpsw_ethtool_ops = {
        .get_drvinfo    = cpsw_get_drvinfo,
        .get_msglevel   = cpsw_get_msglevel,
        .set_msglevel   = cpsw_set_msglevel,
        .get_link       = ethtool_op_get_link,
        .get_ts_info    = cpsw_get_ts_info,
+       .get_settings   = cpsw_get_settings,
+       .set_settings   = cpsw_set_settings,
+       .get_coalesce   = cpsw_get_coalesce,
+       .set_coalesce   = cpsw_set_coalesce,
 };
 
 static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv,
@@ -1011,6 +1401,7 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv,
        slave->data     = data;
        slave->regs     = regs + slave_reg_ofs;
        slave->sliver   = regs + sliver_reg_ofs;
+       slave->port_vlan = data->dual_emac_res_vlan;
 }
 
 static int cpsw_probe_dt(struct cpsw_platform_data *data,
@@ -1030,12 +1421,12 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
        }
        data->slaves = prop;
 
-       if (of_property_read_u32(node, "cpts_active_slave", &prop)) {
-               pr_err("Missing cpts_active_slave property in the DT.\n");
+       if (of_property_read_u32(node, "active_slave", &prop)) {
+               pr_err("Missing active_slave property in the DT.\n");
                ret = -EINVAL;
                goto error_ret;
        }
-       data->cpts_active_slave = prop;
+       data->active_slave = prop;
 
        if (of_property_read_u32(node, "cpts_clock_mult", &prop)) {
                pr_err("Missing cpts_clock_mult property in the DT.\n");
@@ -1093,6 +1484,9 @@ 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;
+
        /*
         * Populate all the child nodes here...
         */
@@ -1126,6 +1520,18 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                if (mac_addr)
                        memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN);
 
+               if (data->dual_emac) {
+                       if (of_property_read_u32(node, "dual_emac_res_vlan",
+                                                &prop)) {
+                               pr_err("Missing dual_emac_res_vlan in DT.\n");
+                               slave_data->dual_emac_res_vlan = i+1;
+                               pr_err("Using %d as Reserved VLAN for %d slave\n",
+                                      slave_data->dual_emac_res_vlan, i);
+                       } else {
+                               slave_data->dual_emac_res_vlan = prop;
+                       }
+               }
+
                i++;
        }
 
@@ -1136,6 +1542,82 @@ error_ret:
        return ret;
 }
 
+static int cpsw_probe_dual_emac(struct platform_device *pdev,
+                               struct cpsw_priv *priv)
+{
+       struct cpsw_platform_data       *data = &priv->data;
+       struct net_device               *ndev;
+       struct cpsw_priv                *priv_sl2;
+       int ret = 0, i;
+
+       ndev = alloc_etherdev(sizeof(struct cpsw_priv));
+       if (!ndev) {
+               pr_err("cpsw: error allocating net_device\n");
+               return -ENOMEM;
+       }
+
+       priv_sl2 = netdev_priv(ndev);
+       spin_lock_init(&priv_sl2->lock);
+       priv_sl2->data = *data;
+       priv_sl2->pdev = pdev;
+       priv_sl2->ndev = ndev;
+       priv_sl2->dev  = &ndev->dev;
+       priv_sl2->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
+       priv_sl2->rx_packet_max = max(rx_packet_max, 128);
+
+       if (is_valid_ether_addr(data->slave_data[1].mac_addr)) {
+               memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr,
+                       ETH_ALEN);
+               pr_info("cpsw: Detected MACID = %pM\n", priv_sl2->mac_addr);
+       } else {
+               random_ether_addr(priv_sl2->mac_addr);
+               pr_info("cpsw: Random MACID = %pM\n", priv_sl2->mac_addr);
+       }
+       memcpy(ndev->dev_addr, priv_sl2->mac_addr, ETH_ALEN);
+
+       priv_sl2->slaves = priv->slaves;
+       priv_sl2->clk = priv->clk;
+
+       priv_sl2->coal_intvl = 0;
+       priv_sl2->bus_freq_mhz = priv->bus_freq_mhz;
+
+       priv_sl2->cpsw_res = priv->cpsw_res;
+       priv_sl2->regs = priv->regs;
+       priv_sl2->host_port = priv->host_port;
+       priv_sl2->host_port_regs = priv->host_port_regs;
+       priv_sl2->wr_regs = priv->wr_regs;
+       priv_sl2->dma = priv->dma;
+       priv_sl2->txch = priv->txch;
+       priv_sl2->rxch = priv->rxch;
+       priv_sl2->ale = priv->ale;
+       priv_sl2->emac_port = 1;
+       priv->slaves[1].ndev = ndev;
+       priv_sl2->cpts = priv->cpts;
+       priv_sl2->version = priv->version;
+
+       for (i = 0; i < priv->num_irqs; i++) {
+               priv_sl2->irqs_table[i] = priv->irqs_table[i];
+               priv_sl2->num_irqs = priv->num_irqs;
+       }
+
+       ndev->features |= NETIF_F_HW_VLAN_FILTER;
+
+       ndev->netdev_ops = &cpsw_netdev_ops;
+       SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops);
+       netif_napi_add(ndev, &priv_sl2->napi, cpsw_poll, CPSW_POLL_WEIGHT);
+
+       /* register the network device */
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+       ret = register_netdev(ndev);
+       if (ret) {
+               pr_err("cpsw: error registering net device\n");
+               free_netdev(ndev);
+               ret = -ENODEV;
+       }
+
+       return ret;
+}
+
 static int cpsw_probe(struct platform_device *pdev)
 {
        struct cpsw_platform_data       *data = pdev->dev.platform_data;
@@ -1162,6 +1644,11 @@ static int cpsw_probe(struct platform_device *pdev)
        priv->dev  = &ndev->dev;
        priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
        priv->rx_packet_max = max(rx_packet_max, 128);
+       priv->cpts = devm_kzalloc(&pdev->dev, sizeof(struct cpts), GFP_KERNEL);
+       if (!ndev) {
+               pr_err("error allocating cpts\n");
+               goto clean_ndev_ret;
+       }
 
        /*
         * This may be required here for child devices.
@@ -1194,12 +1681,17 @@ static int cpsw_probe(struct platform_device *pdev)
        for (i = 0; i < data->slaves; i++)
                priv->slaves[i].slave_num = i;
 
+       priv->slaves[0].ndev = ndev;
+       priv->emac_port = 0;
+
        priv->clk = clk_get(&pdev->dev, "fck");
        if (IS_ERR(priv->clk)) {
                dev_err(&pdev->dev, "fck is not found\n");
                ret = -ENODEV;
                goto clean_slave_ret;
        }
+       priv->coal_intvl = 0;
+       priv->bus_freq_mhz = clk_get_rate(priv->clk) / 1000000;
 
        priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!priv->cpsw_res) {
@@ -1248,7 +1740,7 @@ static int cpsw_probe(struct platform_device *pdev)
        switch (priv->version) {
        case CPSW_VERSION_1:
                priv->host_port_regs = ss_regs + CPSW1_HOST_PORT_OFFSET;
-               priv->cpts.reg       = ss_regs + CPSW1_CPTS_OFFSET;
+               priv->cpts->reg       = ss_regs + CPSW1_CPTS_OFFSET;
                dma_params.dmaregs   = ss_regs + CPSW1_CPDMA_OFFSET;
                dma_params.txhdp     = ss_regs + CPSW1_STATERAM_OFFSET;
                ale_params.ale_regs  = ss_regs + CPSW1_ALE_OFFSET;
@@ -1259,7 +1751,7 @@ static int cpsw_probe(struct platform_device *pdev)
                break;
        case CPSW_VERSION_2:
                priv->host_port_regs = ss_regs + CPSW2_HOST_PORT_OFFSET;
-               priv->cpts.reg       = ss_regs + CPSW2_CPTS_OFFSET;
+               priv->cpts->reg       = ss_regs + CPSW2_CPTS_OFFSET;
                dma_params.dmaregs   = ss_regs + CPSW2_CPDMA_OFFSET;
                dma_params.txhdp     = ss_regs + CPSW2_STATERAM_OFFSET;
                ale_params.ale_regs  = ss_regs + CPSW2_ALE_OFFSET;
@@ -1346,7 +1838,7 @@ static int cpsw_probe(struct platform_device *pdev)
                k++;
        }
 
-       ndev->flags |= IFF_ALLMULTI;    /* see cpsw_ndo_change_rx_flags() */
+       ndev->features |= NETIF_F_HW_VLAN_FILTER;
 
        ndev->netdev_ops = &cpsw_netdev_ops;
        SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops);
@@ -1361,13 +1853,21 @@ static int cpsw_probe(struct platform_device *pdev)
                goto clean_irq_ret;
        }
 
-       if (cpts_register(&pdev->dev, &priv->cpts,
+       if (cpts_register(&pdev->dev, priv->cpts,
                          data->cpts_clock_mult, data->cpts_clock_shift))
                dev_err(priv->dev, "error registering cpts device\n");
 
        cpsw_notice(priv, probe, "initialized device (regs %x, irq %d)\n",
                  priv->cpsw_res->start, ndev->irq);
 
+       if (priv->data.dual_emac) {
+               ret = cpsw_probe_dual_emac(pdev, priv);
+               if (ret) {
+                       cpsw_err(priv, probe, "error probe slave 2 emac interface\n");
+                       goto clean_irq_ret;
+               }
+       }
+
        return 0;
 
 clean_irq_ret:
@@ -1406,7 +1906,7 @@ static int cpsw_remove(struct platform_device *pdev)
        pr_info("removing device");
        platform_set_drvdata(pdev, NULL);
 
-       cpts_unregister(&priv->cpts);
+       cpts_unregister(priv->cpts);
        free_irq(ndev->irq, priv);
        cpsw_ale_destroy(priv->ale);
        cpdma_chan_destroy(priv->txch);
index 0e9ccc2cf91fefce7bda15f6c9ad4ee580e6aa5d..7fa60d6092edf9c4fa1caf641dbe0764ea583e7a 100644 (file)
@@ -148,7 +148,7 @@ static int cpsw_ale_write(struct cpsw_ale *ale, int idx, u32 *ale_entry)
        return idx;
 }
 
-static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr)
+int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr, u16 vid)
 {
        u32 ale_entry[ALE_ENTRY_WORDS];
        int type, idx;
@@ -160,6 +160,8 @@ static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr)
                type = cpsw_ale_get_entry_type(ale_entry);
                if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
                        continue;
+               if (cpsw_ale_get_vlan_id(ale_entry) != vid)
+                       continue;
                cpsw_ale_get_addr(ale_entry, entry_addr);
                if (memcmp(entry_addr, addr, 6) == 0)
                        return idx;
@@ -167,6 +169,22 @@ static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr)
        return -ENOENT;
 }
 
+int cpsw_ale_match_vlan(struct cpsw_ale *ale, u16 vid)
+{
+       u32 ale_entry[ALE_ENTRY_WORDS];
+       int type, idx;
+
+       for (idx = 0; idx < ale->params.ale_entries; idx++) {
+               cpsw_ale_read(ale, idx, ale_entry);
+               type = cpsw_ale_get_entry_type(ale_entry);
+               if (type != ALE_TYPE_VLAN)
+                       continue;
+               if (cpsw_ale_get_vlan_id(ale_entry) == vid)
+                       return idx;
+       }
+       return -ENOENT;
+}
+
 static int cpsw_ale_match_free(struct cpsw_ale *ale)
 {
        u32 ale_entry[ALE_ENTRY_WORDS];
@@ -274,19 +292,32 @@ int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask)
        return 0;
 }
 
-int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags)
+static inline void cpsw_ale_set_vlan_entry_type(u32 *ale_entry,
+                                               int flags, u16 vid)
+{
+       if (flags & ALE_VLAN) {
+               cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR);
+               cpsw_ale_set_vlan_id(ale_entry, vid);
+       } else {
+               cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
+       }
+}
+
+int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port,
+                      int flags, u16 vid)
 {
        u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
        int idx;
 
-       cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
+       cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid);
+
        cpsw_ale_set_addr(ale_entry, addr);
        cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT);
        cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0);
        cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
        cpsw_ale_set_port_num(ale_entry, port);
 
-       idx = cpsw_ale_match_addr(ale, addr);
+       idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
        if (idx < 0)
                idx = cpsw_ale_match_free(ale);
        if (idx < 0)
@@ -298,12 +329,13 @@ int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags)
        return 0;
 }
 
-int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port)
+int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port,
+                      int flags, u16 vid)
 {
        u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
        int idx;
 
-       idx = cpsw_ale_match_addr(ale, addr);
+       idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
        if (idx < 0)
                return -ENOENT;
 
@@ -313,18 +345,19 @@ int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port)
 }
 
 int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
-                       int super, int mcast_state)
+                      int flags, u16 vid, int mcast_state)
 {
        u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
        int idx, mask;
 
-       idx = cpsw_ale_match_addr(ale, addr);
+       idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
        if (idx >= 0)
                cpsw_ale_read(ale, idx, ale_entry);
 
-       cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
+       cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid);
+
        cpsw_ale_set_addr(ale_entry, addr);
-       cpsw_ale_set_super(ale_entry, super);
+       cpsw_ale_set_super(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
        cpsw_ale_set_mcast_state(ale_entry, mcast_state);
 
        mask = cpsw_ale_get_port_mask(ale_entry);
@@ -342,12 +375,13 @@ int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
        return 0;
 }
 
-int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask)
+int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
+                      int flags, u16 vid)
 {
        u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
        int idx;
 
-       idx = cpsw_ale_match_addr(ale, addr);
+       idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
        if (idx < 0)
                return -EINVAL;
 
@@ -362,6 +396,55 @@ int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask)
        return 0;
 }
 
+int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
+                     int reg_mcast, int unreg_mcast)
+{
+       u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
+       int idx;
+
+       idx = cpsw_ale_match_vlan(ale, vid);
+       if (idx >= 0)
+               cpsw_ale_read(ale, idx, ale_entry);
+
+       cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN);
+       cpsw_ale_set_vlan_id(ale_entry, vid);
+
+       cpsw_ale_set_vlan_untag_force(ale_entry, untag);
+       cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast);
+       cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
+       cpsw_ale_set_vlan_member_list(ale_entry, port);
+
+       if (idx < 0)
+               idx = cpsw_ale_match_free(ale);
+       if (idx < 0)
+               idx = cpsw_ale_find_ageable(ale);
+       if (idx < 0)
+               return -ENOMEM;
+
+       cpsw_ale_write(ale, idx, ale_entry);
+       return 0;
+}
+
+int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask)
+{
+       u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
+       int idx;
+
+       idx = cpsw_ale_match_vlan(ale, vid);
+       if (idx < 0)
+               return -ENOENT;
+
+       cpsw_ale_read(ale, idx, ale_entry);
+
+       if (port_mask)
+               cpsw_ale_set_vlan_member_list(ale_entry, port_mask);
+       else
+               cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
+
+       cpsw_ale_write(ale, idx, ale_entry);
+       return 0;
+}
+
 struct ale_control_info {
        const char      *name;
        int             offset, port_offset;
index 2bd09cbce522dc222fb5a72f9691d368a3b57094..30daa1265f0c27c1d277a0d953d9d73c6e1995cf 100644 (file)
@@ -64,8 +64,14 @@ enum cpsw_ale_port_state {
 };
 
 /* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */
-#define ALE_SECURE                     1
-#define ALE_BLOCKED                    2
+#define ALE_SECURE                     BIT(0)
+#define ALE_BLOCKED                    BIT(1)
+#define ALE_SUPER                      BIT(2)
+#define ALE_VLAN                       BIT(3)
+
+#define ALE_PORT_HOST                  BIT(0)
+#define ALE_PORT_1                     BIT(1)
+#define ALE_PORT_2                     BIT(2)
 
 #define ALE_MCAST_FWD                  0
 #define ALE_MCAST_BLOCK_LEARN_FWD      1
@@ -81,11 +87,17 @@ void cpsw_ale_stop(struct cpsw_ale *ale);
 int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout);
 int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask);
 int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask);
-int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags);
-int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port);
+int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port,
+                      int flags, u16 vid);
+int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port,
+                      int flags, u16 vid);
 int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
-                       int super, int mcast_state);
-int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask);
+                      int flags, u16 vid, int mcast_state);
+int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
+                      int flags, u16 vid);
+int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
+                       int reg_mcast, int unreg_mcast);
+int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port);
 
 int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control);
 int cpsw_ale_control_set(struct cpsw_ale *ale, int port,
index 49956730cd8db884ac1696d108cb62fe61d49533..ee13dc78430c18f542d4a59dad5d9d09a8256b8c 100644 (file)
@@ -60,6 +60,9 @@
 #define CPDMA_DESC_EOQ         BIT(28)
 #define CPDMA_DESC_TD_COMPLETE BIT(27)
 #define CPDMA_DESC_PASS_CRC    BIT(26)
+#define CPDMA_DESC_TO_PORT_EN  BIT(20)
+#define CPDMA_TO_PORT_SHIFT    16
+#define CPDMA_DESC_PORT_MASK   (BIT(18) | BIT(17) | BIT(16))
 
 #define CPDMA_TEARDOWN_VALUE   0xfffffffc
 
@@ -105,13 +108,13 @@ struct cpdma_ctlr {
 };
 
 struct cpdma_chan {
+       struct cpdma_desc __iomem       *head, *tail;
+       void __iomem                    *hdp, *cp, *rxfree;
        enum cpdma_state                state;
        struct cpdma_ctlr               *ctlr;
        int                             chan_num;
        spinlock_t                      lock;
-       struct cpdma_desc __iomem       *head, *tail;
        int                             count;
-       void __iomem                    *hdp, *cp, *rxfree;
        u32                             mask;
        cpdma_handler_fn                handler;
        enum dma_data_direction         dir;
@@ -132,6 +135,14 @@ struct cpdma_chan {
 #define chan_write(chan, fld, v)       __raw_writel(v, (chan)->fld)
 #define desc_write(desc, fld, v)       __raw_writel((u32)(v), &(desc)->fld)
 
+#define cpdma_desc_to_port(chan, mode, directed)                       \
+       do {                                                            \
+               if (!is_rx_chan(chan) && ((directed == 1) ||            \
+                                         (directed == 2)))             \
+                       mode |= (CPDMA_DESC_TO_PORT_EN |                \
+                                (directed << CPDMA_TO_PORT_SHIFT));    \
+       } while (0)
+
 /*
  * Utility constructs for a cpdma descriptor pool.  Some devices (e.g. davinci
  * emac) have dedicated on-chip memory for these descriptors.  Some other
@@ -217,17 +228,27 @@ desc_from_phys(struct cpdma_desc_pool *pool, dma_addr_t dma)
 }
 
 static struct cpdma_desc __iomem *
-cpdma_desc_alloc(struct cpdma_desc_pool *pool, int num_desc)
+cpdma_desc_alloc(struct cpdma_desc_pool *pool, int num_desc, bool is_rx)
 {
        unsigned long flags;
        int index;
+       int desc_start;
+       int desc_end;
        struct cpdma_desc __iomem *desc = NULL;
 
        spin_lock_irqsave(&pool->lock, flags);
 
-       index = bitmap_find_next_zero_area(pool->bitmap, pool->num_desc, 0,
-                                          num_desc, 0);
-       if (index < pool->num_desc) {
+       if (is_rx) {
+               desc_start = 0;
+               desc_end = pool->num_desc/2;
+        } else {
+               desc_start = pool->num_desc/2;
+               desc_end = pool->num_desc;
+       }
+
+       index = bitmap_find_next_zero_area(pool->bitmap,
+                               desc_end, desc_start, num_desc, 0);
+       if (index < desc_end) {
                bitmap_set(pool->bitmap, index, num_desc);
                desc = pool->iomap + pool->desc_size * index;
                pool->used_desc++;
@@ -439,10 +460,8 @@ int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr)
        if (ctlr->state != CPDMA_STATE_IDLE)
                cpdma_ctlr_stop(ctlr);
 
-       for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
-               if (ctlr->channels[i])
-                       cpdma_chan_destroy(ctlr->channels[i]);
-       }
+       for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++)
+               cpdma_chan_destroy(ctlr->channels[i]);
 
        cpdma_desc_pool_destroy(ctlr->pool);
        spin_unlock_irqrestore(&ctlr->lock, flags);
@@ -473,11 +492,13 @@ int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable)
        spin_unlock_irqrestore(&ctlr->lock, flags);
        return 0;
 }
+EXPORT_SYMBOL_GPL(cpdma_ctlr_int_ctrl);
 
-void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr)
+void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value)
 {
-       dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, 0);
+       dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, value);
 }
+EXPORT_SYMBOL_GPL(cpdma_ctlr_eoi);
 
 struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
                                     cpdma_handler_fn handler)
@@ -652,7 +673,7 @@ static void __cpdma_chan_submit(struct cpdma_chan *chan,
 }
 
 int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
-                     int len, gfp_t gfp_mask)
+                     int len, int directed, gfp_t gfp_mask)
 {
        struct cpdma_ctlr               *ctlr = chan->ctlr;
        struct cpdma_desc __iomem       *desc;
@@ -668,7 +689,7 @@ int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
                goto unlock_ret;
        }
 
-       desc = cpdma_desc_alloc(ctlr->pool, 1);
+       desc = cpdma_desc_alloc(ctlr->pool, 1, is_rx_chan(chan));
        if (!desc) {
                chan->stats.desc_alloc_fail++;
                ret = -ENOMEM;
@@ -682,6 +703,7 @@ int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
 
        buffer = dma_map_single(ctlr->dev, data, len, chan->dir);
        mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP;
+       cpdma_desc_to_port(chan, mode, directed);
 
        desc_write(desc, hw_next,   0);
        desc_write(desc, hw_buffer, buffer);
@@ -704,6 +726,29 @@ unlock_ret:
 }
 EXPORT_SYMBOL_GPL(cpdma_chan_submit);
 
+bool cpdma_check_free_tx_desc(struct cpdma_chan *chan)
+{
+       unsigned long flags;
+       int index;
+       bool ret;
+       struct cpdma_ctlr       *ctlr = chan->ctlr;
+       struct cpdma_desc_pool  *pool = ctlr->pool;
+
+       spin_lock_irqsave(&pool->lock, flags);
+
+       index = bitmap_find_next_zero_area(pool->bitmap,
+                               pool->num_desc, pool->num_desc/2, 1, 0);
+
+       if (index < pool->num_desc)
+               ret = true;
+       else
+               ret = false;
+
+       spin_unlock_irqrestore(&pool->lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cpdma_check_free_tx_desc);
+
 static void __cpdma_chan_free(struct cpdma_chan *chan,
                              struct cpdma_desc __iomem *desc,
                              int outlen, int status)
@@ -749,7 +794,8 @@ static int __cpdma_chan_process(struct cpdma_chan *chan)
                status = -EBUSY;
                goto unlock_ret;
        }
-       status  = status & (CPDMA_DESC_EOQ | CPDMA_DESC_TD_COMPLETE);
+       status  = status & (CPDMA_DESC_EOQ | CPDMA_DESC_TD_COMPLETE |
+                           CPDMA_DESC_PORT_MASK);
 
        chan->head = desc_from_phys(pool, desc_read(desc, hw_next));
        chan_write(chan, cp, desc_dma);
@@ -984,3 +1030,4 @@ unlock_ret:
        spin_unlock_irqrestore(&ctlr->lock, flags);
        return ret;
 }
+EXPORT_SYMBOL_GPL(cpdma_control_set);
index afa19a0c0d81000d265a8930c9ecec7220863518..d9bcc6032fdc9406299dea2af8f7123f9561ecea 100644 (file)
 #define __chan_linear(chan_num)        ((chan_num) & (CPDMA_MAX_CHANNELS - 1))
 #define chan_linear(chan)      __chan_linear((chan)->chan_num)
 
+#define CPDMA_RX_SOURCE_PORT(__status__)       ((__status__ >> 16) & 0x7)
+
+#define CPDMA_EOI_RX_THRESH    0x0
+#define CPDMA_EOI_RX           0x1
+#define CPDMA_EOI_TX           0x2
+#define CPDMA_EOI_MISC         0x3
+
 struct cpdma_params {
        struct device           *dev;
        void __iomem            *dmaregs;
@@ -82,12 +89,13 @@ int cpdma_chan_dump(struct cpdma_chan *chan);
 int cpdma_chan_get_stats(struct cpdma_chan *chan,
                         struct cpdma_chan_stats *stats);
 int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
-                     int len, gfp_t gfp_mask);
+                     int len, int directed, gfp_t gfp_mask);
 int cpdma_chan_process(struct cpdma_chan *chan, int quota);
 
 int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable);
-void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr);
+void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value);
 int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable);
+bool cpdma_check_free_tx_desc(struct cpdma_chan *chan);
 
 enum cpdma_control {
        CPDMA_CMD_IDLE,                 /* write-only */
index 2a3e2c56bc60ef8a8d2a959086c13d75b8494f96..f727e326b63c676d5ab2d35beaa6df9f14f0829f 100644 (file)
@@ -120,7 +120,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
 #define EMAC_DEF_TX_CH                 (0) /* Default 0th channel */
 #define EMAC_DEF_RX_CH                 (0) /* Default 0th channel */
 #define EMAC_DEF_RX_NUM_DESC           (128)
-#define EMAC_DEF_TX_NUM_DESC           (128)
 #define EMAC_DEF_MAX_TX_CH             (1) /* Max TX channels configured */
 #define EMAC_DEF_MAX_RX_CH             (1) /* Max RX channels configured */
 #define EMAC_POLL_WEIGHT               (64) /* Default NAPI poll weight */
@@ -342,7 +341,6 @@ struct emac_priv {
        u32 mac_hash2;
        u32 multicast_hash_cnt[EMAC_NUM_MULTICAST_BITS];
        u32 rx_addr_type;
-       atomic_t cur_tx;
        const char *phy_id;
 #ifdef CONFIG_OF
        struct device_node *phy_node;
@@ -1039,7 +1037,7 @@ static void emac_rx_handler(void *token, int len, int status)
 
 recycle:
        ret = cpdma_chan_submit(priv->rxchan, skb, skb->data,
-                       skb_tailroom(skb), GFP_KERNEL);
+                       skb_tailroom(skb), 0, GFP_KERNEL);
 
        WARN_ON(ret == -ENOMEM);
        if (unlikely(ret < 0))
@@ -1050,10 +1048,10 @@ static void emac_tx_handler(void *token, int len, int status)
 {
        struct sk_buff          *skb = token;
        struct net_device       *ndev = skb->dev;
-       struct emac_priv        *priv = netdev_priv(ndev);
-
-       atomic_dec(&priv->cur_tx);
 
+       /* Check whether the queue is stopped due to stalled tx dma, if the
+        * queue is stopped then start the queue as we have free desc for tx
+        */
        if (unlikely(netif_queue_stopped(ndev)))
                netif_start_queue(ndev);
        ndev->stats.tx_packets++;
@@ -1094,14 +1092,17 @@ static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev)
        skb_tx_timestamp(skb);
 
        ret_code = cpdma_chan_submit(priv->txchan, skb, skb->data, skb->len,
-                                    GFP_KERNEL);
+                                    0, GFP_KERNEL);
        if (unlikely(ret_code != 0)) {
                if (netif_msg_tx_err(priv) && net_ratelimit())
                        dev_err(emac_dev, "DaVinci EMAC: desc submit failed");
                goto fail_tx;
        }
 
-       if (atomic_inc_return(&priv->cur_tx) >= EMAC_DEF_TX_NUM_DESC)
+       /* If there is no more tx desc left free then we need to
+        * tell the kernel to stop sending us tx frames.
+        */
+       if (unlikely(cpdma_check_free_tx_desc(priv->txch)))
                netif_stop_queue(ndev);
 
        return NETDEV_TX_OK;
@@ -1558,7 +1559,7 @@ static int emac_dev_open(struct net_device *ndev)
                        break;
 
                ret = cpdma_chan_submit(priv->rxchan, skb, skb->data,
-                                       skb_tailroom(skb), GFP_KERNEL);
+                                       skb_tailroom(skb), 0, GFP_KERNEL);
                if (WARN_ON(ret < 0))
                        break;
        }
index 30ca0a60a64c020e6b3609bff361078e03b6a89f..1d264c0f5a9b468285296e706cfef2d367678085 100644 (file)
@@ -240,13 +240,14 @@ static const struct ath_ops ath5k_common_ops = {
 * Driver Initialization *
 \***********************/
 
-static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
+static void ath5k_reg_notifier(struct wiphy *wiphy,
+                              struct regulatory_request *request)
 {
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct ath5k_hw *ah = hw->priv;
        struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
 
-       return ath_reg_notifier_apply(wiphy, request, regulatory);
+       ath_reg_notifier_apply(wiphy, request, regulatory);
 }
 
 /********************\
index 5516a8ccc3c6809589ff1d1fd8756859f504f7e9..4225cca0f198199a4e0b0e28ee5b4748cb85a45d 100644 (file)
@@ -3492,8 +3492,8 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
                ath6kl_cfg80211_stop(vif);
 }
 
-static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy,
-                                     struct regulatory_request *request)
+static void ath6kl_cfg80211_reg_notify(struct wiphy *wiphy,
+                                      struct regulatory_request *request)
 {
        struct ath6kl *ar = wiphy_priv(wiphy);
        u32 rates[IEEE80211_NUM_BANDS];
@@ -3506,17 +3506,13 @@ static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy,
                   request->processed ? " processed" : "",
                   request->initiator, request->user_reg_hint_type);
 
-       /*
-        * As firmware is not able intersect regdoms, we can only listen to
-        * cellular hints.
-        */
        if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE)
-               return -EOPNOTSUPP;
+               return;
 
        ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, request->alpha2);
        if (ret) {
                ath6kl_err("failed to set regdomain: %d\n", ret);
-               return ret;
+               return;
        }
 
        /*
@@ -3536,10 +3532,8 @@ static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy,
        if (ret) {
                ath6kl_err("failed to start scan for a regdomain change: %d\n",
                           ret);
-               return ret;
+               return;
        }
-
-       return 0;
 }
 
 static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
index 7647ed6b73d770278ca63875c314bcce9f98ddf2..17507dc8a1e717ea637ba9dee75b633eddde37b7 100644 (file)
@@ -58,6 +58,7 @@ config ATH9K_DEBUGFS
        bool "Atheros ath9k debugging"
        depends on ATH9K
        select MAC80211_DEBUGFS
+       select RELAY
        ---help---
          Say Y, if you need access to ath9k's statistics for
          interrupts, rate control, etc.
index 3a69804f4c16e94e369691a78f612e859d86d90f..d1ff3c246a1232cd1be306fbddce97df9a3d0d28 100644 (file)
@@ -86,29 +86,25 @@ static int ath_ahb_probe(struct platform_device *pdev)
 
        if (!pdev->dev.platform_data) {
                dev_err(&pdev->dev, "no platform data specified\n");
-               ret = -EINVAL;
-               goto err_out;
+               return -EINVAL;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL) {
                dev_err(&pdev->dev, "no memory resource found\n");
-               ret = -ENXIO;
-               goto err_out;
+               return -ENXIO;
        }
 
-       mem = ioremap_nocache(res->start, resource_size(res));
+       mem = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res));
        if (mem == NULL) {
                dev_err(&pdev->dev, "ioremap failed\n");
-               ret = -ENOMEM;
-               goto err_out;
+               return -ENOMEM;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (res == NULL) {
                dev_err(&pdev->dev, "no IRQ resource found\n");
-               ret = -ENXIO;
-               goto err_iounmap;
+               return -ENXIO;
        }
 
        irq = res->start;
@@ -116,8 +112,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
        hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
        if (hw == NULL) {
                dev_err(&pdev->dev, "no memory for ieee80211_hw\n");
-               ret = -ENOMEM;
-               goto err_iounmap;
+               return -ENOMEM;
        }
 
        SET_IEEE80211_DEV(hw, &pdev->dev);
@@ -156,9 +151,6 @@ static int ath_ahb_probe(struct platform_device *pdev)
  err_free_hw:
        ieee80211_free_hw(hw);
        platform_set_drvdata(pdev, NULL);
- err_iounmap:
-       iounmap(mem);
- err_out:
        return ret;
 }
 
@@ -168,12 +160,10 @@ static int ath_ahb_remove(struct platform_device *pdev)
 
        if (hw) {
                struct ath_softc *sc = hw->priv;
-               void __iomem *mem = sc->mem;
 
                ath9k_deinit_device(sc);
                free_irq(sc->irq, sc);
                ieee80211_free_hw(sc->hw);
-               iounmap(mem);
                platform_set_drvdata(pdev, NULL);
        }
 
index e09ec40ce71ab6c25bd801a0661c61f446496aed..7ecd40f07a7442d4769c6e0e60331f9824f3d217 100644 (file)
@@ -152,7 +152,8 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,
        ath_dbg(common, ANI, "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",
                aniState->ofdmNoiseImmunityLevel,
                immunityLevel, BEACON_RSSI(ah),
-               aniState->rssiThrLow, aniState->rssiThrHigh);
+               ATH9K_ANI_RSSI_THR_LOW,
+               ATH9K_ANI_RSSI_THR_HIGH);
 
        if (!scan)
                aniState->ofdmNoiseImmunityLevel = immunityLevel;
@@ -173,7 +174,7 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,
 
        weak_sig = entry_ofdm->ofdm_weak_signal_on;
        if (ah->opmode == NL80211_IFTYPE_STATION &&
-           BEACON_RSSI(ah) <= aniState->rssiThrHigh)
+           BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH)
                weak_sig = true;
 
        if (aniState->ofdmWeakSigDetect != weak_sig)
@@ -216,11 +217,11 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel,
 
        ath_dbg(common, ANI, "**** ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",
                aniState->cckNoiseImmunityLevel, immunityLevel,
-               BEACON_RSSI(ah), aniState->rssiThrLow,
-               aniState->rssiThrHigh);
+               BEACON_RSSI(ah), ATH9K_ANI_RSSI_THR_LOW,
+               ATH9K_ANI_RSSI_THR_HIGH);
 
        if (ah->opmode == NL80211_IFTYPE_STATION &&
-           BEACON_RSSI(ah) <= aniState->rssiThrLow &&
+           BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_LOW &&
            immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI)
                immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI;
 
@@ -418,9 +419,6 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)
                return;
 
        aniState = &ah->curchan->ani;
-       if (WARN_ON(!aniState))
-               return;
-
        if (!ath9k_hw_ani_read_counters(ah))
                return;
 
@@ -489,23 +487,6 @@ void ath9k_hw_disable_mib_counters(struct ath_hw *ah)
 }
 EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
 
-void ath9k_hw_ani_setup(struct ath_hw *ah)
-{
-       int i;
-
-       static const int totalSizeDesired[] = { -55, -55, -55, -55, -62 };
-       static const int coarseHigh[] = { -14, -14, -14, -14, -12 };
-       static const int coarseLow[] = { -64, -64, -64, -64, -70 };
-       static const int firpwr[] = { -78, -78, -78, -78, -80 };
-
-       for (i = 0; i < 5; i++) {
-               ah->totalSizeDesired[i] = totalSizeDesired[i];
-               ah->coarse_high[i] = coarseHigh[i];
-               ah->coarse_low[i] = coarseLow[i];
-               ah->firpwr[i] = firpwr[i];
-       }
-}
-
 void ath9k_hw_ani_init(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
@@ -531,8 +512,6 @@ void ath9k_hw_ani_init(struct ath_hw *ah)
 
                ani->ofdmsTurn = true;
 
-               ani->rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
-               ani->rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
                ani->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
                ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
                ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
index 1485bf5e3518851eab0e2f6531af8f5ed7118824..dddb1361039a71de9dbd205bba879e0dfcb8a14a 100644 (file)
@@ -104,7 +104,6 @@ struct ath9k_ani_default {
 };
 
 struct ar5416AniState {
-       struct ath9k_channel *c;
        u8 noiseImmunityLevel;
        u8 ofdmNoiseImmunityLevel;
        u8 cckNoiseImmunityLevel;
@@ -113,15 +112,9 @@ struct ar5416AniState {
        u8 spurImmunityLevel;
        u8 firstepLevel;
        u8 ofdmWeakSigDetect;
-       u8 cckWeakSigThreshold;
        u32 listenTime;
-       int32_t rssiThrLow;
-       int32_t rssiThrHigh;
        u32 ofdmPhyErrCount;
        u32 cckPhyErrCount;
-       int16_t pktRssi[2];
-       int16_t ofdmErrRssi[2];
-       int16_t cckErrRssi[2];
        struct ath9k_ani_default iniDef;
 };
 
@@ -147,7 +140,6 @@ struct ar5416Stats {
 
 void ath9k_enable_mib_counters(struct ath_hw *ah);
 void ath9k_hw_disable_mib_counters(struct ath_hw *ah);
-void ath9k_hw_ani_setup(struct ath_hw *ah);
 void ath9k_hw_ani_init(struct ath_hw *ah);
 
 #endif /* ANI_H */
index f81e7fc60a364b499dfc9fba0d5ea6483ee5aba5..467ccfae2ceed8e12588a9517709c51301297e11 100644 (file)
@@ -466,7 +466,7 @@ static const u32 ar5416Bank0[][2] = {
 };
 
 static const u32 ar5416BB_RfGain[][3] = {
-       /* Addr      5G_HT20     5G_HT40   */
+       /* Addr      5G          2G        */
        {0x00009a00, 0x00000000, 0x00000000},
        {0x00009a04, 0x00000040, 0x00000040},
        {0x00009a08, 0x00000080, 0x00000080},
@@ -546,12 +546,12 @@ static const u32 ar5416Bank2[][2] = {
 };
 
 static const u32 ar5416Bank3[][3] = {
-       /* Addr      5G_HT20     5G_HT40   */
+       /* Addr      5G          2G        */
        {0x000098f0, 0x01400018, 0x01c00018},
 };
 
 static const u32 ar5416Bank6[][3] = {
-       /* Addr      5G_HT20     5G_HT40   */
+       /* Addr      5G          2G        */
        {0x0000989c, 0x00000000, 0x00000000},
        {0x0000989c, 0x00000000, 0x00000000},
        {0x0000989c, 0x00000000, 0x00000000},
@@ -588,7 +588,7 @@ static const u32 ar5416Bank6[][3] = {
 };
 
 static const u32 ar5416Bank6TPC[][3] = {
-       /* Addr      5G_HT20     5G_HT40   */
+       /* Addr      5G          2G        */
        {0x0000989c, 0x00000000, 0x00000000},
        {0x0000989c, 0x00000000, 0x00000000},
        {0x0000989c, 0x00000000, 0x00000000},
index 874186bfda41d1fd93a3838725ab3e95d3210369..fd69376ecc83a0ee89bc9da8e9472b396863cbde 100644 (file)
@@ -470,16 +470,15 @@ static void ar5008_hw_spur_mitigate(struct ath_hw *ah,
 static int ar5008_hw_rf_alloc_ext_banks(struct ath_hw *ah)
 {
 #define ATH_ALLOC_BANK(bank, size) do { \
-               bank = kzalloc((sizeof(u32) * size), GFP_KERNEL); \
-               if (!bank) { \
-                       ath_err(common, "Cannot allocate RF banks\n"); \
-                       return -ENOMEM; \
-               } \
+               bank = devm_kzalloc(ah->dev, sizeof(u32) * size, GFP_KERNEL); \
+               if (!bank) \
+                       goto error; \
        } while (0);
 
        struct ath_common *common = ath9k_hw_common(ah);
 
-       BUG_ON(AR_SREV_9280_20_OR_LATER(ah));
+       if (AR_SREV_9280_20_OR_LATER(ah))
+           return 0;
 
        ATH_ALLOC_BANK(ah->analogBank0Data, ah->iniBank0.ia_rows);
        ATH_ALLOC_BANK(ah->analogBank1Data, ah->iniBank1.ia_rows);
@@ -492,35 +491,12 @@ static int ar5008_hw_rf_alloc_ext_banks(struct ath_hw *ah)
 
        return 0;
 #undef ATH_ALLOC_BANK
+error:
+       ath_err(common, "Cannot allocate RF banks\n");
+       return -ENOMEM;
 }
 
 
-/**
- * ar5008_hw_rf_free_ext_banks - Free memory for analog bank scratch buffers
- * @ah: atheros hardware struture
- * For the external AR2133/AR5133 radios banks.
- */
-static void ar5008_hw_rf_free_ext_banks(struct ath_hw *ah)
-{
-#define ATH_FREE_BANK(bank) do { \
-               kfree(bank); \
-               bank = NULL; \
-       } while (0);
-
-       BUG_ON(AR_SREV_9280_20_OR_LATER(ah));
-
-       ATH_FREE_BANK(ah->analogBank0Data);
-       ATH_FREE_BANK(ah->analogBank1Data);
-       ATH_FREE_BANK(ah->analogBank2Data);
-       ATH_FREE_BANK(ah->analogBank3Data);
-       ATH_FREE_BANK(ah->analogBank6Data);
-       ATH_FREE_BANK(ah->analogBank6TPCData);
-       ATH_FREE_BANK(ah->analogBank7Data);
-       ATH_FREE_BANK(ah->bank6Temp);
-
-#undef ATH_FREE_BANK
-}
-
 /* *
  * ar5008_hw_set_rf_regs - programs rf registers based on EEPROM
  * @ah: atheros hardware structure
@@ -1380,7 +1356,7 @@ static void ar5008_hw_set_radar_conf(struct ath_hw *ah)
        conf->radar_inband = 8;
 }
 
-void ar5008_hw_attach_phy_ops(struct ath_hw *ah)
+int ar5008_hw_attach_phy_ops(struct ath_hw *ah)
 {
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
        static const u32 ar5416_cca_regs[6] = {
@@ -1391,12 +1367,15 @@ void ar5008_hw_attach_phy_ops(struct ath_hw *ah)
                AR_PHY_CH1_EXT_CCA,
                AR_PHY_CH2_EXT_CCA
        };
+       int ret;
+
+       ret = ar5008_hw_rf_alloc_ext_banks(ah);
+       if (ret)
+           return ret;
 
        priv_ops->rf_set_freq = ar5008_hw_set_channel;
        priv_ops->spur_mitigate_freq = ar5008_hw_spur_mitigate;
 
-       priv_ops->rf_alloc_ext_banks = ar5008_hw_rf_alloc_ext_banks;
-       priv_ops->rf_free_ext_banks = ar5008_hw_rf_free_ext_banks;
        priv_ops->set_rf_regs = ar5008_hw_set_rf_regs;
        priv_ops->set_channel_regs = ar5008_hw_set_channel_regs;
        priv_ops->init_bb = ar5008_hw_init_bb;
@@ -1421,4 +1400,5 @@ void ar5008_hw_attach_phy_ops(struct ath_hw *ah)
        ar5008_hw_set_nf_limits(ah);
        ar5008_hw_set_radar_conf(ah);
        memcpy(ah->nf_regs, ar5416_cca_regs, sizeof(ah->nf_regs));
+       return 0;
 }
index ea4a230997acc7c34c3e944a9ddf4d8c9e65f914..59524e1d4678c5fd0eb4a73979648cc76c2d353b 100644 (file)
@@ -460,7 +460,7 @@ static const u32 ar5416Common_9100[][2] = {
 };
 
 static const u32 ar5416Bank6_9100[][3] = {
-       /* Addr      5G_HT20     5G_HT40   */
+       /* Addr      5G          2G        */
        {0x0000989c, 0x00000000, 0x00000000},
        {0x0000989c, 0x00000000, 0x00000000},
        {0x0000989c, 0x00000000, 0x00000000},
@@ -497,7 +497,7 @@ static const u32 ar5416Bank6_9100[][3] = {
 };
 
 static const u32 ar5416Bank6TPC_9100[][3] = {
-       /* Addr      5G_HT20     5G_HT40   */
+       /* Addr      5G          2G        */
        {0x0000989c, 0x00000000, 0x00000000},
        {0x0000989c, 0x00000000, 0x00000000},
        {0x0000989c, 0x00000000, 0x00000000},
index 648da3e885e9250080f0403ebb74437b02b0e892..f053d978540e712658124e4e884273bf423cb833 100644 (file)
 
 /* General hardware code for the A5008/AR9001/AR9002 hadware families */
 
-static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
+static int ar9002_hw_init_mode_regs(struct ath_hw *ah)
 {
        if (AR_SREV_9271(ah)) {
                INIT_INI_ARRAY(&ah->iniModes, ar9271Modes_9271);
                INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271);
                INIT_INI_ARRAY(&ah->iniModes_9271_ANI_reg, ar9271Modes_9271_ANI_reg);
-               return;
+               return 0;
        }
 
        if (ah->config.pcie_clock_req)
@@ -102,9 +102,9 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
                u32 size = sizeof(u32) * addac->ia_rows * addac->ia_columns;
                u32 *data;
 
-               data = kmalloc(size, GFP_KERNEL);
+               data = devm_kzalloc(ah->dev, size, GFP_KERNEL);
                if (!data)
-                       return;
+                       return -ENOMEM;
 
                memcpy(data, addac->ia_array, size);
                addac->ia_array = data;
@@ -120,6 +120,7 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
                INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
                       ar9287Common_japan_2484_cck_fir_coeff_9287_1_1);
        }
+       return 0;
 }
 
 static void ar9280_20_hw_init_rxgain_ini(struct ath_hw *ah)
@@ -409,22 +410,30 @@ void ar9002_hw_enable_async_fifo(struct ath_hw *ah)
 }
 
 /* Sets up the AR5008/AR9001/AR9002 hardware familiy callbacks */
-void ar9002_hw_attach_ops(struct ath_hw *ah)
+int ar9002_hw_attach_ops(struct ath_hw *ah)
 {
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
        struct ath_hw_ops *ops = ath9k_hw_ops(ah);
+       int ret;
+
+       ret = ar9002_hw_init_mode_regs(ah);
+       if (ret)
+               return ret;
 
-       priv_ops->init_mode_regs = ar9002_hw_init_mode_regs;
        priv_ops->init_mode_gain_regs = ar9002_hw_init_mode_gain_regs;
 
        ops->config_pci_powersave = ar9002_hw_configpcipowersave;
 
-       ar5008_hw_attach_phy_ops(ah);
+       ret = ar5008_hw_attach_phy_ops(ah);
+       if (ret)
+               return ret;
+
        if (AR_SREV_9280_20_OR_LATER(ah))
                ar9002_hw_attach_phy_ops(ah);
 
        ar9002_hw_attach_calib_ops(ah);
        ar9002_hw_attach_mac_ops(ah);
+       return 0;
 }
 
 void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan)
index 846dd7974eb8ff4c571574d5f26dcd2c1ed98a64..f4003512d8d57e223c59fc10477ad9f99d262ede 100644 (file)
@@ -555,14 +555,73 @@ static void ar9002_hw_antdiv_comb_conf_set(struct ath_hw *ah,
        REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
 }
 
+static void ar9002_hw_spectral_scan_config(struct ath_hw *ah,
+                                   struct ath_spec_scan *param)
+{
+       u8 count;
+
+       if (!param->enabled) {
+               REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN,
+                           AR_PHY_SPECTRAL_SCAN_ENABLE);
+               return;
+       }
+       REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_FFT_ENA);
+       REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENABLE);
+
+       if (param->short_repeat)
+               REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
+                           AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);
+       else
+               REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN,
+                           AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);
+
+       /* on AR92xx, the highest bit of count will make the the chip send
+        * spectral samples endlessly. Check if this really was intended,
+        * and fix otherwise.
+        */
+       count = param->count;
+       if (param->endless)
+               count = 0x80;
+       else if (count & 0x80)
+               count = 0x7f;
+
+       REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
+                     AR_PHY_SPECTRAL_SCAN_COUNT, count);
+       REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
+                     AR_PHY_SPECTRAL_SCAN_PERIOD, param->period);
+       REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
+                     AR_PHY_SPECTRAL_SCAN_FFT_PERIOD, param->fft_period);
+
+       return;
+}
+
+static void ar9002_hw_spectral_scan_trigger(struct ath_hw *ah)
+{
+       REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENABLE);
+       /* Activate spectral scan */
+       REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
+                   AR_PHY_SPECTRAL_SCAN_ACTIVE);
+}
+
+static void ar9002_hw_spectral_scan_wait(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       /* Poll for spectral scan complete */
+       if (!ath9k_hw_wait(ah, AR_PHY_SPECTRAL_SCAN,
+                          AR_PHY_SPECTRAL_SCAN_ACTIVE,
+                          0, AH_WAIT_TIMEOUT)) {
+               ath_err(common, "spectral scan wait failed\n");
+               return;
+       }
+}
+
 void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
 {
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
        struct ath_hw_ops *ops = ath9k_hw_ops(ah);
 
        priv_ops->set_rf_regs = NULL;
-       priv_ops->rf_alloc_ext_banks = NULL;
-       priv_ops->rf_free_ext_banks = NULL;
        priv_ops->rf_set_freq = ar9002_hw_set_channel;
        priv_ops->spur_mitigate_freq = ar9002_hw_spur_mitigate;
        priv_ops->olc_init = ar9002_olc_init;
@@ -571,6 +630,9 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
 
        ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get;
        ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set;
+       ops->spectral_scan_config = ar9002_hw_spectral_scan_config;
+       ops->spectral_scan_trigger = ar9002_hw_spectral_scan_trigger;
+       ops->spectral_scan_wait = ar9002_hw_spectral_scan_wait;
 
        ar9002_hw_set_nf_limits(ah);
 }
index 262e1e036fd730fe63f273001aff016ac600e0cf..db5ffada221718f76f6a1dfeabfbb5c6ed2929ef 100644 (file)
@@ -744,6 +744,186 @@ static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = {
        {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
 };
 
+static const u32 ar9300Modes_mixed_ob_db_tx_gain_table_2p2[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x0000a2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352},
+       {0x0000a2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584},
+       {0x0000a2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800},
+       {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+       {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+       {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+       {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+       {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202},
+       {0x0000a514, 0x1c000223, 0x1c000223, 0x11000400, 0x11000400},
+       {0x0000a518, 0x21002220, 0x21002220, 0x15000402, 0x15000402},
+       {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404},
+       {0x0000a520, 0x2b022220, 0x2b022220, 0x1b000603, 0x1b000603},
+       {0x0000a524, 0x2f022222, 0x2f022222, 0x1f000a02, 0x1f000a02},
+       {0x0000a528, 0x34022225, 0x34022225, 0x23000a04, 0x23000a04},
+       {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x26000a20, 0x26000a20},
+       {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2a000e20, 0x2a000e20},
+       {0x0000a534, 0x4202242a, 0x4202242a, 0x2e000e22, 0x2e000e22},
+       {0x0000a538, 0x4702244a, 0x4702244a, 0x31000e24, 0x31000e24},
+       {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x34001640, 0x34001640},
+       {0x0000a540, 0x4e02246c, 0x4e02246c, 0x38001660, 0x38001660},
+       {0x0000a544, 0x52022470, 0x52022470, 0x3b001861, 0x3b001861},
+       {0x0000a548, 0x55022490, 0x55022490, 0x3e001a81, 0x3e001a81},
+       {0x0000a54c, 0x59022492, 0x59022492, 0x42001a83, 0x42001a83},
+       {0x0000a550, 0x5d022692, 0x5d022692, 0x44001c84, 0x44001c84},
+       {0x0000a554, 0x61022892, 0x61022892, 0x48001ce3, 0x48001ce3},
+       {0x0000a558, 0x65024890, 0x65024890, 0x4c001ce5, 0x4c001ce5},
+       {0x0000a55c, 0x69024892, 0x69024892, 0x50001ce9, 0x50001ce9},
+       {0x0000a560, 0x6e024c92, 0x6e024c92, 0x54001ceb, 0x54001ceb},
+       {0x0000a564, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec},
+       {0x0000a568, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec},
+       {0x0000a56c, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec},
+       {0x0000a570, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec},
+       {0x0000a574, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec},
+       {0x0000a578, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec},
+       {0x0000a57c, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec},
+       {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000},
+       {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002},
+       {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004},
+       {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200},
+       {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202},
+       {0x0000a594, 0x1c800223, 0x1c800223, 0x11800400, 0x11800400},
+       {0x0000a598, 0x21802220, 0x21802220, 0x15800402, 0x15800402},
+       {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404},
+       {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1b800603, 0x1b800603},
+       {0x0000a5a4, 0x2f822222, 0x2f822222, 0x1f800a02, 0x1f800a02},
+       {0x0000a5a8, 0x34822225, 0x34822225, 0x23800a04, 0x23800a04},
+       {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x26800a20, 0x26800a20},
+       {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2a800e20, 0x2a800e20},
+       {0x0000a5b4, 0x4282242a, 0x4282242a, 0x2e800e22, 0x2e800e22},
+       {0x0000a5b8, 0x4782244a, 0x4782244a, 0x31800e24, 0x31800e24},
+       {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x34801640, 0x34801640},
+       {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x38801660, 0x38801660},
+       {0x0000a5c4, 0x52822470, 0x52822470, 0x3b801861, 0x3b801861},
+       {0x0000a5c8, 0x55822490, 0x55822490, 0x3e801a81, 0x3e801a81},
+       {0x0000a5cc, 0x59822492, 0x59822492, 0x42801a83, 0x42801a83},
+       {0x0000a5d0, 0x5d822692, 0x5d822692, 0x44801c84, 0x44801c84},
+       {0x0000a5d4, 0x61822892, 0x61822892, 0x48801ce3, 0x48801ce3},
+       {0x0000a5d8, 0x65824890, 0x65824890, 0x4c801ce5, 0x4c801ce5},
+       {0x0000a5dc, 0x69824892, 0x69824892, 0x50801ce9, 0x50801ce9},
+       {0x0000a5e0, 0x6e824c92, 0x6e824c92, 0x54801ceb, 0x54801ceb},
+       {0x0000a5e4, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec},
+       {0x0000a5e8, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec},
+       {0x0000a5ec, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec},
+       {0x0000a5f0, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec},
+       {0x0000a5f4, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec},
+       {0x0000a5f8, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec},
+       {0x0000a5fc, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec},
+       {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a614, 0x02004000, 0x02004000, 0x01404000, 0x01404000},
+       {0x0000a618, 0x02004801, 0x02004801, 0x01404501, 0x01404501},
+       {0x0000a61c, 0x02808a02, 0x02808a02, 0x02008501, 0x02008501},
+       {0x0000a620, 0x0380ce03, 0x0380ce03, 0x0280ca03, 0x0280ca03},
+       {0x0000a624, 0x04411104, 0x04411104, 0x03010c04, 0x03010c04},
+       {0x0000a628, 0x04411104, 0x04411104, 0x04014c04, 0x04014c04},
+       {0x0000a62c, 0x04411104, 0x04411104, 0x04015005, 0x04015005},
+       {0x0000a630, 0x04411104, 0x04411104, 0x04015005, 0x04015005},
+       {0x0000a634, 0x04411104, 0x04411104, 0x04015005, 0x04015005},
+       {0x0000a638, 0x04411104, 0x04411104, 0x04015005, 0x04015005},
+       {0x0000a63c, 0x04411104, 0x04411104, 0x04015005, 0x04015005},
+       {0x0000b2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352},
+       {0x0000b2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584},
+       {0x0000b2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800},
+       {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x0000c2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352},
+       {0x0000c2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584},
+       {0x0000c2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800},
+       {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x00016044, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4},
+       {0x00016048, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001},
+       {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+       {0x00016444, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4},
+       {0x00016448, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001},
+       {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+       {0x00016844, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4},
+       {0x00016848, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001},
+       {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+};
+
+static const u32 ar9300Modes_type5_tx_gain_table_2p2[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x0000a2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+       {0x0000a2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+       {0x0000a2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
+       {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+       {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+       {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+       {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+       {0x0000a510, 0x15000028, 0x15000028, 0x0f000202, 0x0f000202},
+       {0x0000a514, 0x1b00002b, 0x1b00002b, 0x12000400, 0x12000400},
+       {0x0000a518, 0x1f020028, 0x1f020028, 0x16000402, 0x16000402},
+       {0x0000a51c, 0x2502002b, 0x2502002b, 0x19000404, 0x19000404},
+       {0x0000a520, 0x2a04002a, 0x2a04002a, 0x1c000603, 0x1c000603},
+       {0x0000a524, 0x2e06002a, 0x2e06002a, 0x21000a02, 0x21000a02},
+       {0x0000a528, 0x3302202d, 0x3302202d, 0x25000a04, 0x25000a04},
+       {0x0000a52c, 0x3804202c, 0x3804202c, 0x28000a20, 0x28000a20},
+       {0x0000a530, 0x3c06202c, 0x3c06202c, 0x2c000e20, 0x2c000e20},
+       {0x0000a534, 0x4108202d, 0x4108202d, 0x30000e22, 0x30000e22},
+       {0x0000a538, 0x4506402d, 0x4506402d, 0x34000e24, 0x34000e24},
+       {0x0000a53c, 0x4906222d, 0x4906222d, 0x38001640, 0x38001640},
+       {0x0000a540, 0x4d062231, 0x4d062231, 0x3c001660, 0x3c001660},
+       {0x0000a544, 0x50082231, 0x50082231, 0x3f001861, 0x3f001861},
+       {0x0000a548, 0x5608422e, 0x5608422e, 0x43001a81, 0x43001a81},
+       {0x0000a54c, 0x5e08442e, 0x5e08442e, 0x47001a83, 0x47001a83},
+       {0x0000a550, 0x620a4431, 0x620a4431, 0x4a001c84, 0x4a001c84},
+       {0x0000a554, 0x640a4432, 0x640a4432, 0x4e001ce3, 0x4e001ce3},
+       {0x0000a558, 0x680a4434, 0x680a4434, 0x52001ce5, 0x52001ce5},
+       {0x0000a55c, 0x6c0a6434, 0x6c0a6434, 0x56001ce9, 0x56001ce9},
+       {0x0000a560, 0x6f0a6633, 0x6f0a6633, 0x5a001ceb, 0x5a001ceb},
+       {0x0000a564, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a568, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a56c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a570, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a574, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a578, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a57c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a608, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+       {0x0000a60c, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+       {0x0000a610, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+       {0x0000a614, 0x01804601, 0x01804601, 0x01404000, 0x01404000},
+       {0x0000a618, 0x01804601, 0x01804601, 0x01404501, 0x01404501},
+       {0x0000a61c, 0x01804601, 0x01804601, 0x02008501, 0x02008501},
+       {0x0000a620, 0x03408d02, 0x03408d02, 0x0280ca03, 0x0280ca03},
+       {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
+       {0x0000a628, 0x03410d04, 0x03410d04, 0x04014c04, 0x04014c04},
+       {0x0000a62c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+       {0x0000a630, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+       {0x0000a634, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+       {0x0000a638, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+       {0x0000a63c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+       {0x0000b2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+       {0x0000b2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+       {0x0000b2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
+       {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x0000c2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+       {0x0000c2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+       {0x0000c2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
+       {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+       {0x00016048, 0x65240001, 0x65240001, 0x66480001, 0x66480001},
+       {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+       {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+       {0x00016448, 0x65240001, 0x65240001, 0x66480001, 0x66480001},
+       {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+       {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+       {0x00016848, 0x65240001, 0x65240001, 0x66480001, 0x66480001},
+       {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+};
+
 static const u32 ar9300Common_rx_gain_table_2p2[][2] = {
        /* Addr      allmodes  */
        {0x0000a000, 0x00010000},
index 56317b0fb6b692f3f9ae20b8ba681ec18371788f..4cc13940c8950d2a7ad2a3fdf1a092ac3736a7d5 100644 (file)
@@ -32,7 +32,6 @@ struct coeff {
 
 enum ar9003_cal_types {
        IQ_MISMATCH_CAL = BIT(0),
-       TEMP_COMP_CAL = BIT(1),
 };
 
 static void ar9003_hw_setup_calibration(struct ath_hw *ah,
@@ -49,7 +48,7 @@ static void ar9003_hw_setup_calibration(struct ath_hw *ah,
                 */
                REG_RMW_FIELD(ah, AR_PHY_TIMING4,
                              AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX,
-               currCal->calData->calCountMax);
+                             currCal->calData->calCountMax);
                REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
 
                ath_dbg(common, CALIBRATE,
@@ -58,14 +57,8 @@ static void ar9003_hw_setup_calibration(struct ath_hw *ah,
                /* Kick-off cal */
                REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL);
                break;
-       case TEMP_COMP_CAL:
-               REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM,
-                             AR_PHY_65NM_CH0_THERM_LOCAL, 1);
-               REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM,
-                             AR_PHY_65NM_CH0_THERM_START, 1);
-
-               ath_dbg(common, CALIBRATE,
-                       "starting Temperature Compensation Calibration\n");
+       default:
+               ath_err(common, "Invalid calibration type\n");
                break;
        }
 }
@@ -323,6 +316,14 @@ static const struct ath9k_percal_data iq_cal_single_sample = {
 static void ar9003_hw_init_cal_settings(struct ath_hw *ah)
 {
        ah->iq_caldata.calData = &iq_cal_single_sample;
+
+       if (AR_SREV_9300_20_OR_LATER(ah)) {
+               ah->enabled_cals |= TX_IQ_CAL;
+               if (AR_SREV_9485_OR_LATER(ah) && !AR_SREV_9340(ah))
+                       ah->enabled_cals |= TX_IQ_ON_AGC_CAL;
+       }
+
+       ah->supp_cals = IQ_MISMATCH_CAL;
 }
 
 /*
@@ -959,22 +960,68 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g)
                      AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0);
 }
 
+static void ar9003_hw_do_manual_peak_cal(struct ath_hw *ah,
+                                        struct ath9k_channel *chan)
+{
+       int i;
+
+       if (!AR_SREV_9462(ah) && !AR_SREV_9565(ah))
+               return;
+
+       for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+               if (!(ah->rxchainmask & (1 << i)))
+                       continue;
+               ar9003_hw_manual_peak_cal(ah, i, IS_CHAN_2GHZ(chan));
+       }
+}
+
+static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable)
+{
+       u32 cl_idx[AR9300_MAX_CHAINS] = { AR_PHY_CL_TAB_0,
+                                         AR_PHY_CL_TAB_1,
+                                         AR_PHY_CL_TAB_2 };
+       struct ath9k_hw_cal_data *caldata = ah->caldata;
+       bool txclcal_done = false;
+       int i, j;
+
+       if (!caldata || !(ah->enabled_cals & TX_CL_CAL))
+               return;
+
+       txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) &
+                         AR_PHY_AGC_CONTROL_CLC_SUCCESS);
+
+       if (caldata->done_txclcal_once) {
+               for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+                       if (!(ah->txchainmask & (1 << i)))
+                               continue;
+                       for (j = 0; j < MAX_CL_TAB_ENTRY; j++)
+                               REG_WRITE(ah, CL_TAB_ENTRY(cl_idx[i]),
+                                         caldata->tx_clcal[i][j]);
+               }
+       } else if (is_reusable && txclcal_done) {
+               for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+                       if (!(ah->txchainmask & (1 << i)))
+                               continue;
+                       for (j = 0; j < MAX_CL_TAB_ENTRY; j++)
+                               caldata->tx_clcal[i][j] =
+                                       REG_READ(ah, CL_TAB_ENTRY(cl_idx[i]));
+               }
+               caldata->done_txclcal_once = true;
+       }
+}
+
 static bool ar9003_hw_init_cal(struct ath_hw *ah,
                               struct ath9k_channel *chan)
 {
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_hw_cal_data *caldata = ah->caldata;
-       bool txiqcal_done = false, txclcal_done = false;
+       bool txiqcal_done = false;
        bool is_reusable = true, status = true;
-       bool run_rtt_cal = false, run_agc_cal;
+       bool run_rtt_cal = false, run_agc_cal, sep_iq_cal = false;
        bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT);
        u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL |
                                          AR_PHY_AGC_CONTROL_FLTR_CAL   |
                                          AR_PHY_AGC_CONTROL_PKDET_CAL;
-       int i, j;
-       u32 cl_idx[AR9300_MAX_CHAINS] = { AR_PHY_CL_TAB_0,
-                                         AR_PHY_CL_TAB_1,
-                                         AR_PHY_CL_TAB_2 };
 
        ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask);
 
@@ -1014,7 +1061,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
                }
        }
 
-       if (!(ah->enabled_cals & TX_IQ_CAL))
+       if ((IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) ||
+           !(ah->enabled_cals & TX_IQ_CAL))
                goto skip_tx_iqcal;
 
        /* Do Tx IQ Calibration */
@@ -1034,21 +1082,22 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
                        REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
                                    AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
                txiqcal_done = run_agc_cal = true;
-               goto skip_tx_iqcal;
-       } else if (caldata && !caldata->done_txiqcal_once)
+       } else if (caldata && !caldata->done_txiqcal_once) {
                run_agc_cal = true;
+               sep_iq_cal = true;
+       }
 
+skip_tx_iqcal:
        if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal)
                ar9003_mci_init_cal_req(ah, &is_reusable);
 
-       if (!(IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan))) {
+       if (sep_iq_cal) {
                txiqcal_done = ar9003_hw_tx_iq_cal_run(ah);
                REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
                udelay(5);
                REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
        }
 
-skip_tx_iqcal:
        if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) {
                /* Calibrate the AGC */
                REG_WRITE(ah, AR_PHY_AGC_CONTROL,
@@ -1059,14 +1108,8 @@ skip_tx_iqcal:
                status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
                                       AR_PHY_AGC_CONTROL_CAL,
                                       0, AH_WAIT_TIMEOUT);
-               if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
-                       for (i = 0; i < AR9300_MAX_CHAINS; i++) {
-                               if (!(ah->rxchainmask & (1 << i)))
-                                       continue;
-                               ar9003_hw_manual_peak_cal(ah, i,
-                                                         IS_CHAN_2GHZ(chan));
-                       }
-               }
+
+               ar9003_hw_do_manual_peak_cal(ah, chan);
        }
 
        if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal)
@@ -1091,31 +1134,7 @@ skip_tx_iqcal:
        else if (caldata && caldata->done_txiqcal_once)
                ar9003_hw_tx_iq_cal_reload(ah);
 
-#define CL_TAB_ENTRY(reg_base) (reg_base + (4 * j))
-       if (caldata && (ah->enabled_cals & TX_CL_CAL)) {
-               txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) &
-                                          AR_PHY_AGC_CONTROL_CLC_SUCCESS);
-               if (caldata->done_txclcal_once) {
-                       for (i = 0; i < AR9300_MAX_CHAINS; i++) {
-                               if (!(ah->txchainmask & (1 << i)))
-                                       continue;
-                               for (j = 0; j < MAX_CL_TAB_ENTRY; j++)
-                                       REG_WRITE(ah, CL_TAB_ENTRY(cl_idx[i]),
-                                                 caldata->tx_clcal[i][j]);
-                       }
-               } else if (is_reusable && txclcal_done) {
-                       for (i = 0; i < AR9300_MAX_CHAINS; i++) {
-                               if (!(ah->txchainmask & (1 << i)))
-                                       continue;
-                               for (j = 0; j < MAX_CL_TAB_ENTRY; j++)
-                                       caldata->tx_clcal[i][j] =
-                                               REG_READ(ah,
-                                                 CL_TAB_ENTRY(cl_idx[i]));
-                       }
-                       caldata->done_txclcal_once = true;
-               }
-       }
-#undef CL_TAB_ENTRY
+       ar9003_hw_cl_cal_post_proc(ah, is_reusable);
 
        if (run_rtt_cal && caldata) {
                if (is_reusable) {
@@ -1133,20 +1152,10 @@ skip_tx_iqcal:
 
        /* Initialize list pointers */
        ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
-       ah->supp_cals = IQ_MISMATCH_CAL;
-
-       if (ah->supp_cals & IQ_MISMATCH_CAL) {
-               INIT_CAL(&ah->iq_caldata);
-               INSERT_CAL(ah, &ah->iq_caldata);
-               ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n");
-       }
 
-       if (ah->supp_cals & TEMP_COMP_CAL) {
-               INIT_CAL(&ah->tempCompCalData);
-               INSERT_CAL(ah, &ah->tempCompCalData);
-               ath_dbg(common, CALIBRATE,
-                       "enabling Temperature Compensation Calibration\n");
-       }
+       INIT_CAL(&ah->iq_caldata);
+       INSERT_CAL(ah, &ah->iq_caldata);
+       ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n");
 
        /* Initialize current pointer to first element in list */
        ah->cal_list_curr = ah->cal_list;
index 562186ca9b5248a26c7cb957357d80f0d5f162fb..881e989ea4701b0dc98c3229e196b5b2b2a5b70f 100644 (file)
@@ -4586,14 +4586,14 @@ static int ar9003_hw_cal_pier_get(struct ath_hw *ah,
        return 0;
 }
 
-static int ar9003_hw_power_control_override(struct ath_hw *ah,
-                                           int frequency,
-                                           int *correction,
-                                           int *voltage, int *temperature)
+static void ar9003_hw_power_control_override(struct ath_hw *ah,
+                                            int frequency,
+                                            int *correction,
+                                            int *voltage, int *temperature)
 {
-       int tempSlope = 0;
+       int temp_slope = 0, temp_slope1 = 0, temp_slope2 = 0;
        struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
-       int f[8], t[8], i;
+       int f[8], t[8], t1[3], t2[3], i;
 
        REG_RMW(ah, AR_PHY_TPC_11_B0,
                (correction[0] << AR_PHY_TPC_OLPC_GAIN_DELTA_S),
@@ -4624,38 +4624,108 @@ static int ar9003_hw_power_control_override(struct ath_hw *ah,
         * enable temperature compensation
         * Need to use register names
         */
-       if (frequency < 4000)
-               tempSlope = eep->modalHeader2G.tempSlope;
-       else if ((eep->baseEepHeader.miscConfiguration & 0x20) != 0) {
-               for (i = 0; i < 8; i++) {
-                       t[i] = eep->base_ext1.tempslopextension[i];
-                       f[i] = FBIN2FREQ(eep->calFreqPier5G[i], 0);
+       if (frequency < 4000) {
+               temp_slope = eep->modalHeader2G.tempSlope;
+       } else {
+               if (AR_SREV_9550(ah)) {
+                       t[0] = eep->base_ext1.tempslopextension[2];
+                       t1[0] = eep->base_ext1.tempslopextension[3];
+                       t2[0] = eep->base_ext1.tempslopextension[4];
+                       f[0] = 5180;
+
+                       t[1] = eep->modalHeader5G.tempSlope;
+                       t1[1] = eep->base_ext1.tempslopextension[0];
+                       t2[1] = eep->base_ext1.tempslopextension[1];
+                       f[1] = 5500;
+
+                       t[2] = eep->base_ext1.tempslopextension[5];
+                       t1[2] = eep->base_ext1.tempslopextension[6];
+                       t2[2] = eep->base_ext1.tempslopextension[7];
+                       f[2] = 5785;
+
+                       temp_slope = ar9003_hw_power_interpolate(frequency,
+                                                                f, t, 3);
+                       temp_slope1 = ar9003_hw_power_interpolate(frequency,
+                                                                  f, t1, 3);
+                       temp_slope2 = ar9003_hw_power_interpolate(frequency,
+                                                                  f, t2, 3);
+
+                       goto tempslope;
                }
-               tempSlope = ar9003_hw_power_interpolate((s32) frequency,
-                                                       f, t, 8);
-       } else if (eep->base_ext2.tempSlopeLow != 0) {
-               t[0] = eep->base_ext2.tempSlopeLow;
-               f[0] = 5180;
-               t[1] = eep->modalHeader5G.tempSlope;
-               f[1] = 5500;
-               t[2] = eep->base_ext2.tempSlopeHigh;
-               f[2] = 5785;
-               tempSlope = ar9003_hw_power_interpolate((s32) frequency,
-                                                       f, t, 3);
-       } else
-               tempSlope = eep->modalHeader5G.tempSlope;
 
-       REG_RMW_FIELD(ah, AR_PHY_TPC_19, AR_PHY_TPC_19_ALPHA_THERM, tempSlope);
+               if ((eep->baseEepHeader.miscConfiguration & 0x20) != 0) {
+                       for (i = 0; i < 8; i++) {
+                               t[i] = eep->base_ext1.tempslopextension[i];
+                               f[i] = FBIN2FREQ(eep->calFreqPier5G[i], 0);
+                       }
+                       temp_slope = ar9003_hw_power_interpolate((s32) frequency,
+                                                                f, t, 8);
+               } else if (eep->base_ext2.tempSlopeLow != 0) {
+                       t[0] = eep->base_ext2.tempSlopeLow;
+                       f[0] = 5180;
+                       t[1] = eep->modalHeader5G.tempSlope;
+                       f[1] = 5500;
+                       t[2] = eep->base_ext2.tempSlopeHigh;
+                       f[2] = 5785;
+                       temp_slope = ar9003_hw_power_interpolate((s32) frequency,
+                                                                f, t, 3);
+               } else {
+                       temp_slope = eep->modalHeader5G.tempSlope;
+               }
+       }
+
+tempslope:
+       if (AR_SREV_9550(ah)) {
+               /*
+                * AR955x has tempSlope register for each chain.
+                * Check whether temp_compensation feature is enabled or not.
+                */
+               if (eep->baseEepHeader.featureEnable & 0x1) {
+                       if (frequency < 4000) {
+                               REG_RMW_FIELD(ah, AR_PHY_TPC_19,
+                                             AR_PHY_TPC_19_ALPHA_THERM,
+                                             eep->base_ext2.tempSlopeLow);
+                               REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
+                                             AR_PHY_TPC_19_ALPHA_THERM,
+                                             temp_slope);
+                               REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2,
+                                             AR_PHY_TPC_19_ALPHA_THERM,
+                                             eep->base_ext2.tempSlopeHigh);
+                       } else {
+                               REG_RMW_FIELD(ah, AR_PHY_TPC_19,
+                                             AR_PHY_TPC_19_ALPHA_THERM,
+                                             temp_slope);
+                               REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
+                                             AR_PHY_TPC_19_ALPHA_THERM,
+                                             temp_slope1);
+                               REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2,
+                                             AR_PHY_TPC_19_ALPHA_THERM,
+                                             temp_slope2);
+                       }
+               } else {
+                       /*
+                        * If temp compensation is not enabled,
+                        * set all registers to 0.
+                        */
+                       REG_RMW_FIELD(ah, AR_PHY_TPC_19,
+                                     AR_PHY_TPC_19_ALPHA_THERM, 0);
+                       REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
+                                     AR_PHY_TPC_19_ALPHA_THERM, 0);
+                       REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2,
+                                     AR_PHY_TPC_19_ALPHA_THERM, 0);
+               }
+       } else {
+               REG_RMW_FIELD(ah, AR_PHY_TPC_19,
+                             AR_PHY_TPC_19_ALPHA_THERM, temp_slope);
+       }
 
        if (AR_SREV_9462_20(ah))
                REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
-                             AR_PHY_TPC_19_B1_ALPHA_THERM, tempSlope);
+                             AR_PHY_TPC_19_B1_ALPHA_THERM, temp_slope);
 
 
        REG_RMW_FIELD(ah, AR_PHY_TPC_18, AR_PHY_TPC_18_THERM_CAL_VALUE,
                      temperature[0]);
-
-       return 0;
 }
 
 /* Apply the recorded correction values. */
index 59bf5f31e212c07bfef14251384aa3f5d2de0c8b..a3523c969a3ace778b3daa4aabd2ac2d85761390 100644 (file)
@@ -507,28 +507,59 @@ static void ar9003_tx_gain_table_mode4(struct ath_hw *ah)
        else if (AR_SREV_9580(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9580_1p0_mixed_ob_db_tx_gain_table);
+       else
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                       ar9300Modes_mixed_ob_db_tx_gain_table_2p2);
+}
+
+static void ar9003_tx_gain_table_mode5(struct ath_hw *ah)
+{
+       if (AR_SREV_9485_11(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                       ar9485Modes_green_ob_db_tx_gain_1_1);
+       else if (AR_SREV_9340(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                       ar9340Modes_ub124_tx_gain_table_1p0);
+       else if (AR_SREV_9580(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                       ar9580_1p0_type5_tx_gain_table);
+       else if (AR_SREV_9300_22(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                       ar9300Modes_type5_tx_gain_table_2p2);
 }
 
+static void ar9003_tx_gain_table_mode6(struct ath_hw *ah)
+{
+       if (AR_SREV_9340(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                       ar9340Modes_low_ob_db_and_spur_tx_gain_table_1p0);
+       else if (AR_SREV_9485_11(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                       ar9485Modes_green_spur_ob_db_tx_gain_1_1);
+       else if (AR_SREV_9580(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                       ar9580_1p0_type6_tx_gain_table);
+}
+
+typedef void (*ath_txgain_tab)(struct ath_hw *ah);
+
 static void ar9003_tx_gain_table_apply(struct ath_hw *ah)
 {
-       switch (ar9003_hw_get_tx_gain_idx(ah)) {
-       case 0:
-       default:
-               ar9003_tx_gain_table_mode0(ah);
-               break;
-       case 1:
-               ar9003_tx_gain_table_mode1(ah);
-               break;
-       case 2:
-               ar9003_tx_gain_table_mode2(ah);
-               break;
-       case 3:
-               ar9003_tx_gain_table_mode3(ah);
-               break;
-       case 4:
-               ar9003_tx_gain_table_mode4(ah);
-               break;
-       }
+       static const ath_txgain_tab modes[] = {
+               ar9003_tx_gain_table_mode0,
+               ar9003_tx_gain_table_mode1,
+               ar9003_tx_gain_table_mode2,
+               ar9003_tx_gain_table_mode3,
+               ar9003_tx_gain_table_mode4,
+               ar9003_tx_gain_table_mode5,
+               ar9003_tx_gain_table_mode6,
+       };
+       int idx = ar9003_hw_get_tx_gain_idx(ah);
+
+       if (idx >= ARRAY_SIZE(modes))
+               idx = 0;
+
+       modes[idx](ah);
 }
 
 static void ar9003_rx_gain_table_mode0(struct ath_hw *ah)
@@ -673,7 +704,7 @@ void ar9003_hw_attach_ops(struct ath_hw *ah)
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
        struct ath_hw_ops *ops = ath9k_hw_ops(ah);
 
-       priv_ops->init_mode_regs = ar9003_hw_init_mode_regs;
+       ar9003_hw_init_mode_regs(ah);
        priv_ops->init_mode_gain_regs = ar9003_hw_init_mode_gain_regs;
 
        ops->config_pci_powersave = ar9003_hw_configpcipowersave;
index 3afc24bde6d65d88a0d469abb0ef73d93856edcb..2bf6548dd143c420f1a5d7372040b5638dd8ad38 100644 (file)
@@ -68,7 +68,7 @@ static const int m2ThreshExt_off = 127;
 static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
 {
        u16 bMode, fracMode = 0, aModeRefSel = 0;
-       u32 freq, channelSel = 0, reg32 = 0;
+       u32 freq, chan_frac, div, channelSel = 0, reg32 = 0;
        struct chan_centers centers;
        int loadSynthChannel;
 
@@ -77,9 +77,6 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
 
        if (freq < 4800) {     /* 2 GHz, fractional mode */
                if (AR_SREV_9330(ah)) {
-                       u32 chan_frac;
-                       u32 div;
-
                        if (ah->is_clk_25mhz)
                                div = 75;
                        else
@@ -89,34 +86,40 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
                        chan_frac = (((freq * 4) % div) * 0x20000) / div;
                        channelSel = (channelSel << 17) | chan_frac;
                } else if (AR_SREV_9485(ah) || AR_SREV_9565(ah)) {
-                       u32 chan_frac;
-
                        /*
-                        * freq_ref = 40 / (refdiva >> amoderefsel); where refdiva=1 and amoderefsel=0
+                        * freq_ref = 40 / (refdiva >> amoderefsel);
+                        * where refdiva=1 and amoderefsel=0
                         * ndiv = ((chan_mhz * 4) / 3) / freq_ref;
                         * chansel = int(ndiv), chanfrac = (ndiv - chansel) * 0x20000
                         */
                        channelSel = (freq * 4) / 120;
                        chan_frac = (((freq * 4) % 120) * 0x20000) / 120;
                        channelSel = (channelSel << 17) | chan_frac;
-               } else if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) {
+               } else if (AR_SREV_9340(ah)) {
                        if (ah->is_clk_25mhz) {
-                               u32 chan_frac;
-
                                channelSel = (freq * 2) / 75;
                                chan_frac = (((freq * 2) % 75) * 0x20000) / 75;
                                channelSel = (channelSel << 17) | chan_frac;
-                       } else
+                       } else {
                                channelSel = CHANSEL_2G(freq) >> 1;
-               } else
+                       }
+               } else if (AR_SREV_9550(ah)) {
+                       if (ah->is_clk_25mhz)
+                               div = 75;
+                       else
+                               div = 120;
+
+                       channelSel = (freq * 4) / div;
+                       chan_frac = (((freq * 4) % div) * 0x20000) / div;
+                       channelSel = (channelSel << 17) | chan_frac;
+               } else {
                        channelSel = CHANSEL_2G(freq);
+               }
                /* Set to 2G mode */
                bMode = 1;
        } else {
                if ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) &&
                    ah->is_clk_25mhz) {
-                       u32 chan_frac;
-
                        channelSel = freq / 75;
                        chan_frac = ((freq % 75) * 0x20000) / 75;
                        channelSel = (channelSel << 17) | chan_frac;
@@ -1437,6 +1440,67 @@ set_rfmode:
        return 0;
 }
 
+static void ar9003_hw_spectral_scan_config(struct ath_hw *ah,
+                                          struct ath_spec_scan *param)
+{
+       u8 count;
+
+       if (!param->enabled) {
+               REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN,
+                           AR_PHY_SPECTRAL_SCAN_ENABLE);
+               return;
+       }
+
+       REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_FFT_ENA);
+       REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENABLE);
+
+       /* on AR93xx and newer, count = 0 will make the the chip send
+        * spectral samples endlessly. Check if this really was intended,
+        * and fix otherwise.
+        */
+       count = param->count;
+       if (param->endless)
+               count = 0;
+       else if (param->count == 0)
+               count = 1;
+
+       if (param->short_repeat)
+               REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
+                           AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);
+       else
+               REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN,
+                           AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);
+
+       REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
+                     AR_PHY_SPECTRAL_SCAN_COUNT, count);
+       REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
+                     AR_PHY_SPECTRAL_SCAN_PERIOD, param->period);
+       REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
+                     AR_PHY_SPECTRAL_SCAN_FFT_PERIOD, param->fft_period);
+
+       return;
+}
+
+static void ar9003_hw_spectral_scan_trigger(struct ath_hw *ah)
+{
+       /* Activate spectral scan */
+       REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
+                   AR_PHY_SPECTRAL_SCAN_ACTIVE);
+}
+
+static void ar9003_hw_spectral_scan_wait(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       /* Poll for spectral scan complete */
+       if (!ath9k_hw_wait(ah, AR_PHY_SPECTRAL_SCAN,
+                          AR_PHY_SPECTRAL_SCAN_ACTIVE,
+                          0, AH_WAIT_TIMEOUT)) {
+               ath_err(common, "spectral scan wait failed\n");
+               return;
+       }
+}
+
 void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 {
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
@@ -1470,6 +1534,9 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
        ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
        ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
        ops->antctrl_shared_chain_lnadiv = ar9003_hw_antctrl_shared_chain_lnadiv;
+       ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
+       ops->spectral_scan_trigger = ar9003_hw_spectral_scan_trigger;
+       ops->spectral_scan_wait = ar9003_hw_spectral_scan_wait;
 
        ar9003_hw_set_nf_limits(ah);
        ar9003_hw_set_radar_conf(ah);
index 10795629848871cbd2e34df016102fd9cfe920f2..e71774196c01bbf7fa6f3a33b1789556fc63ae9a 100644 (file)
 #define AR_PHY_TPC_5_B2          (AR_SM2_BASE + 0x208)
 #define AR_PHY_TPC_6_B2          (AR_SM2_BASE + 0x20c)
 #define AR_PHY_TPC_11_B2         (AR_SM2_BASE + 0x220)
-#define AR_PHY_PDADC_TAB_2       (AR_SM2_BASE + 0x240)
+#define AR_PHY_TPC_19_B2         (AR_SM2_BASE + 0x240)
 #define AR_PHY_TX_IQCAL_STATUS_B2   (AR_SM2_BASE + 0x48c)
 #define AR_PHY_TX_IQCAL_CORR_COEFF_B2(_i)    (AR_SM2_BASE + 0x450 + ((_i) << 2))
 
index f69d292bdc027403aa7400bce554899a41462a0b..25db9215985aa1519e0901cb698b312811d6dc91 100644 (file)
@@ -1172,6 +1172,106 @@ static const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = {
        {0x00016448, 0x24925666, 0x24925666, 0x8e481266, 0x8e481266},
 };
 
+static const u32 ar9340Modes_low_ob_db_and_spur_tx_gain_table_1p0[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03eaac5a, 0x03eaac5a},
+       {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03f330ac, 0x03f330ac},
+       {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03fc3f00, 0x03fc3f00},
+       {0x0000a2e8, 0x00000000, 0x00000000, 0x03ffc000, 0x03ffc000},
+       {0x0000a394, 0x00000444, 0x00000444, 0x00000404, 0x00000404},
+       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+       {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a504, 0x06000003, 0x06000003, 0x02000001, 0x02000001},
+       {0x0000a508, 0x0a000020, 0x0a000020, 0x05000003, 0x05000003},
+       {0x0000a50c, 0x10000023, 0x10000023, 0x0a000005, 0x0a000005},
+       {0x0000a510, 0x16000220, 0x16000220, 0x0e000201, 0x0e000201},
+       {0x0000a514, 0x1c000223, 0x1c000223, 0x11000203, 0x11000203},
+       {0x0000a518, 0x21002220, 0x21002220, 0x14000401, 0x14000401},
+       {0x0000a51c, 0x27002223, 0x27002223, 0x18000403, 0x18000403},
+       {0x0000a520, 0x2b022220, 0x2b022220, 0x1b000602, 0x1b000602},
+       {0x0000a524, 0x2f022222, 0x2f022222, 0x1f000802, 0x1f000802},
+       {0x0000a528, 0x34022225, 0x34022225, 0x21000620, 0x21000620},
+       {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x25000820, 0x25000820},
+       {0x0000a530, 0x3e02222c, 0x3e02222c, 0x29000822, 0x29000822},
+       {0x0000a534, 0x4202242a, 0x4202242a, 0x2d000824, 0x2d000824},
+       {0x0000a538, 0x4702244a, 0x4702244a, 0x30000828, 0x30000828},
+       {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x3400082a, 0x3400082a},
+       {0x0000a540, 0x4e02246c, 0x4e02246c, 0x38000849, 0x38000849},
+       {0x0000a544, 0x5302266c, 0x5302266c, 0x3b000a2c, 0x3b000a2c},
+       {0x0000a548, 0x5702286c, 0x5702286c, 0x3e000e2b, 0x3e000e2b},
+       {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x42000e2d, 0x42000e2d},
+       {0x0000a550, 0x61024a6c, 0x61024a6c, 0x4500124a, 0x4500124a},
+       {0x0000a554, 0x66026a6c, 0x66026a6c, 0x4900124c, 0x4900124c},
+       {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x4c00126c, 0x4c00126c},
+       {0x0000a55c, 0x7002708c, 0x7002708c, 0x4f00128c, 0x4f00128c},
+       {0x0000a560, 0x7302b08a, 0x7302b08a, 0x52001290, 0x52001290},
+       {0x0000a564, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292},
+       {0x0000a568, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292},
+       {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292},
+       {0x0000a570, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292},
+       {0x0000a574, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292},
+       {0x0000a578, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292},
+       {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292},
+       {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000},
+       {0x0000a584, 0x06800003, 0x06800003, 0x02800001, 0x02800001},
+       {0x0000a588, 0x0a800020, 0x0a800020, 0x05800003, 0x05800003},
+       {0x0000a58c, 0x10800023, 0x10800023, 0x0a800005, 0x0a800005},
+       {0x0000a590, 0x16800220, 0x16800220, 0x0e800201, 0x0e800201},
+       {0x0000a594, 0x1c800223, 0x1c800223, 0x11800203, 0x11800203},
+       {0x0000a598, 0x21820220, 0x21820220, 0x14800401, 0x14800401},
+       {0x0000a59c, 0x27820223, 0x27820223, 0x18800403, 0x18800403},
+       {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1b800602, 0x1b800602},
+       {0x0000a5a4, 0x2f822222, 0x2f822222, 0x1f800802, 0x1f800802},
+       {0x0000a5a8, 0x34822225, 0x34822225, 0x21800620, 0x21800620},
+       {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x25800820, 0x25800820},
+       {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x29800822, 0x29800822},
+       {0x0000a5b4, 0x4282242a, 0x4282242a, 0x2d800824, 0x2d800824},
+       {0x0000a5b8, 0x4782244a, 0x4782244a, 0x30800828, 0x30800828},
+       {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x3480082a, 0x3480082a},
+       {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x38800849, 0x38800849},
+       {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3b800a2c, 0x3b800a2c},
+       {0x0000a5c8, 0x5782286c, 0x5782286c, 0x3e800e2b, 0x3e800e2b},
+       {0x0000a5cc, 0x5c84286b, 0x5c84286b, 0x42800e2d, 0x42800e2d},
+       {0x0000a5d0, 0x61842a6c, 0x61842a6c, 0x4580124a, 0x4580124a},
+       {0x0000a5d4, 0x66862a6c, 0x66862a6c, 0x4980124c, 0x4980124c},
+       {0x0000a5d8, 0x6b862e6c, 0x6b862e6c, 0x4c80126c, 0x4c80126c},
+       {0x0000a5dc, 0x7086308c, 0x7086308c, 0x4f80128c, 0x4f80128c},
+       {0x0000a5e0, 0x738a308a, 0x738a308a, 0x52801290, 0x52801290},
+       {0x0000a5e4, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292},
+       {0x0000a5e8, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292},
+       {0x0000a5ec, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292},
+       {0x0000a5f0, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292},
+       {0x0000a5f4, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292},
+       {0x0000a5f8, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292},
+       {0x0000a5fc, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292},
+       {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a614, 0x01404000, 0x01404000, 0x01404501, 0x01404501},
+       {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501},
+       {0x0000a61c, 0x02008802, 0x02008802, 0x01404501, 0x01404501},
+       {0x0000a620, 0x0300cc03, 0x0300cc03, 0x03c0cf02, 0x03c0cf02},
+       {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03c0cf03, 0x03c0cf03},
+       {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04011004, 0x04011004},
+       {0x0000a62c, 0x03810c03, 0x03810c03, 0x05419405, 0x05419405},
+       {0x0000a630, 0x03810e04, 0x03810e04, 0x05419506, 0x05419506},
+       {0x0000a634, 0x03810e04, 0x03810e04, 0x05419506, 0x05419506},
+       {0x0000a638, 0x03810e04, 0x03810e04, 0x05419506, 0x05419506},
+       {0x0000a63c, 0x03810e04, 0x03810e04, 0x05419506, 0x05419506},
+       {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03eaac5a, 0x03eaac5a},
+       {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03f330ac, 0x03f330ac},
+       {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03fc3f00, 0x03fc3f00},
+       {0x0000b2e8, 0x00000000, 0x00000000, 0x03ffc000, 0x03ffc000},
+       {0x00016044, 0x022492db, 0x022492db, 0x022492db, 0x022492db},
+       {0x00016048, 0x24925666, 0x24925666, 0x24925266, 0x24925266},
+       {0x00016280, 0x01000015, 0x01000015, 0x01001015, 0x01001015},
+       {0x00016288, 0xf0318000, 0xf0318000, 0xf0318000, 0xf0318000},
+       {0x00016444, 0x022492db, 0x022492db, 0x022492db, 0x022492db},
+       {0x00016448, 0x24925666, 0x24925666, 0x24925266, 0x24925266},
+};
+
 static const u32 ar9340_1p0_mac_core[][2] = {
        /* Addr      allmodes  */
        {0x00000008, 0x00000000},
index a3710f3bb90c5b0fe8a02cc26455ec31b3697162..712f415b8c0861165ab2a2ee74cbea853788295b 100644 (file)
@@ -260,6 +260,79 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = {
        {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260},
 };
 
+static const u32 ar9485Modes_green_ob_db_tx_gain_1_1[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003},
+       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8},
+       {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000},
+       {0x0000a500, 0x00022200, 0x00022200, 0x00000006, 0x00000006},
+       {0x0000a504, 0x05062002, 0x05062002, 0x03000201, 0x03000201},
+       {0x0000a508, 0x0c002e00, 0x0c002e00, 0x06000203, 0x06000203},
+       {0x0000a50c, 0x11062202, 0x11062202, 0x0a000401, 0x0a000401},
+       {0x0000a510, 0x17022e00, 0x17022e00, 0x0e000403, 0x0e000403},
+       {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x12000405, 0x12000405},
+       {0x0000a518, 0x25020ec0, 0x25020ec0, 0x15000604, 0x15000604},
+       {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x18000605, 0x18000605},
+       {0x0000a520, 0x2f001f04, 0x2f001f04, 0x1c000a04, 0x1c000a04},
+       {0x0000a524, 0x35001fc4, 0x35001fc4, 0x21000a06, 0x21000a06},
+       {0x0000a528, 0x3c022f04, 0x3c022f04, 0x29000a24, 0x29000a24},
+       {0x0000a52c, 0x41023e85, 0x41023e85, 0x2f000e21, 0x2f000e21},
+       {0x0000a530, 0x48023ec6, 0x48023ec6, 0x31000e20, 0x31000e20},
+       {0x0000a534, 0x4d023f01, 0x4d023f01, 0x33000e20, 0x33000e20},
+       {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62},
+       {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63},
+       {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65},
+       {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66},
+       {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645},
+       {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865},
+       {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86},
+       {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9},
+       {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb},
+       {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb},
+       {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb},
+       {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb},
+       {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
+       {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
+       {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
+       {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
+       {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
+       {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
+       {0x0000b500, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b504, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b508, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b50c, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b510, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b514, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b518, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b51c, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b520, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b524, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b528, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b52c, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a},
+       {0x0000b530, 0x0000003a, 0x0000003a, 0x0000003a, 0x0000003a},
+       {0x0000b534, 0x0000004a, 0x0000004a, 0x0000004a, 0x0000004a},
+       {0x0000b538, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b53c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b540, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b544, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b548, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b54c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b550, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b554, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b558, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b55c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b560, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b564, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b568, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b56c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b570, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b574, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b578, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b57c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db},
+       {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260},
+};
+
 static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = {
        /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002},
@@ -450,6 +523,79 @@ static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = {
 
 #define ar9485_modes_lowest_ob_db_tx_gain_1_1 ar9485Modes_low_ob_db_tx_gain_1_1
 
+static const u32 ar9485Modes_green_spur_ob_db_tx_gain_1_1[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003},
+       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8},
+       {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000},
+       {0x0000a500, 0x00022200, 0x00022200, 0x00000006, 0x00000006},
+       {0x0000a504, 0x05062002, 0x05062002, 0x03000201, 0x03000201},
+       {0x0000a508, 0x0c002e00, 0x0c002e00, 0x07000203, 0x07000203},
+       {0x0000a50c, 0x11062202, 0x11062202, 0x0a000401, 0x0a000401},
+       {0x0000a510, 0x17022e00, 0x17022e00, 0x0e000403, 0x0e000403},
+       {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x12000405, 0x12000405},
+       {0x0000a518, 0x25020ec0, 0x25020ec0, 0x14000406, 0x14000406},
+       {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1800040a, 0x1800040a},
+       {0x0000a520, 0x2f001f04, 0x2f001f04, 0x1c000460, 0x1c000460},
+       {0x0000a524, 0x35001fc4, 0x35001fc4, 0x22000463, 0x22000463},
+       {0x0000a528, 0x3c022f04, 0x3c022f04, 0x26000465, 0x26000465},
+       {0x0000a52c, 0x41023e85, 0x41023e85, 0x2e0006e0, 0x2e0006e0},
+       {0x0000a530, 0x48023ec6, 0x48023ec6, 0x310006e0, 0x310006e0},
+       {0x0000a534, 0x4d023f01, 0x4d023f01, 0x330006e0, 0x330006e0},
+       {0x0000a538, 0x53023f4b, 0x53023f4b, 0x3e0008e3, 0x3e0008e3},
+       {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x410008e5, 0x410008e5},
+       {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x430008e6, 0x430008e6},
+       {0x0000a544, 0x6502feca, 0x6502feca, 0x4a0008ec, 0x4a0008ec},
+       {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4e0008f1, 0x4e0008f1},
+       {0x0000a54c, 0x7203feca, 0x7203feca, 0x520008f3, 0x520008f3},
+       {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x54000eed, 0x54000eed},
+       {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x58000ef1, 0x58000ef1},
+       {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5c000ef3, 0x5c000ef3},
+       {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x60000ef5, 0x60000ef5},
+       {0x0000a560, 0x900fff0b, 0x900fff0b, 0x62000ef6, 0x62000ef6},
+       {0x0000a564, 0x960fffcb, 0x960fffcb, 0x62000ef6, 0x62000ef6},
+       {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
+       {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
+       {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
+       {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
+       {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
+       {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
+       {0x0000b500, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b504, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b508, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b50c, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b510, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b514, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b518, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b51c, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b520, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b524, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b528, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
+       {0x0000b52c, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a},
+       {0x0000b530, 0x0000003a, 0x0000003a, 0x0000003a, 0x0000003a},
+       {0x0000b534, 0x0000004a, 0x0000004a, 0x0000004a, 0x0000004a},
+       {0x0000b538, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b53c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b540, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b544, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b548, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b54c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b550, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b554, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b558, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b55c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b560, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b564, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b568, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b56c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b570, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b574, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b578, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x0000b57c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b},
+       {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db},
+       {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260},
+};
+
 static const u32 ar9485_1_1[][2] = {
        /* Addr      allmodes  */
        {0x0000a580, 0x00000000},
index df97f21c52dc1d9a895e4b3a4ce466861f03b792..ccc5b6c99add76617b4b990e106bd57be0d1f09f 100644 (file)
 static const u32 ar955x_1p0_radio_postamble[][5] = {
        /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x00016098, 0xd2dd5554, 0xd2dd5554, 0xd28b3330, 0xd28b3330},
-       {0x0001609c, 0x0a566f3a, 0x0a566f3a, 0x06345f2a, 0x06345f2a},
-       {0x000160ac, 0xa4647c00, 0xa4647c00, 0xa4646800, 0xa4646800},
-       {0x000160b0, 0x01885f52, 0x01885f52, 0x04accf3a, 0x04accf3a},
-       {0x00016104, 0xb7a00001, 0xb7a00001, 0xb7a00001, 0xb7a00001},
+       {0x0001609c, 0x0a566f3a, 0x0a566f3a, 0x0a566f3a, 0x0a566f3a},
+       {0x000160ac, 0xa4647c00, 0xa4647c00, 0x24647c00, 0x24647c00},
+       {0x000160b0, 0x01885f52, 0x01885f52, 0x01885f52, 0x01885f52},
+       {0x00016104, 0xb7a00000, 0xb7a00000, 0xb7a00001, 0xb7a00001},
        {0x0001610c, 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000},
        {0x00016140, 0x10804008, 0x10804008, 0x10804008, 0x10804008},
-       {0x00016504, 0xb7a00001, 0xb7a00001, 0xb7a00001, 0xb7a00001},
+       {0x00016504, 0xb7a00000, 0xb7a00000, 0xb7a00001, 0xb7a00001},
        {0x0001650c, 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000},
        {0x00016540, 0x10804008, 0x10804008, 0x10804008, 0x10804008},
-       {0x00016904, 0xb7a00001, 0xb7a00001, 0xb7a00001, 0xb7a00001},
+       {0x00016904, 0xb7a00000, 0xb7a00000, 0xb7a00001, 0xb7a00001},
        {0x0001690c, 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000},
        {0x00016940, 0x10804008, 0x10804008, 0x10804008, 0x10804008},
 };
@@ -69,15 +69,15 @@ static const u32 ar955x_1p0_baseband_postamble[][5] = {
        {0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x005c0ec4, 0x005c0ec0},
        {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
        {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f},
-       {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
+       {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b},
        {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff},
        {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018},
        {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
        {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
        {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
-       {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
+       {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01010e0e, 0x01010e0e},
        {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501},
-       {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+       {0x0000a264, 0x00000e0e, 0x00000e0e, 0x01000e0e, 0x01000e0e},
        {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
        {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
        {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
@@ -125,7 +125,7 @@ static const u32 ar955x_1p0_radio_core[][2] = {
        {0x00016094, 0x00000000},
        {0x000160a0, 0x0a108ffe},
        {0x000160a4, 0x812fc370},
-       {0x000160a8, 0x423c8000},
+       {0x000160a8, 0x423c8100},
        {0x000160b4, 0x92480080},
        {0x000160c0, 0x006db6d0},
        {0x000160c4, 0x6db6db60},
@@ -134,7 +134,7 @@ static const u32 ar955x_1p0_radio_core[][2] = {
        {0x00016100, 0x11999601},
        {0x00016108, 0x00080010},
        {0x00016144, 0x02084080},
-       {0x00016148, 0x000080c0},
+       {0x00016148, 0x00008040},
        {0x00016280, 0x01800804},
        {0x00016284, 0x00038dc5},
        {0x00016288, 0x00000000},
@@ -178,7 +178,7 @@ static const u32 ar955x_1p0_radio_core[][2] = {
        {0x00016500, 0x11999601},
        {0x00016508, 0x00080010},
        {0x00016544, 0x02084080},
-       {0x00016548, 0x000080c0},
+       {0x00016548, 0x00008040},
        {0x00016780, 0x00000000},
        {0x00016784, 0x00000000},
        {0x00016788, 0x00400705},
@@ -218,7 +218,7 @@ static const u32 ar955x_1p0_radio_core[][2] = {
        {0x00016900, 0x11999601},
        {0x00016908, 0x00080010},
        {0x00016944, 0x02084080},
-       {0x00016948, 0x000080c0},
+       {0x00016948, 0x00008040},
        {0x00016b80, 0x00000000},
        {0x00016b84, 0x00000000},
        {0x00016b88, 0x00400705},
@@ -245,9 +245,9 @@ static const u32 ar955x_1p0_radio_core[][2] = {
 
 static const u32 ar955x_1p0_modes_xpa_tx_gain_table[][9] = {
        /* Addr      5G_HT20_L   5G_HT40_L   5G_HT20_M   5G_HT40_M   5G_HT20_H   5G_HT40_H   2G_HT40     2G_HT20  */
-       {0x0000a2dc, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xfffd5aaa, 0xfffd5aaa},
-       {0x0000a2e0, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xfffe9ccc, 0xfffe9ccc},
-       {0x0000a2e4, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xffffe0f0, 0xffffe0f0},
+       {0x0000a2dc, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xfffd5aaa, 0xfffd5aaa},
+       {0x0000a2e0, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffe9ccc, 0xfffe9ccc},
+       {0x0000a2e4, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffffe0f0, 0xffffe0f0},
        {0x0000a2e8, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xfffcff00, 0xfffcff00},
        {0x0000a410, 0x000050de, 0x000050de, 0x000050de, 0x000050de, 0x000050de, 0x000050de, 0x000050da, 0x000050da},
        {0x0000a500, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000000, 0x00000000},
@@ -256,63 +256,63 @@ static const u32 ar955x_1p0_modes_xpa_tx_gain_table[][9] = {
        {0x0000a50c, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c000006, 0x0c000006},
        {0x0000a510, 0x1000000d, 0x1000000d, 0x1000000d, 0x1000000d, 0x1000000d, 0x1000000d, 0x0f00000a, 0x0f00000a},
        {0x0000a514, 0x14000011, 0x14000011, 0x14000011, 0x14000011, 0x14000011, 0x14000011, 0x1300000c, 0x1300000c},
-       {0x0000a518, 0x19004008, 0x19004008, 0x19004008, 0x19004008, 0x18004008, 0x18004008, 0x1700000e, 0x1700000e},
-       {0x0000a51c, 0x1d00400a, 0x1d00400a, 0x1d00400a, 0x1d00400a, 0x1c00400a, 0x1c00400a, 0x1b000064, 0x1b000064},
-       {0x0000a520, 0x230020a2, 0x230020a2, 0x210020a2, 0x210020a2, 0x200020a2, 0x200020a2, 0x1f000242, 0x1f000242},
-       {0x0000a524, 0x2500006e, 0x2500006e, 0x2500006e, 0x2500006e, 0x2400006e, 0x2400006e, 0x23000229, 0x23000229},
-       {0x0000a528, 0x29022221, 0x29022221, 0x28022221, 0x28022221, 0x27022221, 0x27022221, 0x270002a2, 0x270002a2},
-       {0x0000a52c, 0x2d00062a, 0x2d00062a, 0x2c00062a, 0x2c00062a, 0x2a00062a, 0x2a00062a, 0x2c001203, 0x2c001203},
-       {0x0000a530, 0x340220a5, 0x340220a5, 0x320220a5, 0x320220a5, 0x2f0220a5, 0x2f0220a5, 0x30001803, 0x30001803},
-       {0x0000a534, 0x380022c5, 0x380022c5, 0x350022c5, 0x350022c5, 0x320022c5, 0x320022c5, 0x33000881, 0x33000881},
-       {0x0000a538, 0x3b002486, 0x3b002486, 0x39002486, 0x39002486, 0x36002486, 0x36002486, 0x38001809, 0x38001809},
-       {0x0000a53c, 0x3f00248a, 0x3f00248a, 0x3d00248a, 0x3d00248a, 0x3a00248a, 0x3a00248a, 0x3a000814, 0x3a000814},
-       {0x0000a540, 0x4202242c, 0x4202242c, 0x4102242c, 0x4102242c, 0x3f02242c, 0x3f02242c, 0x3f001a0c, 0x3f001a0c},
-       {0x0000a544, 0x490044c6, 0x490044c6, 0x460044c6, 0x460044c6, 0x420044c6, 0x420044c6, 0x43001a0e, 0x43001a0e},
-       {0x0000a548, 0x4d024485, 0x4d024485, 0x4a024485, 0x4a024485, 0x46024485, 0x46024485, 0x46001812, 0x46001812},
-       {0x0000a54c, 0x51044483, 0x51044483, 0x4e044483, 0x4e044483, 0x4a044483, 0x4a044483, 0x49001884, 0x49001884},
-       {0x0000a550, 0x5404a40c, 0x5404a40c, 0x5204a40c, 0x5204a40c, 0x4d04a40c, 0x4d04a40c, 0x4d001e84, 0x4d001e84},
-       {0x0000a554, 0x57024632, 0x57024632, 0x55024632, 0x55024632, 0x52024632, 0x52024632, 0x50001e69, 0x50001e69},
-       {0x0000a558, 0x5c00a634, 0x5c00a634, 0x5900a634, 0x5900a634, 0x5600a634, 0x5600a634, 0x550006f4, 0x550006f4},
-       {0x0000a55c, 0x5f026832, 0x5f026832, 0x5d026832, 0x5d026832, 0x5a026832, 0x5a026832, 0x59000ad3, 0x59000ad3},
-       {0x0000a560, 0x6602b012, 0x6602b012, 0x6202b012, 0x6202b012, 0x5d02b012, 0x5d02b012, 0x5e000ad5, 0x5e000ad5},
-       {0x0000a564, 0x6e02d0e1, 0x6e02d0e1, 0x6802d0e1, 0x6802d0e1, 0x6002d0e1, 0x6002d0e1, 0x61001ced, 0x61001ced},
-       {0x0000a568, 0x7202b4c4, 0x7202b4c4, 0x6c02b4c4, 0x6c02b4c4, 0x6502b4c4, 0x6502b4c4, 0x660018d4, 0x660018d4},
-       {0x0000a56c, 0x75007894, 0x75007894, 0x70007894, 0x70007894, 0x6b007894, 0x6b007894, 0x660018d4, 0x660018d4},
-       {0x0000a570, 0x7b025c74, 0x7b025c74, 0x75025c74, 0x75025c74, 0x70025c74, 0x70025c74, 0x660018d4, 0x660018d4},
-       {0x0000a574, 0x8300bcb5, 0x8300bcb5, 0x7a00bcb5, 0x7a00bcb5, 0x7600bcb5, 0x7600bcb5, 0x660018d4, 0x660018d4},
-       {0x0000a578, 0x8a04dc74, 0x8a04dc74, 0x7f04dc74, 0x7f04dc74, 0x7c04dc74, 0x7c04dc74, 0x660018d4, 0x660018d4},
-       {0x0000a57c, 0x8a04dc74, 0x8a04dc74, 0x7f04dc74, 0x7f04dc74, 0x7c04dc74, 0x7c04dc74, 0x660018d4, 0x660018d4},
+       {0x0000a518, 0x1700002b, 0x1700002b, 0x1700002b, 0x1700002b, 0x1600002b, 0x1600002b, 0x1700000e, 0x1700000e},
+       {0x0000a51c, 0x1b00002d, 0x1b00002d, 0x1b00002d, 0x1b00002d, 0x1a00002d, 0x1a00002d, 0x1b000064, 0x1b000064},
+       {0x0000a520, 0x20000031, 0x20000031, 0x1f000031, 0x1f000031, 0x1e000031, 0x1e000031, 0x1f000242, 0x1f000242},
+       {0x0000a524, 0x24000051, 0x24000051, 0x23000051, 0x23000051, 0x23000051, 0x23000051, 0x23000229, 0x23000229},
+       {0x0000a528, 0x27000071, 0x27000071, 0x27000071, 0x27000071, 0x26000071, 0x26000071, 0x270002a2, 0x270002a2},
+       {0x0000a52c, 0x2b000092, 0x2b000092, 0x2b000092, 0x2b000092, 0x2b000092, 0x2b000092, 0x2c001203, 0x2c001203},
+       {0x0000a530, 0x3000028c, 0x3000028c, 0x2f00028c, 0x2f00028c, 0x2e00028c, 0x2e00028c, 0x30001803, 0x30001803},
+       {0x0000a534, 0x34000290, 0x34000290, 0x33000290, 0x33000290, 0x32000290, 0x32000290, 0x33000881, 0x33000881},
+       {0x0000a538, 0x37000292, 0x37000292, 0x36000292, 0x36000292, 0x35000292, 0x35000292, 0x38001809, 0x38001809},
+       {0x0000a53c, 0x3b02028d, 0x3b02028d, 0x3a02028d, 0x3a02028d, 0x3902028d, 0x3902028d, 0x3a000814, 0x3a000814},
+       {0x0000a540, 0x3f020291, 0x3f020291, 0x3e020291, 0x3e020291, 0x3d020291, 0x3d020291, 0x3f001a0c, 0x3f001a0c},
+       {0x0000a544, 0x44020490, 0x44020490, 0x43020490, 0x43020490, 0x42020490, 0x42020490, 0x43001a0e, 0x43001a0e},
+       {0x0000a548, 0x48020492, 0x48020492, 0x47020492, 0x47020492, 0x46020492, 0x46020492, 0x46001812, 0x46001812},
+       {0x0000a54c, 0x4c020692, 0x4c020692, 0x4b020692, 0x4b020692, 0x4a020692, 0x4a020692, 0x49001884, 0x49001884},
+       {0x0000a550, 0x50020892, 0x50020892, 0x4f020892, 0x4f020892, 0x4e020892, 0x4e020892, 0x4d001e84, 0x4d001e84},
+       {0x0000a554, 0x53040891, 0x53040891, 0x53040891, 0x53040891, 0x52040891, 0x52040891, 0x50001e69, 0x50001e69},
+       {0x0000a558, 0x58040893, 0x58040893, 0x57040893, 0x57040893, 0x56040893, 0x56040893, 0x550006f4, 0x550006f4},
+       {0x0000a55c, 0x5c0408b4, 0x5c0408b4, 0x5a0408b4, 0x5a0408b4, 0x5a0408b4, 0x5a0408b4, 0x59000ad3, 0x59000ad3},
+       {0x0000a560, 0x610408b6, 0x610408b6, 0x5e0408b6, 0x5e0408b6, 0x5e0408b6, 0x5e0408b6, 0x5e000ad5, 0x5e000ad5},
+       {0x0000a564, 0x670408f6, 0x670408f6, 0x620408f6, 0x620408f6, 0x620408f6, 0x620408f6, 0x61001ced, 0x61001ced},
+       {0x0000a568, 0x6a040cf6, 0x6a040cf6, 0x66040cf6, 0x66040cf6, 0x66040cf6, 0x66040cf6, 0x660018d4, 0x660018d4},
+       {0x0000a56c, 0x6d040d76, 0x6d040d76, 0x6a040d76, 0x6a040d76, 0x6a040d76, 0x6a040d76, 0x660018d4, 0x660018d4},
+       {0x0000a570, 0x70060db6, 0x70060db6, 0x6e060db6, 0x6e060db6, 0x6e060db6, 0x6e060db6, 0x660018d4, 0x660018d4},
+       {0x0000a574, 0x730a0df6, 0x730a0df6, 0x720a0df6, 0x720a0df6, 0x720a0df6, 0x720a0df6, 0x660018d4, 0x660018d4},
+       {0x0000a578, 0x770a13f6, 0x770a13f6, 0x760a13f6, 0x760a13f6, 0x760a13f6, 0x760a13f6, 0x660018d4, 0x660018d4},
+       {0x0000a57c, 0x770a13f6, 0x770a13f6, 0x760a13f6, 0x760a13f6, 0x760a13f6, 0x760a13f6, 0x660018d4, 0x660018d4},
        {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
-       {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x03804000, 0x03804000},
-       {0x0000a610, 0x04c08c01, 0x04c08c01, 0x04808b01, 0x04808b01, 0x04808a01, 0x04808a01, 0x0300ca02, 0x0300ca02},
-       {0x0000a614, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00000e04, 0x00000e04},
-       {0x0000a618, 0x04010c01, 0x04010c01, 0x03c10b01, 0x03c10b01, 0x03810a01, 0x03810a01, 0x03014000, 0x03014000},
-       {0x0000a61c, 0x03814e05, 0x03814e05, 0x03414d05, 0x03414d05, 0x03414d05, 0x03414d05, 0x00000000, 0x00000000},
-       {0x0000a620, 0x04010303, 0x04010303, 0x03c10303, 0x03c10303, 0x03810303, 0x03810303, 0x00000000, 0x00000000},
-       {0x0000a624, 0x03814e05, 0x03814e05, 0x03414d05, 0x03414d05, 0x03414d05, 0x03414d05, 0x03014000, 0x03014000},
-       {0x0000a628, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x03804c05, 0x03804c05},
-       {0x0000a62c, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x0701de06, 0x0701de06},
-       {0x0000a630, 0x03418000, 0x03418000, 0x03018000, 0x03018000, 0x02c18000, 0x02c18000, 0x07819c07, 0x07819c07},
-       {0x0000a634, 0x03815004, 0x03815004, 0x03414f04, 0x03414f04, 0x03414e04, 0x03414e04, 0x0701dc07, 0x0701dc07},
-       {0x0000a638, 0x03005302, 0x03005302, 0x02c05202, 0x02c05202, 0x02805202, 0x02805202, 0x0701dc07, 0x0701dc07},
-       {0x0000a63c, 0x04c09302, 0x04c09302, 0x04809202, 0x04809202, 0x04809202, 0x04809202, 0x0701dc07, 0x0701dc07},
-       {0x0000b2dc, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xfffd5aaa, 0xfffd5aaa},
-       {0x0000b2e0, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xfffe9ccc, 0xfffe9ccc},
-       {0x0000b2e4, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xffffe0f0, 0xffffe0f0},
+       {0x0000a60c, 0x02c04b01, 0x02c04b01, 0x02c04b01, 0x02c04b01, 0x02c04b01, 0x02c04b01, 0x03804000, 0x03804000},
+       {0x0000a610, 0x04008b01, 0x04008b01, 0x04008b01, 0x04008b01, 0x03c08b01, 0x03c08b01, 0x0300ca02, 0x0300ca02},
+       {0x0000a614, 0x05811403, 0x05811403, 0x05411303, 0x05411303, 0x05411303, 0x05411303, 0x00000e04, 0x00000e04},
+       {0x0000a618, 0x05811604, 0x05811604, 0x05411504, 0x05411504, 0x05411504, 0x05411504, 0x03014000, 0x03014000},
+       {0x0000a61c, 0x05811604, 0x05811604, 0x05411504, 0x05411504, 0x05411504, 0x05411504, 0x00000000, 0x00000000},
+       {0x0000a620, 0x05811604, 0x05811604, 0x05411504, 0x05411504, 0x05411504, 0x05411504, 0x00000000, 0x00000000},
+       {0x0000a624, 0x05811604, 0x05811604, 0x05411504, 0x05411504, 0x05411504, 0x05411504, 0x03014000, 0x03014000},
+       {0x0000a628, 0x05811604, 0x05811604, 0x05411504, 0x05411504, 0x05411504, 0x05411504, 0x03804c05, 0x03804c05},
+       {0x0000a62c, 0x06815604, 0x06815604, 0x06415504, 0x06415504, 0x06015504, 0x06015504, 0x0701de06, 0x0701de06},
+       {0x0000a630, 0x07819a05, 0x07819a05, 0x07419905, 0x07419905, 0x07019805, 0x07019805, 0x07819c07, 0x07819c07},
+       {0x0000a634, 0x07819e06, 0x07819e06, 0x07419d06, 0x07419d06, 0x07019c06, 0x07019c06, 0x0701dc07, 0x0701dc07},
+       {0x0000a638, 0x07819e06, 0x07819e06, 0x07419d06, 0x07419d06, 0x07019c06, 0x07019c06, 0x0701dc07, 0x0701dc07},
+       {0x0000a63c, 0x07819e06, 0x07819e06, 0x07419d06, 0x07419d06, 0x07019c06, 0x07019c06, 0x0701dc07, 0x0701dc07},
+       {0x0000b2dc, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xfffd5aaa, 0xfffd5aaa},
+       {0x0000b2e0, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffe9ccc, 0xfffe9ccc},
+       {0x0000b2e4, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffffe0f0, 0xffffe0f0},
        {0x0000b2e8, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xfffcff00, 0xfffcff00},
-       {0x0000c2dc, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xfffd5aaa, 0xfffd5aaa},
-       {0x0000c2e0, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xfffe9ccc, 0xfffe9ccc},
-       {0x0000c2e4, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xffffe0f0, 0xffffe0f0},
+       {0x0000c2dc, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xfffd5aaa, 0xfffd5aaa},
+       {0x0000c2e0, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffe9ccc, 0xfffe9ccc},
+       {0x0000c2e4, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffffe0f0, 0xffffe0f0},
        {0x0000c2e8, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xfffcff00, 0xfffcff00},
        {0x00016044, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x010002d4, 0x010002d4},
-       {0x00016048, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x66482401, 0x66482401},
+       {0x00016048, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401},
        {0x00016280, 0x01801e84, 0x01801e84, 0x01801e84, 0x01801e84, 0x01801e84, 0x01801e84, 0x01808e84, 0x01808e84},
        {0x00016444, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x010002d4, 0x010002d4},
-       {0x00016448, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x66482401, 0x66482401},
+       {0x00016448, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401},
        {0x00016844, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x010002d4, 0x010002d4},
-       {0x00016848, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x66482401, 0x66482401},
+       {0x00016848, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401},
 };
 
 static const u32 ar955x_1p0_mac_core[][2] = {
@@ -846,7 +846,7 @@ static const u32 ar955x_1p0_baseband_core[][2] = {
        {0x0000a44c, 0x00000001},
        {0x0000a450, 0x00010000},
        {0x0000a458, 0x00000000},
-       {0x0000a644, 0x3fad9d74},
+       {0x0000a644, 0xbfad9d74},
        {0x0000a648, 0x0048060a},
        {0x0000a64c, 0x00003c37},
        {0x0000a670, 0x03020100},
@@ -1277,7 +1277,7 @@ static const u32 ar955x_1p0_modes_fast_clock[][3] = {
        {0x0000801c, 0x148ec02b, 0x148ec057},
        {0x00008318, 0x000044c0, 0x00008980},
        {0x00009e00, 0x0372131c, 0x0372131c},
-       {0x0000a230, 0x0000000b, 0x00000016},
+       {0x0000a230, 0x0000400b, 0x00004016},
        {0x0000a254, 0x00000898, 0x00001130},
 };
 
index 6e1915aee712c5978d2d581311366e82b0d24d70..28fd99203f6447e4d3545b0fd12b920733ffc10c 100644 (file)
@@ -685,6 +685,82 @@ static const u32 ar9580_1p0_mixed_ob_db_tx_gain_table[][5] = {
 
 #define ar9580_1p0_high_ob_db_tx_gain_table ar9300Modes_high_ob_db_tx_gain_table_2p2
 
+#define ar9580_1p0_type5_tx_gain_table ar9300Modes_type5_tx_gain_table_2p2
+
+static const u32 ar9580_1p0_type6_tx_gain_table[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x0000a2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+       {0x0000a2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+       {0x0000a2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
+       {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+       {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+       {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+       {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+       {0x0000a510, 0x15000028, 0x15000028, 0x0f000202, 0x0f000202},
+       {0x0000a514, 0x1b00002b, 0x1b00002b, 0x12000400, 0x12000400},
+       {0x0000a518, 0x1f020028, 0x1f020028, 0x16000402, 0x16000402},
+       {0x0000a51c, 0x2502002b, 0x2502002b, 0x19000404, 0x19000404},
+       {0x0000a520, 0x2a04002a, 0x2a04002a, 0x1c000603, 0x1c000603},
+       {0x0000a524, 0x2e06002a, 0x2e06002a, 0x21000a02, 0x21000a02},
+       {0x0000a528, 0x3302202d, 0x3302202d, 0x25000a04, 0x25000a04},
+       {0x0000a52c, 0x3804202c, 0x3804202c, 0x28000a20, 0x28000a20},
+       {0x0000a530, 0x3c06202c, 0x3c06202c, 0x2c000e20, 0x2c000e20},
+       {0x0000a534, 0x4108202d, 0x4108202d, 0x30000e22, 0x30000e22},
+       {0x0000a538, 0x4506402d, 0x4506402d, 0x34000e24, 0x34000e24},
+       {0x0000a53c, 0x4906222d, 0x4906222d, 0x38001640, 0x38001640},
+       {0x0000a540, 0x4d062231, 0x4d062231, 0x3c001660, 0x3c001660},
+       {0x0000a544, 0x50082231, 0x50082231, 0x3f001861, 0x3f001861},
+       {0x0000a548, 0x5608422e, 0x5608422e, 0x43001a81, 0x43001a81},
+       {0x0000a54c, 0x5e08442e, 0x5e08442e, 0x47001a83, 0x47001a83},
+       {0x0000a550, 0x620a4431, 0x620a4431, 0x4a001c84, 0x4a001c84},
+       {0x0000a554, 0x640a4432, 0x640a4432, 0x4e001ce3, 0x4e001ce3},
+       {0x0000a558, 0x680a4434, 0x680a4434, 0x52001ce5, 0x52001ce5},
+       {0x0000a55c, 0x6c0a6434, 0x6c0a6434, 0x56001ce9, 0x56001ce9},
+       {0x0000a560, 0x6f0a6633, 0x6f0a6633, 0x5a001ceb, 0x5a001ceb},
+       {0x0000a564, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a568, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a56c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a570, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a574, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a578, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a57c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+       {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a608, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+       {0x0000a60c, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+       {0x0000a610, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+       {0x0000a614, 0x01804601, 0x01804601, 0x01404000, 0x01404000},
+       {0x0000a618, 0x01804601, 0x01804601, 0x01404501, 0x01404501},
+       {0x0000a61c, 0x01804601, 0x01804601, 0x02008501, 0x02008501},
+       {0x0000a620, 0x03408d02, 0x03408d02, 0x0280ca03, 0x0280ca03},
+       {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
+       {0x0000a628, 0x03410d04, 0x03410d04, 0x04014c04, 0x04014c04},
+       {0x0000a62c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+       {0x0000a630, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+       {0x0000a634, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+       {0x0000a638, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+       {0x0000a63c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+       {0x0000b2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+       {0x0000b2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+       {0x0000b2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
+       {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x0000c2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+       {0x0000c2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+       {0x0000c2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
+       {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+       {0x00016048, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
+       {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+       {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+       {0x00016448, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
+       {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+       {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+       {0x00016848, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
+       {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+};
+
 static const u32 ar9580_1p0_soc_preamble[][2] = {
        /* Addr      allmodes  */
        {0x000040a4, 0x00a0c1c9},
index 42794c546a4068ac91b47c1252153b9040362555..97c90b21e1cb72108010d994f4e037f6758ba774 100644 (file)
@@ -109,14 +109,11 @@ struct ath_descdma {
        void *dd_desc;
        dma_addr_t dd_desc_paddr;
        u32 dd_desc_len;
-       struct ath_buf *dd_bufptr;
 };
 
 int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
                      struct list_head *head, const char *name,
                      int nbuf, int ndesc, bool is_tx);
-void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd,
-                        struct list_head *head);
 
 /***********/
 /* RX / TX */
@@ -319,10 +316,11 @@ struct ath_rx {
        unsigned int rxfilter;
        struct list_head rxbuf;
        struct ath_descdma rxdma;
-       struct ath_buf *rx_bufptr;
        struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
 
        struct sk_buff *frag;
+
+       u32 ampdu_ref;
 };
 
 int ath_startrecv(struct ath_softc *sc);
@@ -336,14 +334,12 @@ void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq);
 void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq);
 void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq);
 void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq);
-bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx);
-void ath_draintxq(struct ath_softc *sc,
-                    struct ath_txq *txq, bool retry_tx);
+bool ath_drain_all_txq(struct ath_softc *sc);
+void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq);
 void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an);
 void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an);
 void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq);
 int ath_tx_init(struct ath_softc *sc, int nbufs);
-void ath_tx_cleanup(struct ath_softc *sc);
 int ath_txq_update(struct ath_softc *sc, int qnum,
                   struct ath9k_tx_queue_info *q);
 void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop);
@@ -672,6 +668,23 @@ struct ath9k_vif_iter_data {
        int nadhocs;   /* number of adhoc vifs */
 };
 
+/* enum spectral_mode:
+ *
+ * @SPECTRAL_DISABLED: spectral mode is disabled
+ * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with
+ *     something else.
+ * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples
+ *     is performed manually.
+ * @SPECTRAL_CHANSCAN: Like manual, but also triggered when changing channels
+ *     during a channel scan.
+ */
+enum spectral_mode {
+       SPECTRAL_DISABLED = 0,
+       SPECTRAL_BACKGROUND,
+       SPECTRAL_MANUAL,
+       SPECTRAL_CHANSCAN,
+};
+
 struct ath_softc {
        struct ieee80211_hw *hw;
        struct device *dev;
@@ -740,6 +753,11 @@ struct ath_softc {
        u8 ant_tx, ant_rx;
        struct dfs_pattern_detector *dfs_detector;
        u32 wow_enabled;
+       /* relay(fs) channel for spectral scan */
+       struct rchan *rfs_chan_spec_scan;
+       enum spectral_mode spectral_mode;
+       struct ath_spec_scan spec_config;
+       int scanning;
 
 #ifdef CONFIG_PM_SLEEP
        atomic_t wow_got_bmiss_intr;
@@ -748,6 +766,133 @@ struct ath_softc {
 #endif
 };
 
+#define SPECTRAL_SCAN_BITMASK          0x10
+/* Radar info packet format, used for DFS and spectral formats. */
+struct ath_radar_info {
+       u8 pulse_length_pri;
+       u8 pulse_length_ext;
+       u8 pulse_bw_info;
+} __packed;
+
+/* The HT20 spectral data has 4 bytes of additional information at it's end.
+ *
+ * [7:0]: all bins {max_magnitude[1:0], bitmap_weight[5:0]}
+ * [7:0]: all bins  max_magnitude[9:2]
+ * [7:0]: all bins {max_index[5:0], max_magnitude[11:10]}
+ * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
+ */
+struct ath_ht20_mag_info {
+       u8 all_bins[3];
+       u8 max_exp;
+} __packed;
+
+#define SPECTRAL_HT20_NUM_BINS         56
+
+/* WARNING: don't actually use this struct! MAC may vary the amount of
+ * data by -1/+2. This struct is for reference only.
+ */
+struct ath_ht20_fft_packet {
+       u8 data[SPECTRAL_HT20_NUM_BINS];
+       struct ath_ht20_mag_info mag_info;
+       struct ath_radar_info radar_info;
+} __packed;
+
+#define SPECTRAL_HT20_TOTAL_DATA_LEN   (sizeof(struct ath_ht20_fft_packet))
+
+/* Dynamic 20/40 mode:
+ *
+ * [7:0]: lower bins {max_magnitude[1:0], bitmap_weight[5:0]}
+ * [7:0]: lower bins  max_magnitude[9:2]
+ * [7:0]: lower bins {max_index[5:0], max_magnitude[11:10]}
+ * [7:0]: upper bins {max_magnitude[1:0], bitmap_weight[5:0]}
+ * [7:0]: upper bins  max_magnitude[9:2]
+ * [7:0]: upper bins {max_index[5:0], max_magnitude[11:10]}
+ * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
+ */
+struct ath_ht20_40_mag_info {
+       u8 lower_bins[3];
+       u8 upper_bins[3];
+       u8 max_exp;
+} __packed;
+
+#define SPECTRAL_HT20_40_NUM_BINS              128
+
+/* WARNING: don't actually use this struct! MAC may vary the amount of
+ * data. This struct is for reference only.
+ */
+struct ath_ht20_40_fft_packet {
+       u8 data[SPECTRAL_HT20_40_NUM_BINS];
+       struct ath_ht20_40_mag_info mag_info;
+       struct ath_radar_info radar_info;
+} __packed;
+
+
+#define SPECTRAL_HT20_40_TOTAL_DATA_LEN        (sizeof(struct ath_ht20_40_fft_packet))
+
+/* grabs the max magnitude from the all/upper/lower bins */
+static inline u16 spectral_max_magnitude(u8 *bins)
+{
+       return (bins[0] & 0xc0) >> 6 |
+              (bins[1] & 0xff) << 2 |
+              (bins[2] & 0x03) << 10;
+}
+
+/* return the max magnitude from the all/upper/lower bins */
+static inline u8 spectral_max_index(u8 *bins)
+{
+       s8 m = (bins[2] & 0xfc) >> 2;
+
+       /* TODO: this still doesn't always report the right values ... */
+       if (m > 32)
+               m |= 0xe0;
+       else
+               m &= ~0xe0;
+
+       return m + 29;
+}
+
+/* return the bitmap weight from the all/upper/lower bins */
+static inline u8 spectral_bitmap_weight(u8 *bins)
+{
+       return bins[0] & 0x3f;
+}
+
+/* FFT sample format given to userspace via debugfs.
+ *
+ * Please keep the type/length at the front position and change
+ * other fields after adding another sample type
+ *
+ * TODO: this might need rework when switching to nl80211-based
+ * interface.
+ */
+enum ath_fft_sample_type {
+       ATH_FFT_SAMPLE_HT20 = 1,
+};
+
+struct fft_sample_tlv {
+       u8 type;        /* see ath_fft_sample */
+       __be16 length;
+       /* type dependent data follows */
+} __packed;
+
+struct fft_sample_ht20 {
+       struct fft_sample_tlv tlv;
+
+       u8 max_exp;
+
+       __be16 freq;
+       s8 rssi;
+       s8 noise;
+
+       __be16 max_magnitude;
+       u8 max_index;
+       u8 bitmap_weight;
+
+       __be64 tsf;
+
+       u8 data[SPECTRAL_HT20_NUM_BINS];
+} __packed;
+
 void ath9k_tasklet(unsigned long data);
 int ath_cabq_update(struct ath_softc *);
 
@@ -770,6 +915,10 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
 void ath9k_reload_chainmask_settings(struct ath_softc *sc);
 
 bool ath9k_uses_beacons(int type);
+void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
+int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
+                              enum spectral_mode spectral_mode);
+
 
 #ifdef CONFIG_ATH9K_PCI
 int ath_pci_init(void);
index 2ca355e94da65467e36595990423c80be1b897b6..dd3771954bd7ce855c028aef8aa56f0f27134bcd 100644 (file)
@@ -199,7 +199,7 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
                if (sc->nvifs > 1) {
                        ath_dbg(common, BEACON,
                                "Flushing previous cabq traffic\n");
-                       ath_draintxq(sc, cabq, false);
+                       ath_draintxq(sc, cabq);
                }
        }
 
index e585fc827c50b2e9d5ff2fe08afb15cd49cedba6..3714b971d18ebe41a89d1453acc63462c682af8b 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/export.h>
+#include <linux/relay.h>
 #include <asm/unaligned.h>
 
 #include "ath9k.h"
@@ -894,6 +895,7 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
        RXS_ERR("RX-Bytes-All", rx_bytes_all);
        RXS_ERR("RX-Beacons", rx_beacons);
        RXS_ERR("RX-Frags", rx_frags);
+       RXS_ERR("RX-Spectral", rx_spectral);
 
        if (len > size)
                len = size;
@@ -965,6 +967,290 @@ static const struct file_operations fops_recv = {
        .llseek = default_llseek,
 };
 
+static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char *mode = "";
+       unsigned int len;
+
+       switch (sc->spectral_mode) {
+       case SPECTRAL_DISABLED:
+               mode = "disable";
+               break;
+       case SPECTRAL_BACKGROUND:
+               mode = "background";
+               break;
+       case SPECTRAL_CHANSCAN:
+               mode = "chanscan";
+               break;
+       case SPECTRAL_MANUAL:
+               mode = "manual";
+               break;
+       }
+       len = strlen(mode);
+       return simple_read_from_buffer(user_buf, count, ppos, mode, len);
+}
+
+static ssize_t write_file_spec_scan_ctl(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+
+       if (strncmp("trigger", buf, 7) == 0) {
+               ath9k_spectral_scan_trigger(sc->hw);
+       } else if (strncmp("background", buf, 9) == 0) {
+               ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND);
+               ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");
+       } else if (strncmp("chanscan", buf, 8) == 0) {
+               ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN);
+               ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");
+       } else if (strncmp("manual", buf, 6) == 0) {
+               ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL);
+               ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");
+       } else if (strncmp("disable", buf, 7) == 0) {
+               ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED);
+               ath_dbg(common, CONFIG, "spectral scan: disabled\n");
+       } else {
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static const struct file_operations fops_spec_scan_ctl = {
+       .read = read_file_spec_scan_ctl,
+       .write = write_file_spec_scan_ctl,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t read_file_spectral_short_repeat(struct file *file,
+                                              char __user *user_buf,
+                                              size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.short_repeat);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_short_repeat(struct file *file,
+                                               const char __user *user_buf,
+                                               size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 1)
+               return -EINVAL;
+
+       sc->spec_config.short_repeat = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_short_repeat = {
+       .read = read_file_spectral_short_repeat,
+       .write = write_file_spectral_short_repeat,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t read_file_spectral_count(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.count);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_count(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 255)
+               return -EINVAL;
+
+       sc->spec_config.count = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_count = {
+       .read = read_file_spectral_count,
+       .write = write_file_spectral_count,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t read_file_spectral_period(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.period);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_period(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 255)
+               return -EINVAL;
+
+       sc->spec_config.period = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_period = {
+       .read = read_file_spectral_period,
+       .write = write_file_spectral_period,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t read_file_spectral_fft_period(struct file *file,
+                                            char __user *user_buf,
+                                            size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.fft_period);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_fft_period(struct file *file,
+                                             const char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 15)
+               return -EINVAL;
+
+       sc->spec_config.fft_period = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_fft_period = {
+       .read = read_file_spectral_fft_period,
+       .write = write_file_spectral_fft_period,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static struct dentry *create_buf_file_handler(const char *filename,
+                                             struct dentry *parent,
+                                             umode_t mode,
+                                             struct rchan_buf *buf,
+                                             int *is_global)
+{
+       struct dentry *buf_file;
+
+       buf_file = debugfs_create_file(filename, mode, parent, buf,
+                                      &relay_file_operations);
+       *is_global = 1;
+       return buf_file;
+}
+
+static int remove_buf_file_handler(struct dentry *dentry)
+{
+       debugfs_remove(dentry);
+
+       return 0;
+}
+
+void ath_debug_send_fft_sample(struct ath_softc *sc,
+                              struct fft_sample_tlv *fft_sample_tlv)
+{
+       int length;
+       if (!sc->rfs_chan_spec_scan)
+               return;
+
+       length = __be16_to_cpu(fft_sample_tlv->length) +
+                sizeof(*fft_sample_tlv);
+       relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length);
+}
+
+static struct rchan_callbacks rfs_spec_scan_cb = {
+       .create_buf_file = create_buf_file_handler,
+       .remove_buf_file = remove_buf_file_handler,
+};
+
+
 static ssize_t read_file_regidx(struct file *file, char __user *user_buf,
                                 size_t count, loff_t *ppos)
 {
@@ -1779,6 +2065,24 @@ int ath9k_init_debug(struct ath_hw *ah)
                            &fops_base_eeprom);
        debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_modal_eeprom);
+       sc->rfs_chan_spec_scan = relay_open("spectral_scan",
+                                           sc->debug.debugfs_phy,
+                                           262144, 4, &rfs_spec_scan_cb,
+                                           NULL);
+       debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc,
+                           &fops_spec_scan_ctl);
+       debugfs_create_file("spectral_short_repeat", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc,
+                           &fops_spectral_short_repeat);
+       debugfs_create_file("spectral_count", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc, &fops_spectral_count);
+       debugfs_create_file("spectral_period", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc, &fops_spectral_period);
+       debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc,
+                           &fops_spectral_fft_period);
+
 #ifdef CONFIG_ATH9K_MAC_DEBUG
        debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_samps);
index 6df2ab62dcb706df5bef4a8afdf04593651f111a..410d6d8f1aa7b9e9ffffe9a1a6779c576fb236ea 100644 (file)
@@ -23,6 +23,7 @@
 
 struct ath_txq;
 struct ath_buf;
+struct fft_sample_tlv;
 
 #ifdef CONFIG_ATH9K_DEBUGFS
 #define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++
@@ -218,6 +219,7 @@ struct ath_tx_stats {
  * @rx_too_many_frags_err:  Frames dropped due to too-many-frags received.
  * @rx_beacons:  No. of beacons received.
  * @rx_frags:  No. of rx-fragements received.
+ * @rx_spectral: No of spectral packets received.
  */
 struct ath_rx_stats {
        u32 rx_pkts_all;
@@ -236,6 +238,7 @@ struct ath_rx_stats {
        u32 rx_too_many_frags_err;
        u32 rx_beacons;
        u32 rx_frags;
+       u32 rx_spectral;
 };
 
 struct ath_stats {
@@ -321,6 +324,10 @@ void ath9k_sta_remove_debugfs(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
                              struct ieee80211_sta *sta,
                              struct dentry *dir);
+
+void ath_debug_send_fft_sample(struct ath_softc *sc,
+                              struct fft_sample_tlv *fft_sample);
+
 #else
 
 #define RX_STAT_INC(c) /* NOP */
index 05d5ba66cac3588f64e8f6ab0545af76fbf88cbb..e5d7958ab9485a617c60f520d2981b38b50cb7eb 100644 (file)
@@ -280,14 +280,14 @@ err:
        return ret;
 }
 
-static int ath9k_reg_notifier(struct wiphy *wiphy,
-                             struct regulatory_request *request)
+static void ath9k_reg_notifier(struct wiphy *wiphy,
+                              struct regulatory_request *request)
 {
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct ath9k_htc_priv *priv = hw->priv;
 
-       return ath_reg_notifier_apply(wiphy, request,
-                                     ath9k_hw_regulatory(priv->ah));
+       ath_reg_notifier_apply(wiphy, request,
+                              ath9k_hw_regulatory(priv->ah));
 }
 
 static unsigned int ath9k_regread(void *hw_priv, u32 reg_offset)
index 9c07a8fa5134bb4611b93b9645c20fd0e0c414e3..a8016d70088aa3d492d15ff76f6f16f8f3c2df2f 100644 (file)
@@ -1628,7 +1628,9 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
                if (!ret)
                        ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                break;
-       case IEEE80211_AMPDU_TX_STOP:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
                ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                break;
index 0f2b97f6b7390e32a920e0d645449e820d9d195a..14b701140b49aa3b66c4eb1a280d51500a69f2db 100644 (file)
@@ -101,22 +101,6 @@ static inline void ath9k_hw_spur_mitigate_freq(struct ath_hw *ah,
        ath9k_hw_private_ops(ah)->spur_mitigate_freq(ah, chan);
 }
 
-static inline int ath9k_hw_rf_alloc_ext_banks(struct ath_hw *ah)
-{
-       if (!ath9k_hw_private_ops(ah)->rf_alloc_ext_banks)
-               return 0;
-
-       return ath9k_hw_private_ops(ah)->rf_alloc_ext_banks(ah);
-}
-
-static inline void ath9k_hw_rf_free_ext_banks(struct ath_hw *ah)
-{
-       if (!ath9k_hw_private_ops(ah)->rf_free_ext_banks)
-               return;
-
-       ath9k_hw_private_ops(ah)->rf_free_ext_banks(ah);
-}
-
 static inline bool ath9k_hw_set_rf_regs(struct ath_hw *ah,
                                        struct ath9k_channel *chan,
                                        u16 modesIndex)
index e26f92dd063ecebe58de6f76ac4334c71b27b75a..7b485e4d104cdfb2f6dd584d2f97e0a2e229cf6e 100644 (file)
@@ -54,11 +54,6 @@ static void ath9k_hw_init_cal_settings(struct ath_hw *ah)
        ath9k_hw_private_ops(ah)->init_cal_settings(ah);
 }
 
-static void ath9k_hw_init_mode_regs(struct ath_hw *ah)
-{
-       ath9k_hw_private_ops(ah)->init_mode_regs(ah);
-}
-
 static u32 ath9k_hw_compute_pll_control(struct ath_hw *ah,
                                        struct ath9k_channel *chan)
 {
@@ -208,7 +203,7 @@ void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan,
        udelay(hw_delay + BASE_ACTIVATE_DELAY);
 }
 
-void ath9k_hw_write_array(struct ath_hw *ah, struct ar5416IniArray *array,
+void ath9k_hw_write_array(struct ath_hw *ah, const struct ar5416IniArray *array,
                          int column, unsigned int *writecnt)
 {
        int r;
@@ -554,28 +549,19 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
                ah->eep_ops->get_eeprom_ver(ah),
                ah->eep_ops->get_eeprom_rev(ah));
 
-       ecode = ath9k_hw_rf_alloc_ext_banks(ah);
-       if (ecode) {
-               ath_err(ath9k_hw_common(ah),
-                       "Failed allocating banks for external radio\n");
-               ath9k_hw_rf_free_ext_banks(ah);
-               return ecode;
-       }
-
-       if (ah->config.enable_ani) {
-               ath9k_hw_ani_setup(ah);
+       if (ah->config.enable_ani)
                ath9k_hw_ani_init(ah);
-       }
 
        return 0;
 }
 
-static void ath9k_hw_attach_ops(struct ath_hw *ah)
+static int ath9k_hw_attach_ops(struct ath_hw *ah)
 {
-       if (AR_SREV_9300_20_OR_LATER(ah))
-               ar9003_hw_attach_ops(ah);
-       else
-               ar9002_hw_attach_ops(ah);
+       if (!AR_SREV_9300_20_OR_LATER(ah))
+               return ar9002_hw_attach_ops(ah);
+
+       ar9003_hw_attach_ops(ah);
+       return 0;
 }
 
 /* Called for all hardware families */
@@ -611,7 +597,9 @@ static int __ath9k_hw_init(struct ath_hw *ah)
        ath9k_hw_init_defaults(ah);
        ath9k_hw_init_config(ah);
 
-       ath9k_hw_attach_ops(ah);
+       r = ath9k_hw_attach_ops(ah);
+       if (r)
+               return r;
 
        if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) {
                ath_err(common, "Couldn't wakeup chip\n");
@@ -675,8 +663,6 @@ static int __ath9k_hw_init(struct ath_hw *ah)
        if (!AR_SREV_9300_20_OR_LATER(ah))
                ah->ani_function &= ~ATH9K_ANI_MRC_CCK;
 
-       ath9k_hw_init_mode_regs(ah);
-
        if (!ah->is_pciexpress)
                ath9k_hw_disablepcie(ah);
 
@@ -1153,12 +1139,9 @@ void ath9k_hw_deinit(struct ath_hw *ah)
        struct ath_common *common = ath9k_hw_common(ah);
 
        if (common->state < ATH_HW_INITIALIZED)
-               goto free_hw;
+               return;
 
        ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
-
-free_hw:
-       ath9k_hw_rf_free_ext_banks(ah);
 }
 EXPORT_SYMBOL(ath9k_hw_deinit);
 
@@ -2578,12 +2561,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
                rx_chainmask >>= 1;
        }
 
-       if (AR_SREV_9300_20_OR_LATER(ah)) {
-               ah->enabled_cals |= TX_IQ_CAL;
-               if (AR_SREV_9485_OR_LATER(ah))
-                       ah->enabled_cals |= TX_IQ_ON_AGC_CAL;
-       }
-
        if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
                if (!(ah->ent_mode & AR_ENT_OTP_49GHZ_DISABLE))
                        pCap->hw_caps |= ATH9K_HW_CAP_MCI;
@@ -2592,7 +2569,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
                        pCap->hw_caps |= ATH9K_HW_CAP_RTT;
        }
 
-
        if (AR_SREV_9280_20_OR_LATER(ah)) {
                pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE |
                                 ATH9K_HW_WOW_PATTERN_MATCH_EXACT;
index 9d26fc56ca56a6bf7d4e8ac0ed91366df0de50cd..784e81ccb9031ed6dd3d2f29ad1dcda0faa9f4f7 100644 (file)
@@ -397,6 +397,7 @@ enum ath9k_int {
 #define MAX_RTT_TABLE_ENTRY     6
 #define MAX_IQCAL_MEASUREMENT  8
 #define MAX_CL_TAB_ENTRY       16
+#define CL_TAB_ENTRY(reg_base) (reg_base + (4 * j))
 
 struct ath9k_hw_cal_data {
        u16 channel;
@@ -599,13 +600,10 @@ struct ath_hw_radar_conf {
  * @init_cal_settings: setup types of calibrations supported
  * @init_cal: starts actual calibration
  *
- * @init_mode_regs: Initializes mode registers
  * @init_mode_gain_regs: Initialize TX/RX gain registers
  *
  * @rf_set_freq: change frequency
  * @spur_mitigate_freq: spur mitigation
- * @rf_alloc_ext_banks:
- * @rf_free_ext_banks:
  * @set_rf_regs:
  * @compute_pll_control: compute the PLL control value to use for
  *     AR_RTC_PLL_CONTROL for a given channel
@@ -620,7 +618,6 @@ struct ath_hw_private_ops {
        void (*init_cal_settings)(struct ath_hw *ah);
        bool (*init_cal)(struct ath_hw *ah, struct ath9k_channel *chan);
 
-       void (*init_mode_regs)(struct ath_hw *ah);
        void (*init_mode_gain_regs)(struct ath_hw *ah);
        void (*setup_calibration)(struct ath_hw *ah,
                                  struct ath9k_cal_list *currCal);
@@ -630,8 +627,6 @@ struct ath_hw_private_ops {
                           struct ath9k_channel *chan);
        void (*spur_mitigate_freq)(struct ath_hw *ah,
                                   struct ath9k_channel *chan);
-       int (*rf_alloc_ext_banks)(struct ath_hw *ah);
-       void (*rf_free_ext_banks)(struct ath_hw *ah);
        bool (*set_rf_regs)(struct ath_hw *ah,
                            struct ath9k_channel *chan,
                            u16 modesIndex);
@@ -660,6 +655,37 @@ struct ath_hw_private_ops {
        void (*ani_cache_ini_regs)(struct ath_hw *ah);
 };
 
+/**
+ * struct ath_spec_scan - parameters for Atheros spectral scan
+ *
+ * @enabled: enable/disable spectral scan
+ * @short_repeat: controls whether the chip is in spectral scan mode
+ *               for 4 usec (enabled) or 204 usec (disabled)
+ * @count: number of scan results requested. There are special meanings
+ *        in some chip revisions:
+ *        AR92xx: highest bit set (>=128) for endless mode
+ *                (spectral scan won't stopped until explicitly disabled)
+ *        AR9300 and newer: 0 for endless mode
+ * @endless: true if endless mode is intended. Otherwise, count value is
+ *           corrected to the next possible value.
+ * @period: time duration between successive spectral scan entry points
+ *         (period*256*Tclk). Tclk = ath_common->clockrate
+ * @fft_period: PHY passes FFT frames to MAC every (fft_period+1)*4uS
+ *
+ * Note: Tclk = 40MHz or 44MHz depending upon operating mode.
+ *      Typically it's 44MHz in 2/5GHz on later chips, but there's
+ *      a "fast clock" check for this in 5GHz.
+ *
+ */
+struct ath_spec_scan {
+       bool enabled;
+       bool short_repeat;
+       bool endless;
+       u8 count;
+       u8 period;
+       u8 fft_period;
+};
+
 /**
  * struct ath_hw_ops - callbacks used by hardware code and driver code
  *
@@ -668,6 +694,10 @@ struct ath_hw_private_ops {
  *
  * @config_pci_powersave:
  * @calibrate: periodic calibration for NF, ANI, IQ, ADC gain, ADC-DC
+ *
+ * @spectral_scan_config: set parameters for spectral scan and enable/disable it
+ * @spectral_scan_trigger: trigger a spectral scan run
+ * @spectral_scan_wait: wait for a spectral scan run to finish
  */
 struct ath_hw_ops {
        void (*config_pci_powersave)(struct ath_hw *ah,
@@ -688,6 +718,10 @@ struct ath_hw_ops {
        void (*antdiv_comb_conf_set)(struct ath_hw *ah,
                        struct ath_hw_antcomb_conf *antconf);
        void (*antctrl_shared_chain_lnadiv)(struct ath_hw *hw, bool enable);
+       void (*spectral_scan_config)(struct ath_hw *ah,
+                                    struct ath_spec_scan *param);
+       void (*spectral_scan_trigger)(struct ath_hw *ah);
+       void (*spectral_scan_wait)(struct ath_hw *ah);
 };
 
 struct ath_nf_limits {
@@ -710,6 +744,7 @@ enum ath_cal_list {
 struct ath_hw {
        struct ath_ops reg_ops;
 
+       struct device *dev;
        struct ieee80211_hw *hw;
        struct ath_common common;
        struct ath9k_hw_version hw_version;
@@ -771,7 +806,6 @@ struct ath_hw {
        struct ath9k_cal_list iq_caldata;
        struct ath9k_cal_list adcgain_caldata;
        struct ath9k_cal_list adcdc_caldata;
-       struct ath9k_cal_list tempCompCalData;
        struct ath9k_cal_list *cal_list;
        struct ath9k_cal_list *cal_list_last;
        struct ath9k_cal_list *cal_list_curr;
@@ -830,10 +864,6 @@ struct ath_hw {
        /* ANI */
        u32 proc_phyerr;
        u32 aniperiod;
-       int totalSizeDesired[5];
-       int coarse_high[5];
-       int coarse_low[5];
-       int firpwr[5];
        enum ath9k_ani_cmd ani_function;
        u32 ani_skip_count;
 
@@ -979,7 +1009,7 @@ void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna);
 void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan,
                          int hw_delay);
 bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout);
-void ath9k_hw_write_array(struct ath_hw *ah, struct ar5416IniArray *array,
+void ath9k_hw_write_array(struct ath_hw *ah, const struct ar5416IniArray *array,
                          int column, unsigned int *writecnt);
 u32 ath9k_hw_reverse_bits(u32 val, u32 n);
 u16 ath9k_hw_computetxtime(struct ath_hw *ah,
@@ -1069,14 +1099,14 @@ bool ar9003_is_paprd_enabled(struct ath_hw *ah);
 void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
 
 /* Hardware family op attach helpers */
-void ar5008_hw_attach_phy_ops(struct ath_hw *ah);
+int ar5008_hw_attach_phy_ops(struct ath_hw *ah);
 void ar9002_hw_attach_phy_ops(struct ath_hw *ah);
 void ar9003_hw_attach_phy_ops(struct ath_hw *ah);
 
 void ar9002_hw_attach_calib_ops(struct ath_hw *ah);
 void ar9003_hw_attach_calib_ops(struct ath_hw *ah);
 
-void ar9002_hw_attach_ops(struct ath_hw *ah);
+int ar9002_hw_attach_ops(struct ath_hw *ah);
 void ar9003_hw_attach_ops(struct ath_hw *ah);
 
 void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan);
index f69ef5d48c7b96119cb516259f41f2256f150747..af932c9444def08a14e8826948936ba33e73342c 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/slab.h>
 #include <linux/ath9k_platform.h>
 #include <linux/module.h>
+#include <linux/relay.h>
 
 #include "ath9k.h"
 
@@ -302,16 +303,15 @@ static void setup_ht_cap(struct ath_softc *sc,
        ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
 }
 
-static int ath9k_reg_notifier(struct wiphy *wiphy,
-                             struct regulatory_request *request)
+static void ath9k_reg_notifier(struct wiphy *wiphy,
+                              struct regulatory_request *request)
 {
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
-       int ret;
 
-       ret = ath_reg_notifier_apply(wiphy, request, reg);
+       ath_reg_notifier_apply(wiphy, request, reg);
 
        /* Set tx power */
        if (ah->curchan) {
@@ -321,8 +321,6 @@ static int ath9k_reg_notifier(struct wiphy *wiphy,
                sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
                ath9k_ps_restore(sc);
        }
-
-       return ret;
 }
 
 /*
@@ -337,7 +335,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        u8 *ds;
        struct ath_buf *bf;
-       int i, bsize, error, desc_len;
+       int i, bsize, desc_len;
 
        ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n",
                name, nbuf, ndesc);
@@ -353,8 +351,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
        if ((desc_len % 4) != 0) {
                ath_err(common, "ath_desc not DWORD aligned\n");
                BUG_ON((desc_len % 4) != 0);
-               error = -ENOMEM;
-               goto fail;
+               return -ENOMEM;
        }
 
        dd->dd_desc_len = desc_len * nbuf * ndesc;
@@ -378,12 +375,11 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
        }
 
        /* allocate descriptors */
-       dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len,
-                                        &dd->dd_desc_paddr, GFP_KERNEL);
-       if (dd->dd_desc == NULL) {
-               error = -ENOMEM;
-               goto fail;
-       }
+       dd->dd_desc = dmam_alloc_coherent(sc->dev, dd->dd_desc_len,
+                                         &dd->dd_desc_paddr, GFP_KERNEL);
+       if (!dd->dd_desc)
+               return -ENOMEM;
+
        ds = (u8 *) dd->dd_desc;
        ath_dbg(common, CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n",
                name, ds, (u32) dd->dd_desc_len,
@@ -391,12 +387,9 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 
        /* allocate buffers */
        bsize = sizeof(struct ath_buf) * nbuf;
-       bf = kzalloc(bsize, GFP_KERNEL);
-       if (bf == NULL) {
-               error = -ENOMEM;
-               goto fail2;
-       }
-       dd->dd_bufptr = bf;
+       bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
+       if (!bf)
+               return -ENOMEM;
 
        for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
                bf->bf_desc = ds;
@@ -422,12 +415,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
                list_add_tail(&bf->list, head);
        }
        return 0;
-fail2:
-       dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc,
-                         dd->dd_desc_paddr);
-fail:
-       memset(dd, 0, sizeof(*dd));
-       return error;
 }
 
 static int ath9k_init_queues(struct ath_softc *sc)
@@ -457,11 +444,13 @@ static int ath9k_init_channels_rates(struct ath_softc *sc)
                     ATH9K_NUM_CHANNELS);
 
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) {
-               channels = kmemdup(ath9k_2ghz_chantable,
+               channels = devm_kzalloc(sc->dev,
                        sizeof(ath9k_2ghz_chantable), GFP_KERNEL);
                if (!channels)
                    return -ENOMEM;
 
+               memcpy(channels, ath9k_2ghz_chantable,
+                      sizeof(ath9k_2ghz_chantable));
                sc->sbands[IEEE80211_BAND_2GHZ].channels = channels;
                sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
                sc->sbands[IEEE80211_BAND_2GHZ].n_channels =
@@ -472,14 +461,13 @@ static int ath9k_init_channels_rates(struct ath_softc *sc)
        }
 
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) {
-               channels = kmemdup(ath9k_5ghz_chantable,
+               channels = devm_kzalloc(sc->dev,
                        sizeof(ath9k_5ghz_chantable), GFP_KERNEL);
-               if (!channels) {
-                       if (sc->sbands[IEEE80211_BAND_2GHZ].channels)
-                               kfree(sc->sbands[IEEE80211_BAND_2GHZ].channels);
+               if (!channels)
                        return -ENOMEM;
-               }
 
+               memcpy(channels, ath9k_5ghz_chantable,
+                      sizeof(ath9k_5ghz_chantable));
                sc->sbands[IEEE80211_BAND_5GHZ].channels = channels;
                sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
                sc->sbands[IEEE80211_BAND_5GHZ].n_channels =
@@ -509,6 +497,13 @@ static void ath9k_init_misc(struct ath_softc *sc)
 
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
                sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT;
+
+       sc->spec_config.enabled = 0;
+       sc->spec_config.short_repeat = true;
+       sc->spec_config.count = 8;
+       sc->spec_config.endless = false;
+       sc->spec_config.period = 0xFF;
+       sc->spec_config.fft_period = 0xF;
 }
 
 static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob,
@@ -565,10 +560,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        int ret = 0, i;
        int csz = 0;
 
-       ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL);
+       ah = devm_kzalloc(sc->dev, sizeof(struct ath_hw), GFP_KERNEL);
        if (!ah)
                return -ENOMEM;
 
+       ah->dev = sc->dev;
        ah->hw = sc->hw;
        ah->hw_version.devid = devid;
        ah->reg_ops.read = ath9k_ioread32;
@@ -636,7 +632,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        if (pdata && pdata->eeprom_name) {
                ret = ath9k_eeprom_request(sc, pdata->eeprom_name);
                if (ret)
-                       goto err_eeprom;
+                       return ret;
        }
 
        /* Initializes the hardware for all supported chipsets */
@@ -676,10 +672,6 @@ err_queues:
        ath9k_hw_deinit(ah);
 err_hw:
        ath9k_eeprom_release(sc);
-err_eeprom:
-       kfree(ah);
-       sc->sc_ah = NULL;
-
        return ret;
 }
 
@@ -844,8 +836,8 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
 
        /* Bring up device */
        error = ath9k_init_softc(devid, sc, bus_ops);
-       if (error != 0)
-               goto error_init;
+       if (error)
+               return error;
 
        ah = sc->sc_ah;
        common = ath9k_hw_common(ah);
@@ -855,19 +847,19 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
        error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
                              ath9k_reg_notifier);
        if (error)
-               goto error_regd;
+               goto deinit;
 
        reg = &common->regulatory;
 
        /* Setup TX DMA */
        error = ath_tx_init(sc, ATH_TXBUF);
        if (error != 0)
-               goto error_tx;
+               goto deinit;
 
        /* Setup RX DMA */
        error = ath_rx_init(sc, ATH_RXBUF);
        if (error != 0)
-               goto error_rx;
+               goto deinit;
 
        ath9k_init_txpower_limits(sc);
 
@@ -881,19 +873,19 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
        /* Register with mac80211 */
        error = ieee80211_register_hw(hw);
        if (error)
-               goto error_register;
+               goto rx_cleanup;
 
        error = ath9k_init_debug(ah);
        if (error) {
                ath_err(common, "Unable to create debugfs files\n");
-               goto error_world;
+               goto unregister;
        }
 
        /* Handle world regulatory */
        if (!ath_is_world_regd(reg)) {
                error = regulatory_hint(hw->wiphy, reg->alpha2);
                if (error)
-                       goto error_world;
+                       goto unregister;
        }
 
        ath_init_leds(sc);
@@ -901,17 +893,12 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
 
        return 0;
 
-error_world:
+unregister:
        ieee80211_unregister_hw(hw);
-error_register:
+rx_cleanup:
        ath_rx_cleanup(sc);
-error_rx:
-       ath_tx_cleanup(sc);
-error_tx:
-       /* Nothing */
-error_regd:
+deinit:
        ath9k_deinit_softc(sc);
-error_init:
        return error;
 }
 
@@ -923,12 +910,6 @@ static void ath9k_deinit_softc(struct ath_softc *sc)
 {
        int i = 0;
 
-       if (sc->sbands[IEEE80211_BAND_2GHZ].channels)
-               kfree(sc->sbands[IEEE80211_BAND_2GHZ].channels);
-
-       if (sc->sbands[IEEE80211_BAND_5GHZ].channels)
-               kfree(sc->sbands[IEEE80211_BAND_5GHZ].channels);
-
        ath9k_deinit_btcoex(sc);
 
        for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
@@ -940,8 +921,11 @@ static void ath9k_deinit_softc(struct ath_softc *sc)
                sc->dfs_detector->exit(sc->dfs_detector);
 
        ath9k_eeprom_release(sc);
-       kfree(sc->sc_ah);
-       sc->sc_ah = NULL;
+
+       if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) {
+               relay_close(sc->rfs_chan_spec_scan);
+               sc->rfs_chan_spec_scan = NULL;
+       }
 }
 
 void ath9k_deinit_device(struct ath_softc *sc)
@@ -957,22 +941,9 @@ void ath9k_deinit_device(struct ath_softc *sc)
 
        ieee80211_unregister_hw(hw);
        ath_rx_cleanup(sc);
-       ath_tx_cleanup(sc);
        ath9k_deinit_softc(sc);
 }
 
-void ath_descdma_cleanup(struct ath_softc *sc,
-                        struct ath_descdma *dd,
-                        struct list_head *head)
-{
-       dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc,
-                         dd->dd_desc_paddr);
-
-       INIT_LIST_HEAD(head);
-       kfree(dd->dd_bufptr);
-       memset(dd, 0, sizeof(*dd));
-}
-
 /************************/
 /*     Module Hooks     */
 /************************/
index b42be910a83dc94845fa6be33370357b18416e2f..811007ec07a7528ce296ca98148104d23e60c4ce 100644 (file)
@@ -605,13 +605,13 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
                 * reported, then decryption and MIC errors are irrelevant,
                 * the frame is going to be dropped either way
                 */
-               if (ads.ds_rxstatus8 & AR_CRCErr)
-                       rs->rs_status |= ATH9K_RXERR_CRC;
-               else if (ads.ds_rxstatus8 & AR_PHYErr) {
+               if (ads.ds_rxstatus8 & AR_PHYErr) {
                        rs->rs_status |= ATH9K_RXERR_PHY;
                        phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode);
                        rs->rs_phyerr = phyerr;
-               } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr)
+               } else if (ads.ds_rxstatus8 & AR_CRCErr)
+                       rs->rs_status |= ATH9K_RXERR_CRC;
+               else if (ads.ds_rxstatus8 & AR_DecryptCRCErr)
                        rs->rs_status |= ATH9K_RXERR_DECRYPT;
                else if (ads.ds_rxstatus8 & AR_MichaelErr)
                        rs->rs_status |= ATH9K_RXERR_MIC;
index 4a745e68dd941d9351e4fe911b47d76ec288551d..1ff817061ebc44e07d1af87edb4d9ce471980bb2 100644 (file)
@@ -226,7 +226,8 @@ enum ath9k_phyerr {
        ATH9K_PHYERR_HT_LENGTH_ILLEGAL    = 35,
        ATH9K_PHYERR_HT_RATE_ILLEGAL      = 36,
 
-       ATH9K_PHYERR_MAX                  = 37,
+       ATH9K_PHYERR_SPECTRAL             = 38,
+       ATH9K_PHYERR_MAX                  = 39,
 };
 
 struct ath_desc {
index dd91f8fdc01c3ea44c922bbbbd14bee2df4fd6db..5432f1247e2e437fdcc10b56adc40e28a82b101d 100644 (file)
@@ -182,7 +182,7 @@ static void ath_restart_work(struct ath_softc *sc)
        ath_start_ani(sc);
 }
 
-static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx)
+static bool ath_prepare_reset(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
        bool ret = true;
@@ -196,10 +196,10 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx)
        ath9k_debug_samp_bb_mac(sc);
        ath9k_hw_disable_interrupts(ah);
 
-       if (!ath_stoprecv(sc))
+       if (!ath_drain_all_txq(sc))
                ret = false;
 
-       if (!ath_drain_all_txq(sc, retry_tx))
+       if (!ath_stoprecv(sc))
                ret = false;
 
        return ret;
@@ -247,8 +247,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
        return true;
 }
 
-static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
-                             bool retry_tx)
+static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
@@ -271,7 +270,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
                hchan = ah->curchan;
        }
 
-       if (!ath_prepare_reset(sc, retry_tx))
+       if (!ath_prepare_reset(sc))
                fastcc = false;
 
        ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n",
@@ -312,7 +311,7 @@ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
        if (test_bit(SC_OP_INVALID, &sc->sc_flags))
                return -EIO;
 
-       r = ath_reset_internal(sc, hchan, false);
+       r = ath_reset_internal(sc, hchan);
 
        return r;
 }
@@ -542,23 +541,21 @@ chip_reset:
 #undef SCHED_INTR
 }
 
-static int ath_reset(struct ath_softc *sc, bool retry_tx)
+static int ath_reset(struct ath_softc *sc)
 {
-       int r;
+       int i, r;
 
        ath9k_ps_wakeup(sc);
 
-       r = ath_reset_internal(sc, NULL, retry_tx);
+       r = ath_reset_internal(sc, NULL);
 
-       if (retry_tx) {
-               int i;
-               for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
-                       if (ATH_TXQ_SETUP(sc, i)) {
-                               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);
-                       }
-               }
+       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);
@@ -579,7 +576,7 @@ void ath_reset_work(struct work_struct *work)
 {
        struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work);
 
-       ath_reset(sc, true);
+       ath_reset(sc);
 }
 
 /**********************/
@@ -797,7 +794,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
                ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
        }
 
-       ath_prepare_reset(sc, false);
+       ath_prepare_reset(sc);
 
        if (sc->rx.frag) {
                dev_kfree_skb_any(sc->rx.frag);
@@ -1068,6 +1065,75 @@ static void ath9k_disable_ps(struct ath_softc *sc)
        ath_dbg(common, PS, "PowerSave disabled\n");
 }
 
+void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       u32 rxfilter;
+
+       if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
+               ath_err(common, "spectrum analyzer not implemented on this hardware\n");
+               return;
+       }
+
+       ath9k_ps_wakeup(sc);
+       rxfilter = ath9k_hw_getrxfilter(ah);
+       ath9k_hw_setrxfilter(ah, rxfilter |
+                                ATH9K_RX_FILTER_PHYRADAR |
+                                ATH9K_RX_FILTER_PHYERR);
+
+       /* TODO: usually this should not be neccesary, but for some reason
+        * (or in some mode?) the trigger must be called after the
+        * configuration, otherwise the register will have its values reset
+        * (on my ar9220 to value 0x01002310)
+        */
+       ath9k_spectral_scan_config(hw, sc->spectral_mode);
+       ath9k_hw_ops(ah)->spectral_scan_trigger(ah);
+       ath9k_ps_restore(sc);
+}
+
+int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
+                              enum spectral_mode spectral_mode)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
+               ath_err(common, "spectrum analyzer not implemented on this hardware\n");
+               return -1;
+       }
+
+       switch (spectral_mode) {
+       case SPECTRAL_DISABLED:
+               sc->spec_config.enabled = 0;
+               break;
+       case SPECTRAL_BACKGROUND:
+               /* send endless samples.
+                * TODO: is this really useful for "background"?
+                */
+               sc->spec_config.endless = 1;
+               sc->spec_config.enabled = 1;
+               break;
+       case SPECTRAL_CHANSCAN:
+       case SPECTRAL_MANUAL:
+               sc->spec_config.endless = 0;
+               sc->spec_config.enabled = 1;
+               break;
+       default:
+               return -1;
+       }
+
+       ath9k_ps_wakeup(sc);
+       ath9k_hw_ops(ah)->spectral_scan_config(ah, &sc->spec_config);
+       ath9k_ps_restore(sc);
+
+       sc->spectral_mode = spectral_mode;
+
+       return 0;
+}
+
 static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct ath_softc *sc = hw->priv;
@@ -1181,6 +1247,11 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                 */
                if (old_pos >= 0)
                        ath_update_survey_nf(sc, old_pos);
+
+               /* perform spectral scan if requested. */
+               if (sc->scanning && sc->spectral_mode == SPECTRAL_CHANSCAN)
+                       ath9k_spectral_scan_trigger(hw);
+
        }
 
        if (changed & IEEE80211_CONF_CHANGE_POWER) {
@@ -1603,7 +1674,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
                        ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                ath9k_ps_restore(sc);
                break;
-       case IEEE80211_AMPDU_TX_STOP:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
                ath9k_ps_wakeup(sc);
                ath_tx_aggr_stop(sc, sta, tid);
                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
@@ -1722,11 +1795,11 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
        if (drop) {
                ath9k_ps_wakeup(sc);
                spin_lock_bh(&sc->sc_pcu_lock);
-               drain_txq = ath_drain_all_txq(sc, false);
+               drain_txq = ath_drain_all_txq(sc);
                spin_unlock_bh(&sc->sc_pcu_lock);
 
                if (!drain_txq)
-                       ath_reset(sc, false);
+                       ath_reset(sc);
 
                ath9k_ps_restore(sc);
                ieee80211_wake_queues(hw);
@@ -2234,6 +2307,19 @@ static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
 }
 
 #endif
+static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
+{
+       struct ath_softc *sc = hw->priv;
+
+       sc->scanning = 1;
+}
+
+static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
+{
+       struct ath_softc *sc = hw->priv;
+
+       sc->scanning = 0;
+}
 
 struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
@@ -2280,4 +2366,6 @@ struct ieee80211_ops ath9k_ops = {
        .sta_add_debugfs    = ath9k_sta_add_debugfs,
        .sta_remove_debugfs = ath9k_sta_remove_debugfs,
 #endif
+       .sw_scan_start      = ath9k_sw_scan_start,
+       .sw_scan_complete   = ath9k_sw_scan_complete,
 };
index 5c02702f21e7e1d3508b493ca89499b0226ce2c4..815bee21c19a0fe1aeb2194fbdba2372be2b279b 100644 (file)
@@ -438,7 +438,7 @@ int ath_mci_setup(struct ath_softc *sc)
        struct ath_mci_buf *buf = &mci->sched_buf;
        int ret;
 
-       buf->bf_addr = dma_alloc_coherent(sc->dev,
+       buf->bf_addr = dmam_alloc_coherent(sc->dev,
                                  ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE,
                                  &buf->bf_paddr, GFP_KERNEL);
 
@@ -474,13 +474,6 @@ void ath_mci_cleanup(struct ath_softc *sc)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_hw *ah = sc->sc_ah;
-       struct ath_mci_coex *mci = &sc->mci_coex;
-       struct ath_mci_buf *buf = &mci->sched_buf;
-
-       if (buf->bf_addr)
-               dma_free_coherent(sc->dev,
-                                 ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE,
-                                 buf->bf_addr, buf->bf_paddr);
 
        ar9003_mci_cleanup(ah);
 
index 7ae73fbd91361092f1a0590be0cbc2e2b0ecc9a4..0e0d39583837219a60a374d69494933b0ffc4e43 100644 (file)
@@ -147,7 +147,6 @@ static const struct ath_bus_ops ath_pci_bus_ops = {
 
 static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
-       void __iomem *mem;
        struct ath_softc *sc;
        struct ieee80211_hw *hw;
        u8 csz;
@@ -155,19 +154,19 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        int ret = 0;
        char hw_name[64];
 
-       if (pci_enable_device(pdev))
+       if (pcim_enable_device(pdev))
                return -EIO;
 
        ret =  pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
        if (ret) {
                pr_err("32-bit DMA not available\n");
-               goto err_dma;
+               return ret;
        }
 
        ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
        if (ret) {
                pr_err("32-bit DMA consistent DMA enable failed\n");
-               goto err_dma;
+               return ret;
        }
 
        /*
@@ -203,25 +202,16 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if ((val & 0x0000ff00) != 0)
                pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
 
-       ret = pci_request_region(pdev, 0, "ath9k");
+       ret = pcim_iomap_regions(pdev, BIT(0), "ath9k");
        if (ret) {
                dev_err(&pdev->dev, "PCI memory region reserve error\n");
-               ret = -ENODEV;
-               goto err_region;
-       }
-
-       mem = pci_iomap(pdev, 0, 0);
-       if (!mem) {
-               pr_err("PCI memory map error\n") ;
-               ret = -EIO;
-               goto err_iomap;
+               return -ENODEV;
        }
 
        hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
        if (!hw) {
                dev_err(&pdev->dev, "No memory for ieee80211_hw\n");
-               ret = -ENOMEM;
-               goto err_alloc_hw;
+               return -ENOMEM;
        }
 
        SET_IEEE80211_DEV(hw, &pdev->dev);
@@ -230,7 +220,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        sc = hw->priv;
        sc->hw = hw;
        sc->dev = &pdev->dev;
-       sc->mem = mem;
+       sc->mem = pcim_iomap_table(pdev)[0];
 
        /* Will be cleared in ath9k_start() */
        set_bit(SC_OP_INVALID, &sc->sc_flags);
@@ -251,7 +241,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        ath9k_hw_name(sc->sc_ah, hw_name, sizeof(hw_name));
        wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n",
-                  hw_name, (unsigned long)mem, pdev->irq);
+                  hw_name, (unsigned long)sc->mem, pdev->irq);
 
        return 0;
 
@@ -259,14 +249,6 @@ err_init:
        free_irq(sc->irq, sc);
 err_irq:
        ieee80211_free_hw(hw);
-err_alloc_hw:
-       pci_iounmap(pdev, mem);
-err_iomap:
-       pci_release_region(pdev, 0);
-err_region:
-       /* Nothing */
-err_dma:
-       pci_disable_device(pdev);
        return ret;
 }
 
@@ -274,17 +256,12 @@ static void ath_pci_remove(struct pci_dev *pdev)
 {
        struct ieee80211_hw *hw = pci_get_drvdata(pdev);
        struct ath_softc *sc = hw->priv;
-       void __iomem *mem = sc->mem;
 
        if (!is_ath9k_unloaded)
                sc->sc_ah->ah_flags |= AH_UNPLUGGED;
        ath9k_deinit_device(sc);
        free_irq(sc->irq, sc);
        ieee80211_free_hw(sc->hw);
-
-       pci_iounmap(pdev, mem);
-       pci_disable_device(pdev);
-       pci_release_region(pdev, 0);
 }
 
 #ifdef CONFIG_PM_SLEEP
index 90752f2469704bbbb2bf7cae86a1c2cb4029435a..2d0fd17a1917018c435c4eb64aa4e9cf529ef305 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <linux/dma-mapping.h>
+#include <linux/relay.h>
 #include "ath9k.h"
 #include "ar9003_mac.h"
 
@@ -180,11 +181,6 @@ static void ath_rx_edma_cleanup(struct ath_softc *sc)
                        bf->bf_mpdu = NULL;
                }
        }
-
-       INIT_LIST_HEAD(&sc->rx.rxbuf);
-
-       kfree(sc->rx.rx_bufptr);
-       sc->rx.rx_bufptr = NULL;
 }
 
 static void ath_rx_edma_init_queue(struct ath_rx_edma *rx_edma, int size)
@@ -211,12 +207,11 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
                               ah->caps.rx_hp_qdepth);
 
        size = sizeof(struct ath_buf) * nbufs;
-       bf = kzalloc(size, GFP_KERNEL);
+       bf = devm_kzalloc(sc->dev, size, GFP_KERNEL);
        if (!bf)
                return -ENOMEM;
 
        INIT_LIST_HEAD(&sc->rx.rxbuf);
-       sc->rx.rx_bufptr = bf;
 
        for (i = 0; i < nbufs; i++, bf++) {
                skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_KERNEL);
@@ -357,9 +352,6 @@ void ath_rx_cleanup(struct ath_softc *sc)
                                bf->bf_mpdu = NULL;
                        }
                }
-
-               if (sc->rx.rxdma.dd_desc_len != 0)
-                       ath_descdma_cleanup(sc, &sc->rx.rxdma, &sc->rx.rxbuf);
        }
 }
 
@@ -1024,6 +1016,134 @@ static void ath9k_rx_skb_postprocess(struct ath_common *common,
                rxs->flag &= ~RX_FLAG_DECRYPTED;
 }
 
+#ifdef CONFIG_ATH9K_DEBUGFS
+static s8 fix_rssi_inv_only(u8 rssi_val)
+{
+       if (rssi_val == 128)
+               rssi_val = 0;
+       return (s8) rssi_val;
+}
+#endif
+
+/* returns 1 if this was a spectral frame, even if not handled. */
+static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
+                          struct ath_rx_status *rs, u64 tsf)
+{
+#ifdef CONFIG_ATH9K_DEBUGFS
+       struct ath_hw *ah = sc->sc_ah;
+       u8 bins[SPECTRAL_HT20_NUM_BINS];
+       u8 *vdata = (u8 *)hdr;
+       struct fft_sample_ht20 fft_sample;
+       struct ath_radar_info *radar_info;
+       struct ath_ht20_mag_info *mag_info;
+       int len = rs->rs_datalen;
+       int dc_pos;
+       u16 length, max_magnitude;
+
+       /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
+        * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
+        * yet, but this is supposed to be possible as well.
+        */
+       if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&
+           rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&
+           rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)
+               return 0;
+
+       /* check if spectral scan bit is set. This does not have to be checked
+        * if received through a SPECTRAL phy error, but shouldn't hurt.
+        */
+       radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;
+       if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
+               return 0;
+
+       /* Variation in the data length is possible and will be fixed later.
+        * Note that we only support HT20 for now.
+        *
+        * TODO: add HT20_40 support as well.
+        */
+       if ((len > SPECTRAL_HT20_TOTAL_DATA_LEN + 2) ||
+           (len < SPECTRAL_HT20_TOTAL_DATA_LEN - 1))
+               return 1;
+
+       fft_sample.tlv.type = ATH_FFT_SAMPLE_HT20;
+       length = sizeof(fft_sample) - sizeof(fft_sample.tlv);
+       fft_sample.tlv.length = __cpu_to_be16(length);
+
+       fft_sample.freq = __cpu_to_be16(ah->curchan->chan->center_freq);
+       fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
+       fft_sample.noise = ah->noise;
+
+       switch (len - SPECTRAL_HT20_TOTAL_DATA_LEN) {
+       case 0:
+               /* length correct, nothing to do. */
+               memcpy(bins, vdata, SPECTRAL_HT20_NUM_BINS);
+               break;
+       case -1:
+               /* first byte missing, duplicate it. */
+               memcpy(&bins[1], vdata, SPECTRAL_HT20_NUM_BINS - 1);
+               bins[0] = vdata[0];
+               break;
+       case 2:
+               /* MAC added 2 extra bytes at bin 30 and 32, remove them. */
+               memcpy(bins, vdata, 30);
+               bins[30] = vdata[31];
+               memcpy(&bins[31], &vdata[33], SPECTRAL_HT20_NUM_BINS - 31);
+               break;
+       case 1:
+               /* MAC added 2 extra bytes AND first byte is missing. */
+               bins[0] = vdata[0];
+               memcpy(&bins[0], vdata, 30);
+               bins[31] = vdata[31];
+               memcpy(&bins[32], &vdata[33], SPECTRAL_HT20_NUM_BINS - 32);
+               break;
+       default:
+               return 1;
+       }
+
+       /* DC value (value in the middle) is the blind spot of the spectral
+        * sample and invalid, interpolate it.
+        */
+       dc_pos = SPECTRAL_HT20_NUM_BINS / 2;
+       bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2;
+
+       /* mag data is at the end of the frame, in front of radar_info */
+       mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
+
+       /* copy raw bins without scaling them */
+       memcpy(fft_sample.data, bins, SPECTRAL_HT20_NUM_BINS);
+       fft_sample.max_exp = mag_info->max_exp & 0xf;
+
+       max_magnitude = spectral_max_magnitude(mag_info->all_bins);
+       fft_sample.max_magnitude = __cpu_to_be16(max_magnitude);
+       fft_sample.max_index = spectral_max_index(mag_info->all_bins);
+       fft_sample.bitmap_weight = spectral_bitmap_weight(mag_info->all_bins);
+       fft_sample.tsf = __cpu_to_be64(tsf);
+
+       ath_debug_send_fft_sample(sc, &fft_sample.tlv);
+       return 1;
+#else
+       return 0;
+#endif
+}
+
+static void ath9k_apply_ampdu_details(struct ath_softc *sc,
+       struct ath_rx_status *rs, struct ieee80211_rx_status *rxs)
+{
+       if (rs->rs_isaggr) {
+               rxs->flag |= RX_FLAG_AMPDU_DETAILS | RX_FLAG_AMPDU_LAST_KNOWN;
+
+               rxs->ampdu_reference = sc->rx.ampdu_ref;
+
+               if (!rs->rs_moreaggr) {
+                       rxs->flag |= RX_FLAG_AMPDU_IS_LAST;
+                       sc->rx.ampdu_ref++;
+               }
+
+               if (rs->rs_flags & ATH9K_RX_DELIM_CRC_PRE)
+                       rxs->flag |= RX_FLAG_AMPDU_DELIM_CRC_ERROR;
+       }
+}
+
 int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 {
        struct ath_buf *bf;
@@ -1108,6 +1228,13 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                    unlikely(tsf_lower - rs.rs_tstamp > 0x10000000))
                        rxs->mactime += 0x100000000ULL;
 
+               if (rs.rs_status & ATH9K_RXERR_PHY) {
+                       if (ath_process_fft(sc, hdr, &rs, rxs->mactime)) {
+                               RX_STAT_INC(rx_spectral);
+                               goto requeue_drop_frag;
+                       }
+               }
+
                retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs,
                                                 rxs, &decrypt_error);
                if (retval)
@@ -1223,6 +1350,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3)
                        ath_ant_comb_scan(sc, &rs);
 
+               ath9k_apply_ampdu_details(sc, &rs, rxs);
+
                ieee80211_rx(hw, skb);
 
 requeue_drop_frag:
index ad3c82c091775a7011f7a0638da7522e15685c83..5929850649f0ee45a024a8328d6c76c17b5127bb 100644 (file)
 #define AR_SREV_REVISION_9271_11       1
 #define AR_SREV_VERSION_9300           0x1c0
 #define AR_SREV_REVISION_9300_20       2 /* 2.0 and 2.1 */
+#define AR_SREV_REVISION_9300_22       3
 #define AR_SREV_VERSION_9330           0x200
 #define AR_SREV_REVISION_9330_10       0
 #define AR_SREV_REVISION_9330_11       1
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9300))
 #define AR_SREV_9300_20_OR_LATER(_ah) \
        ((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9300)
+#define AR_SREV_9300_22(_ah) \
+       (AR_SREV_9300(ah) && \
+        ((_ah)->hw_version.macRev == AR_SREV_REVISION_9300_22))
 
 #define AR_SREV_9330(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9330))
 
 #define AR_SREV_9485(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485))
-#define AR_SREV_9485_10(_ah) \
-       (AR_SREV_9485(_ah) && \
-        ((_ah)->hw_version.macRev == AR_SREV_REVISION_9485_10))
 #define AR_SREV_9485_11(_ah) \
        (AR_SREV_9485(_ah) && \
         ((_ah)->hw_version.macRev == AR_SREV_REVISION_9485_11))
index 90e48a0fafe5a3ca6e12be66e6b0402bdf7feaf0..feacaafee959b358d60b8cc03f43f20c7e05c71c 100644 (file)
@@ -378,7 +378,7 @@ static void ath_tx_count_frames(struct ath_softc *sc, struct ath_buf *bf,
 
 static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                                 struct ath_buf *bf, struct list_head *bf_q,
-                                struct ath_tx_status *ts, int txok, bool retry)
+                                struct ath_tx_status *ts, int txok)
 {
        struct ath_node *an = NULL;
        struct sk_buff *skb;
@@ -490,7 +490,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                } else if (!isaggr && txok) {
                        /* transmit completion */
                        acked_cnt++;
-               } else if ((tid->state & AGGR_CLEANUP) || !retry) {
+               } else if (tid->state & AGGR_CLEANUP) {
                        /*
                         * cleanup in progress, just fail
                         * the un-acked sub-frames
@@ -604,6 +604,37 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                ath9k_queue_reset(sc, RESET_TYPE_TX_ERROR);
 }
 
+static bool bf_is_ampdu_not_probing(struct ath_buf *bf)
+{
+    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(bf->bf_mpdu);
+    return bf_isampdu(bf) && !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE);
+}
+
+static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq,
+                                 struct ath_tx_status *ts, struct ath_buf *bf,
+                                 struct list_head *bf_head)
+{
+       bool txok, flush;
+
+       txok = !(ts->ts_status & ATH9K_TXERR_MASK);
+       flush = !!(ts->ts_status & ATH9K_TX_FLUSH);
+       txq->axq_tx_inprogress = false;
+
+       txq->axq_depth--;
+       if (bf_is_ampdu_not_probing(bf))
+               txq->axq_ampdu_depth--;
+
+       if (!bf_isampdu(bf)) {
+               if (!flush)
+                       ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok);
+               ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok);
+       } else
+               ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok);
+
+       if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) && !flush)
+               ath_txq_schedule(sc, txq);
+}
+
 static bool ath_lookup_legacy(struct ath_buf *bf)
 {
        struct sk_buff *skb;
@@ -1331,23 +1362,6 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid
 /* Queue Management */
 /********************/
 
-static void ath_txq_drain_pending_buffers(struct ath_softc *sc,
-                                         struct ath_txq *txq)
-{
-       struct ath_atx_ac *ac, *ac_tmp;
-       struct ath_atx_tid *tid, *tid_tmp;
-
-       list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq, list) {
-               list_del(&ac->list);
-               ac->sched = false;
-               list_for_each_entry_safe(tid, tid_tmp, &ac->tid_q, list) {
-                       list_del(&tid->list);
-                       tid->sched = false;
-                       ath_tid_drain(sc, txq, tid);
-               }
-       }
-}
-
 struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
 {
        struct ath_hw *ah = sc->sc_ah;
@@ -1470,14 +1484,8 @@ int ath_cabq_update(struct ath_softc *sc)
        return 0;
 }
 
-static bool bf_is_ampdu_not_probing(struct ath_buf *bf)
-{
-    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(bf->bf_mpdu);
-    return bf_isampdu(bf) && !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE);
-}
-
 static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq,
-                              struct list_head *list, bool retry_tx)
+                              struct list_head *list)
 {
        struct ath_buf *bf, *lastbf;
        struct list_head bf_head;
@@ -1499,16 +1507,7 @@ static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq,
 
                lastbf = bf->bf_lastbf;
                list_cut_position(&bf_head, list, &lastbf->list);
-
-               txq->axq_depth--;
-               if (bf_is_ampdu_not_probing(bf))
-                       txq->axq_ampdu_depth--;
-
-               if (bf_isampdu(bf))
-                       ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, 0,
-                                            retry_tx);
-               else
-                       ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
+               ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head);
        }
 }
 
@@ -1518,7 +1517,7 @@ static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq,
  * This assumes output has been stopped and
  * we do not need to block ath_tx_tasklet.
  */
-void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
+void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq)
 {
        ath_txq_lock(sc, txq);
 
@@ -1526,8 +1525,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
                int idx = txq->txq_tailidx;
 
                while (!list_empty(&txq->txq_fifo[idx])) {
-                       ath_drain_txq_list(sc, txq, &txq->txq_fifo[idx],
-                                          retry_tx);
+                       ath_drain_txq_list(sc, txq, &txq->txq_fifo[idx]);
 
                        INCR(idx, ATH_TXFIFO_DEPTH);
                }
@@ -1536,16 +1534,12 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
 
        txq->axq_link = NULL;
        txq->axq_tx_inprogress = false;
-       ath_drain_txq_list(sc, txq, &txq->axq_q, retry_tx);
-
-       /* flush any pending frames if aggregation is enabled */
-       if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) && !retry_tx)
-               ath_txq_drain_pending_buffers(sc, txq);
+       ath_drain_txq_list(sc, txq, &txq->axq_q);
 
        ath_txq_unlock_complete(sc, txq);
 }
 
-bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
+bool ath_drain_all_txq(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -1581,7 +1575,7 @@ bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
                 */
                txq = &sc->tx.txq[i];
                txq->stopped = false;
-               ath_draintxq(sc, txq, retry_tx);
+               ath_draintxq(sc, txq);
        }
 
        return !npend;
@@ -2175,28 +2169,6 @@ static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf,
        tx_info->status.rates[tx_rateindex].count = ts->ts_longretry + 1;
 }
 
-static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq,
-                                 struct ath_tx_status *ts, struct ath_buf *bf,
-                                 struct list_head *bf_head)
-{
-       int txok;
-
-       txq->axq_depth--;
-       txok = !(ts->ts_status & ATH9K_TXERR_MASK);
-       txq->axq_tx_inprogress = false;
-       if (bf_is_ampdu_not_probing(bf))
-               txq->axq_ampdu_depth--;
-
-       if (!bf_isampdu(bf)) {
-               ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok);
-               ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok);
-       } else
-               ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok, true);
-
-       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)
-               ath_txq_schedule(sc, txq);
-}
-
 static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
 {
        struct ath_hw *ah = sc->sc_ah;
@@ -2361,8 +2333,8 @@ static int ath_txstatus_setup(struct ath_softc *sc, int size)
        u8 txs_len = sc->sc_ah->caps.txs_len;
 
        dd->dd_desc_len = size * txs_len;
-       dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len,
-                                        &dd->dd_desc_paddr, GFP_KERNEL);
+       dd->dd_desc = dmam_alloc_coherent(sc->dev, dd->dd_desc_len,
+                                         &dd->dd_desc_paddr, GFP_KERNEL);
        if (!dd->dd_desc)
                return -ENOMEM;
 
@@ -2382,14 +2354,6 @@ static int ath_tx_edma_init(struct ath_softc *sc)
        return err;
 }
 
-static void ath_tx_edma_cleanup(struct ath_softc *sc)
-{
-       struct ath_descdma *dd = &sc->txsdma;
-
-       dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc,
-                         dd->dd_desc_paddr);
-}
-
 int ath_tx_init(struct ath_softc *sc, int nbufs)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -2402,7 +2366,7 @@ int ath_tx_init(struct ath_softc *sc, int nbufs)
        if (error != 0) {
                ath_err(common,
                        "Failed to allocate tx descriptors: %d\n", error);
-               goto err;
+               return error;
        }
 
        error = ath_descdma_setup(sc, &sc->beacon.bdma, &sc->beacon.bbuf,
@@ -2410,36 +2374,17 @@ int ath_tx_init(struct ath_softc *sc, int nbufs)
        if (error != 0) {
                ath_err(common,
                        "Failed to allocate beacon descriptors: %d\n", error);
-               goto err;
+               return error;
        }
 
        INIT_DELAYED_WORK(&sc->tx_complete_work, ath_tx_complete_poll_work);
 
-       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
+       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
                error = ath_tx_edma_init(sc);
-               if (error)
-                       goto err;
-       }
-
-err:
-       if (error != 0)
-               ath_tx_cleanup(sc);
 
        return error;
 }
 
-void ath_tx_cleanup(struct ath_softc *sc)
-{
-       if (sc->beacon.bdma.dd_desc_len != 0)
-               ath_descdma_cleanup(sc, &sc->beacon.bdma, &sc->beacon.bbuf);
-
-       if (sc->tx.txdma.dd_desc_len != 0)
-               ath_descdma_cleanup(sc, &sc->tx.txdma, &sc->tx.txbuf);
-
-       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
-               ath_tx_edma_cleanup(sc);
-}
-
 void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an)
 {
        struct ath_atx_tid *tid;
index 2df17f1e49efca2ddb24f38a0145c4ce73225111..25599741cd8a5863fe3879afa288f07b05ead23a 100644 (file)
@@ -85,20 +85,14 @@ enum carl9170_device_state {
        CARL9170_STARTED,
 };
 
-#define CARL9170_NUM_TID               16
 #define WME_BA_BMP_SIZE                        64
 #define CARL9170_TX_USER_RATE_TRIES    3
 
-#define WME_AC_BE   2
-#define WME_AC_BK   3
-#define WME_AC_VI   1
-#define WME_AC_VO   0
-
 #define TID_TO_WME_AC(_tid)                            \
-       ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \
-        (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \
-        (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \
-        WME_AC_VO)
+       ((((_tid) == 0) || ((_tid) == 3)) ? IEEE80211_AC_BE :   \
+        (((_tid) == 1) || ((_tid) == 2)) ? IEEE80211_AC_BK :   \
+        (((_tid) == 4) || ((_tid) == 5)) ? IEEE80211_AC_VI :   \
+        IEEE80211_AC_VO)
 
 #define SEQ_DIFF(_start, _seq) \
        (((_start) - (_seq)) & 0x0fff)
@@ -290,6 +284,7 @@ struct ar9170 {
                unsigned int rx_size;
                unsigned int tx_seq_table;
                bool ba_filter;
+               bool disable_offload_fw;
        } fw;
 
        /* interface configuration combinations */
@@ -493,8 +488,8 @@ struct carl9170_sta_info {
        bool sleeping;
        atomic_t pending_frames;
        unsigned int ampdu_max_len;
-       struct carl9170_sta_tid __rcu *agg[CARL9170_NUM_TID];
-       struct carl9170_ba_stats stats[CARL9170_NUM_TID];
+       struct carl9170_sta_tid __rcu *agg[IEEE80211_NUM_TIDS];
+       struct carl9170_ba_stats stats[IEEE80211_NUM_TIDS];
 };
 
 struct carl9170_tx_info {
index 63fd9af3fd39dd2c1d7ddb183902ae5e587b1ada..47d5c2e910ad834d81c8c22fe368e191be3d6e32 100644 (file)
@@ -215,6 +215,24 @@ static int carl9170_fw_tx_sequence(struct ar9170 *ar)
        return 0;
 }
 
+static void carl9170_fw_set_if_combinations(struct ar9170 *ar,
+                                           u16 if_comb_types)
+{
+       if (ar->fw.vif_num < 2)
+               return;
+
+       ar->if_comb_limits[0].max = ar->fw.vif_num;
+       ar->if_comb_limits[0].types = if_comb_types;
+
+       ar->if_combs[0].num_different_channels = 1;
+       ar->if_combs[0].max_interfaces = ar->fw.vif_num;
+       ar->if_combs[0].limits = ar->if_comb_limits;
+       ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits);
+
+       ar->hw->wiphy->iface_combinations = ar->if_combs;
+       ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs);
+}
+
 static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
 {
        const struct carl9170fw_otus_desc *otus_desc;
@@ -264,7 +282,7 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
        if (!SUPP(CARL9170FW_COMMAND_CAM)) {
                dev_info(&ar->udev->dev, "crypto offloading is disabled "
                         "by firmware.\n");
-               ar->disable_offload = true;
+               ar->fw.disable_offload_fw = true;
        }
 
        if (SUPP(CARL9170FW_PSM) && SUPP(CARL9170FW_FIXED_5GHZ_PSM))
@@ -345,20 +363,15 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
                }
        }
 
-       ar->if_comb_limits[0].max = ar->fw.vif_num;
-       ar->if_comb_limits[0].types = if_comb_types;
-
-       ar->if_combs[0].num_different_channels = 1;
-       ar->if_combs[0].max_interfaces = ar->fw.vif_num;
-       ar->if_combs[0].limits = ar->if_comb_limits;
-       ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits);
-
-       ar->hw->wiphy->iface_combinations = ar->if_combs;
-       ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs);
+       carl9170_fw_set_if_combinations(ar, if_comb_types);
 
        ar->hw->wiphy->interface_modes |= if_comb_types;
 
-       ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+       ar->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+       /* As IBSS Encryption is software-based, IBSS RSN is supported. */
+       ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+                WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_SUPPORTS_TDLS;
 
 #undef SUPPORTED
        return carl9170_fw_tx_sequence(ar);
index 9443c802b25b8578f6afd2b2cb66243ab47f34f1..9111d4ffc1b3851309af860a7b2e3e8175a2fb3e 100644 (file)
@@ -156,6 +156,14 @@ struct carl9170_psm {
 } __packed;
 #define CARL9170_PSM_SIZE              4
 
+/*
+ * Note: If a bit in rx_filter is set, then it
+ * means that the particular frames which matches
+ * the condition are FILTERED/REMOVED/DISCARDED!
+ * (This is can be a bit confusing, especially
+ * because someone people think it's the exact
+ * opposite way, so watch out!)
+ */
 struct carl9170_rx_filter_cmd {
        __le32          rx_filter;
 } __packed;
index fa834c1460f0033b4b6e1273952129d12750898a..0db874abde500750f7365ed6a9c254b297d03025 100644 (file)
 
 #define        AR9170_MAC_REG_BCN_ADDR                 (AR9170_MAC_REG_BASE + 0xd84)
 #define        AR9170_MAC_REG_BCN_LENGTH               (AR9170_MAC_REG_BASE + 0xd88)
-#define                AR9170_MAC_BCN_LENGTH_MAX               256
+#define                AR9170_MAC_BCN_LENGTH_MAX               (512 - 32)
 
 #define AR9170_MAC_REG_BCN_STATUS              (AR9170_MAC_REG_BASE + 0xd8c)
 
index 25a1e2f4f73862fb9bc26f6310d87bb3d51b6cf5..ef82751722e0bd4ae45c50ad18e705cee755222d 100644 (file)
@@ -358,8 +358,13 @@ static int carl9170_op_start(struct ieee80211_hw *hw)
        ar->ps.last_action = jiffies;
        ar->ps.last_slept = jiffies;
        ar->erp_mode = CARL9170_ERP_AUTO;
-       ar->rx_software_decryption = false;
-       ar->disable_offload = false;
+
+       /* Set "disable hw crypto offload" whenever the module parameter
+        * nohwcrypt is true or if the firmware does not support it.
+        */
+       ar->disable_offload = modparam_nohwcrypt |
+               ar->fw.disable_offload_fw;
+       ar->rx_software_decryption = ar->disable_offload;
 
        for (i = 0; i < ar->hw->queues; i++) {
                ar->queue_stop_timeout[i] = jiffies;
@@ -565,12 +570,28 @@ static int carl9170_init_interface(struct ar9170 *ar,
 
        memcpy(common->macaddr, vif->addr, ETH_ALEN);
 
-       if (modparam_nohwcrypt ||
-           ((vif->type != NL80211_IFTYPE_STATION) &&
-            (vif->type != NL80211_IFTYPE_AP))) {
-               ar->rx_software_decryption = true;
-               ar->disable_offload = true;
-       }
+       /* We have to fall back to software crypto, whenever
+        * the user choose to participates in an IBSS. HW
+        * offload for IBSS RSN is not supported by this driver.
+        *
+        * NOTE: If the previous main interface has already
+        * disabled hw crypto offload, we have to keep this
+        * previous disable_offload setting as it was.
+        * Altough ideally, we should notify mac80211 and tell
+        * it to forget about any HW crypto offload for now.
+        */
+       ar->disable_offload |= ((vif->type != NL80211_IFTYPE_STATION) &&
+           (vif->type != NL80211_IFTYPE_AP));
+
+       /* While the driver supports HW offload in a single
+        * P2P client configuration, it doesn't support HW
+        * offload in the favourit, concurrent P2P GO+CLIENT
+        * configuration. Hence, HW offload will always be
+        * disabled for P2P.
+        */
+       ar->disable_offload |= vif->p2p;
+
+       ar->rx_software_decryption = ar->disable_offload;
 
        err = carl9170_set_operating_mode(ar);
        return err;
@@ -580,7 +601,7 @@ static int carl9170_op_add_interface(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif)
 {
        struct carl9170_vif_info *vif_priv = (void *) vif->drv_priv;
-       struct ieee80211_vif *main_vif;
+       struct ieee80211_vif *main_vif, *old_main = NULL;
        struct ar9170 *ar = hw->priv;
        int vif_id = -1, err = 0;
 
@@ -602,6 +623,15 @@ static int carl9170_op_add_interface(struct ieee80211_hw *hw,
                goto init;
        }
 
+       /* Because the AR9170 HW's MAC doesn't provide full support for
+        * multiple, independent interfaces [of different operation modes].
+        * We have to select ONE main interface [main mode of HW], but we
+        * can have multiple slaves [AKA: entry in the ACK-table].
+        *
+        * The first (from HEAD/TOP) interface in the ar->vif_list is
+        * always the main intf. All following intfs in this list
+        * are considered to be slave intfs.
+        */
        main_vif = carl9170_get_main_vif(ar);
 
        if (main_vif) {
@@ -610,6 +640,18 @@ static int carl9170_op_add_interface(struct ieee80211_hw *hw,
                        if (vif->type == NL80211_IFTYPE_STATION)
                                break;
 
+                       /* P2P GO [master] use-case
+                        * Because the P2P GO station is selected dynamically
+                        * by all participating peers of a WIFI Direct network,
+                        * the driver has be able to change the main interface
+                        * operating mode on the fly.
+                        */
+                       if (main_vif->p2p && vif->p2p &&
+                           vif->type == NL80211_IFTYPE_AP) {
+                               old_main = main_vif;
+                               break;
+                       }
+
                        err = -EBUSY;
                        rcu_read_unlock();
 
@@ -648,14 +690,41 @@ static int carl9170_op_add_interface(struct ieee80211_hw *hw,
        vif_priv->id = vif_id;
        vif_priv->enable_beacon = false;
        ar->vifs++;
-       list_add_tail_rcu(&vif_priv->list, &ar->vif_list);
+       if (old_main) {
+               /* We end up in here, if the main interface is being replaced.
+                * Put the new main interface at the HEAD of the list and the
+                * previous inteface will automatically become second in line.
+                */
+               list_add_rcu(&vif_priv->list, &ar->vif_list);
+       } else {
+               /* Add new inteface. If the list is empty, it will become the
+                * main inteface, otherwise it will be slave.
+                */
+               list_add_tail_rcu(&vif_priv->list, &ar->vif_list);
+       }
        rcu_assign_pointer(ar->vif_priv[vif_id].vif, vif);
 
 init:
-       if (carl9170_get_main_vif(ar) == vif) {
+       main_vif = carl9170_get_main_vif(ar);
+
+       if (main_vif == vif) {
                rcu_assign_pointer(ar->beacon_iter, vif_priv);
                rcu_read_unlock();
 
+               if (old_main) {
+                       struct carl9170_vif_info *old_main_priv =
+                               (void *) old_main->drv_priv;
+                       /* downgrade old main intf to slave intf.
+                        * NOTE: We are no longer under rcu_read_lock.
+                        * But we are still holding ar->mutex, so the
+                        * vif data [id, addr] is safe.
+                        */
+                       err = carl9170_mod_virtual_mac(ar, old_main_priv->id,
+                                                      old_main->addr);
+                       if (err)
+                               goto unlock;
+               }
+
                err = carl9170_init_interface(ar, vif);
                if (err)
                        goto unlock;
@@ -1112,9 +1181,7 @@ static int carl9170_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
        if (ar->disable_offload || !vif)
                return -EOPNOTSUPP;
 
-       /*
-        * We have to fall back to software encryption, whenever
-        * the user choose to participates in an IBSS or is connected
+       /* Fall back to software encryption whenever the driver is connected
         * to more than one network.
         *
         * This is very unfortunate, because some machines cannot handle
@@ -1263,7 +1330,7 @@ static int carl9170_op_sta_add(struct ieee80211_hw *hw,
                        return 0;
                }
 
-               for (i = 0; i < CARL9170_NUM_TID; i++)
+               for (i = 0; i < ARRAY_SIZE(sta_info->agg); i++)
                        RCU_INIT_POINTER(sta_info->agg[i], NULL);
 
                sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor);
@@ -1287,7 +1354,7 @@ static int carl9170_op_sta_remove(struct ieee80211_hw *hw,
                sta_info->ht_sta = false;
 
                rcu_read_lock();
-               for (i = 0; i < CARL9170_NUM_TID; i++) {
+               for (i = 0; i < ARRAY_SIZE(sta_info->agg); i++) {
                        struct carl9170_sta_tid *tid_info;
 
                        tid_info = rcu_dereference(sta_info->agg[i]);
@@ -1394,7 +1461,9 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                break;
 
-       case IEEE80211_AMPDU_TX_STOP:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
                rcu_read_lock();
                tid_info = rcu_dereference(sta_info->agg[tid]);
                if (tid_info) {
@@ -1805,10 +1874,6 @@ void *carl9170_alloc(size_t priv_size)
        for (i = 0; i < ARRAY_SIZE(ar->noise); i++)
                ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */
 
-       hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
-
-       /* As IBSS Encryption is software-based, IBSS RSN is supported. */
-       hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
        return ar;
 
 err_nomem:
@@ -1916,13 +1981,13 @@ static int carl9170_parse_eeprom(struct ar9170 *ar)
        return 0;
 }
 
-static int carl9170_reg_notifier(struct wiphy *wiphy,
-                                struct regulatory_request *request)
+static void carl9170_reg_notifier(struct wiphy *wiphy,
+                                 struct regulatory_request *request)
 {
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct ar9170 *ar = hw->priv;
 
-       return ath_reg_notifier_apply(wiphy, request, &ar->common.regulatory);
+       ath_reg_notifier_apply(wiphy, request, &ar->common.regulatory);
 }
 
 int carl9170_register(struct ar9170 *ar)
index ef4ec0da6e498288cafe11ff6770b6eaf96d13e8..9c0b150d5b8e2a20b3373d91b733362f083df63a 100644 (file)
@@ -1520,35 +1520,92 @@ void carl9170_tx_scheduler(struct ar9170 *ar)
                carl9170_tx(ar);
 }
 
-int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
+/* caller has to take rcu_read_lock */
+static struct carl9170_vif_info *carl9170_pick_beaconing_vif(struct ar9170 *ar)
 {
-       struct sk_buff *skb = NULL;
        struct carl9170_vif_info *cvif;
+       int i = 1;
+
+       /* The AR9170 hardware has no fancy beacon queue or some
+        * other scheduling mechanism. So, the driver has to make
+        * due by setting the two beacon timers (pretbtt and tbtt)
+        * once and then swapping the beacon address in the HW's
+        * register file each time the pretbtt fires.
+        */
+
+       cvif = rcu_dereference(ar->beacon_iter);
+       if (ar->vifs > 0 && cvif) {
+               do {
+                       list_for_each_entry_continue_rcu(cvif, &ar->vif_list,
+                                                        list) {
+                               if (cvif->active && cvif->enable_beacon)
+                                       goto out;
+                       }
+               } while (ar->beacon_enabled && i--);
+       }
+
+out:
+       rcu_assign_pointer(ar->beacon_iter, cvif);
+       return cvif;
+}
+
+static bool carl9170_tx_beacon_physet(struct ar9170 *ar, struct sk_buff *skb,
+                                     u32 *ht1, u32 *plcp)
+{
        struct ieee80211_tx_info *txinfo;
        struct ieee80211_tx_rate *rate;
-       __le32 *data, *old = NULL;
-       unsigned int plcp, power, chains;
-       u32 word, ht1, off, addr, len;
-       int i = 0, err = 0;
+       unsigned int power, chains;
+       bool ht_rate;
 
-       rcu_read_lock();
-       cvif = rcu_dereference(ar->beacon_iter);
-retry:
-       if (ar->vifs == 0 || !cvif)
-               goto out_unlock;
+       txinfo = IEEE80211_SKB_CB(skb);
+       rate = &txinfo->control.rates[0];
+       ht_rate = !!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS);
+       carl9170_tx_rate_tpc_chains(ar, txinfo, rate, plcp, &power, &chains);
 
-       list_for_each_entry_continue_rcu(cvif, &ar->vif_list, list) {
-               if (cvif->active && cvif->enable_beacon)
-                       goto found;
+       *ht1 = AR9170_MAC_BCN_HT1_TX_ANT0;
+       if (chains == AR9170_TX_PHY_TXCHAIN_2)
+               *ht1 |= AR9170_MAC_BCN_HT1_TX_ANT1;
+       SET_VAL(AR9170_MAC_BCN_HT1_PWR_CTRL, *ht1, 7);
+       SET_VAL(AR9170_MAC_BCN_HT1_TPC, *ht1, power);
+       SET_VAL(AR9170_MAC_BCN_HT1_CHAIN_MASK, *ht1, chains);
+
+       if (ht_rate) {
+               *ht1 |= AR9170_MAC_BCN_HT1_HT_EN;
+               if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
+                       *plcp |= AR9170_MAC_BCN_HT2_SGI;
+
+               if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+                       *ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_SHARED;
+                       *plcp |= AR9170_MAC_BCN_HT2_BW40;
+               } else if (rate->flags & IEEE80211_TX_RC_DUP_DATA) {
+                       *ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_DUP;
+                       *plcp |= AR9170_MAC_BCN_HT2_BW40;
+               }
+
+               SET_VAL(AR9170_MAC_BCN_HT2_LEN, *plcp, skb->len + FCS_LEN);
+       } else {
+               if (*plcp <= AR9170_TX_PHY_RATE_CCK_11M)
+                       *plcp |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400;
+               else
+                       *plcp |= ((skb->len + FCS_LEN) << 16) + 0x0010;
        }
 
-       if (!ar->beacon_enabled || i++)
-               goto out_unlock;
+       return ht_rate;
+}
 
-       goto retry;
+int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
+{
+       struct sk_buff *skb = NULL;
+       struct carl9170_vif_info *cvif;
+       __le32 *data, *old = NULL;
+       u32 word, ht1, plcp, off, addr, len;
+       int i = 0, err = 0;
+       bool ht_rate;
 
-found:
-       rcu_assign_pointer(ar->beacon_iter, cvif);
+       rcu_read_lock();
+       cvif = carl9170_pick_beaconing_vif(ar);
+       if (!cvif)
+               goto out_unlock;
 
        skb = ieee80211_beacon_get_tim(ar->hw, carl9170_get_vif(cvif),
                NULL, NULL);
@@ -1558,7 +1615,6 @@ found:
                goto err_free;
        }
 
-       txinfo = IEEE80211_SKB_CB(skb);
        spin_lock_bh(&ar->beacon_lock);
        data = (__le32 *)skb->data;
        if (cvif->beacon)
@@ -1588,43 +1644,14 @@ found:
                goto err_unlock;
        }
 
-       ht1 = AR9170_MAC_BCN_HT1_TX_ANT0;
-       rate = &txinfo->control.rates[0];
-       carl9170_tx_rate_tpc_chains(ar, txinfo, rate, &plcp, &power, &chains);
-       if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS)) {
-               if (plcp <= AR9170_TX_PHY_RATE_CCK_11M)
-                       plcp |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400;
-               else
-                       plcp |= ((skb->len + FCS_LEN) << 16) + 0x0010;
-       } else {
-               ht1 |= AR9170_MAC_BCN_HT1_HT_EN;
-               if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
-                       plcp |= AR9170_MAC_BCN_HT2_SGI;
-
-               if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
-                       ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_SHARED;
-                       plcp |= AR9170_MAC_BCN_HT2_BW40;
-               }
-               if (rate->flags & IEEE80211_TX_RC_DUP_DATA) {
-                       ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_DUP;
-                       plcp |= AR9170_MAC_BCN_HT2_BW40;
-               }
-
-               SET_VAL(AR9170_MAC_BCN_HT2_LEN, plcp, skb->len + FCS_LEN);
-       }
-
-       SET_VAL(AR9170_MAC_BCN_HT1_PWR_CTRL, ht1, 7);
-       SET_VAL(AR9170_MAC_BCN_HT1_TPC, ht1, power);
-       SET_VAL(AR9170_MAC_BCN_HT1_CHAIN_MASK, ht1, chains);
-       if (chains == AR9170_TX_PHY_TXCHAIN_2)
-               ht1 |= AR9170_MAC_BCN_HT1_TX_ANT1;
+       ht_rate = carl9170_tx_beacon_physet(ar, skb, &ht1, &plcp);
 
        carl9170_async_regwrite_begin(ar);
        carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT1, ht1);
-       if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS))
-               carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, plcp);
-       else
+       if (ht_rate)
                carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT2, plcp);
+       else
+               carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, plcp);
 
        for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) {
                /*
index 2ec3e9191e4dcc150272fe116d1087bfc93ef6a9..2282847d4bb898fa2f09f858f9530b9ae6191300 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __CARL9170_SHARED_VERSION_H
 #define __CARL9170_SHARED_VERSION_H
 #define CARL9170FW_VERSION_YEAR 12
-#define CARL9170FW_VERSION_MONTH 7
-#define CARL9170FW_VERSION_DAY 7
-#define CARL9170FW_VERSION_GIT "1.9.6"
+#define CARL9170FW_VERSION_MONTH 12
+#define CARL9170FW_VERSION_DAY 15
+#define CARL9170FW_VERSION_GIT "1.9.7"
 #endif /* __CARL9170_SHARED_VERSION_H */
index d81698015bf75897ae9d58fcf80e4ef6359e4be9..ccc4c718f124c2a500410cc9ed16f48841e8ec29 100644 (file)
@@ -195,8 +195,6 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
        const struct ieee80211_reg_rule *reg_rule;
        struct ieee80211_channel *ch;
        unsigned int i;
-       u32 bandwidth = 0;
-       int r;
 
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
 
@@ -214,11 +212,8 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
                                continue;
 
                        if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
-                               r = freq_reg_info(wiphy,
-                                                 ch->center_freq,
-                                                 bandwidth,
-                                                 &reg_rule);
-                               if (r)
+                               reg_rule = freq_reg_info(wiphy, ch->center_freq);
+                               if (IS_ERR(reg_rule))
                                        continue;
                                /*
                                 * If 11d had a rule for this channel ensure
@@ -254,8 +249,6 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy,
        struct ieee80211_supported_band *sband;
        struct ieee80211_channel *ch;
        const struct ieee80211_reg_rule *reg_rule;
-       u32 bandwidth = 0;
-       int r;
 
        sband = wiphy->bands[IEEE80211_BAND_2GHZ];
        if (!sband)
@@ -283,16 +276,16 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy,
         */
 
        ch = &sband->channels[11]; /* CH 12 */
-       r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
-       if (!r) {
+       reg_rule = freq_reg_info(wiphy, ch->center_freq);
+       if (!IS_ERR(reg_rule)) {
                if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
                        if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
                                ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
        }
 
        ch = &sband->channels[12]; /* CH 13 */
-       r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
-       if (!r) {
+       reg_rule = freq_reg_info(wiphy, ch->center_freq);
+       if (!IS_ERR(reg_rule)) {
                if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
                        if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
                                ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
@@ -363,9 +356,9 @@ static u16 ath_regd_find_country_by_name(char *alpha2)
        return -1;
 }
 
-int ath_reg_notifier_apply(struct wiphy *wiphy,
-                          struct regulatory_request *request,
-                          struct ath_regulatory *reg)
+void ath_reg_notifier_apply(struct wiphy *wiphy,
+                           struct regulatory_request *request,
+                           struct ath_regulatory *reg)
 {
        struct ath_common *common = container_of(reg, struct ath_common,
                                                 regulatory);
@@ -380,7 +373,7 @@ int ath_reg_notifier_apply(struct wiphy *wiphy,
         * any pending requests in the queue.
         */
        if (!request)
-               return 0;
+               return;
 
        switch (request->initiator) {
        case NL80211_REGDOM_SET_BY_CORE:
@@ -416,8 +409,6 @@ int ath_reg_notifier_apply(struct wiphy *wiphy,
 
                break;
        }
-
-       return 0;
 }
 EXPORT_SYMBOL(ath_reg_notifier_apply);
 
@@ -507,8 +498,8 @@ ath_get_regpair(int regdmn)
 static int
 ath_regd_init_wiphy(struct ath_regulatory *reg,
                    struct wiphy *wiphy,
-                   int (*reg_notifier)(struct wiphy *wiphy,
-                                       struct regulatory_request *request))
+                   void (*reg_notifier)(struct wiphy *wiphy,
+                                        struct regulatory_request *request))
 {
        const struct ieee80211_regdomain *regd;
 
@@ -628,8 +619,8 @@ static int __ath_regd_init(struct ath_regulatory *reg)
 int
 ath_regd_init(struct ath_regulatory *reg,
              struct wiphy *wiphy,
-             int (*reg_notifier)(struct wiphy *wiphy,
-                                 struct regulatory_request *request))
+             void (*reg_notifier)(struct wiphy *wiphy,
+                                  struct regulatory_request *request))
 {
        struct ath_common *common = container_of(reg, struct ath_common,
                                                 regulatory);
index 03a8268ccf21a7ca9d9c8ccf9a836c7b0d402d57..37f53bd8fcb13ad1b7f850924383a4e9a297f7af 100644 (file)
@@ -252,12 +252,12 @@ enum CountryCode {
 bool ath_is_world_regd(struct ath_regulatory *reg);
 bool ath_is_49ghz_allowed(u16 redomain);
 int ath_regd_init(struct ath_regulatory *reg, struct wiphy *wiphy,
-                 int (*reg_notifier)(struct wiphy *wiphy,
-                 struct regulatory_request *request));
+                 void (*reg_notifier)(struct wiphy *wiphy,
+                                      struct regulatory_request *request));
 u32 ath_regd_get_band_ctl(struct ath_regulatory *reg,
                          enum ieee80211_band band);
-int ath_reg_notifier_apply(struct wiphy *wiphy,
-                          struct regulatory_request *request,
-                          struct ath_regulatory *reg);
+void ath_reg_notifier_apply(struct wiphy *wiphy,
+                           struct regulatory_request *request,
+                           struct ath_regulatory *reg);
 
 #endif
index 116f4e807ae1ce54a79a548f25a8c533dddaa2ff..002851fceb2f9072a4498e623e64a8f755f014ee 100644 (file)
@@ -204,7 +204,6 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
                break;
        default:
                return -EOPNOTSUPP;
-
        }
 
        /* FW don't support scan after connection attempt */
@@ -228,8 +227,8 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
                }
                /* 0-based channel indexes */
                cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1;
-               wil_dbg(wil, "Scan for ch %d  : %d MHz\n", ch,
-                       request->channels[i]->center_freq);
+               wil_dbg_misc(wil, "Scan for ch %d  : %d MHz\n", ch,
+                            request->channels[i]->center_freq);
        }
 
        return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
@@ -425,8 +424,8 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
                return -EINVAL;
        }
 
-       wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
-               channel->center_freq, info->privacy ? "secure" : "open");
+       wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
+                    channel->center_freq, info->privacy ? "secure" : "open");
        print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
                             info->ssid, info->ssid_len);
 
index 38049da710498680365eecb2587a2d7ca7f49e52..dc97e7b2609cb84c00d8954ba067aab60b2b5a0a 100644 (file)
@@ -38,7 +38,9 @@
 #define WIL6210_IMC_RX         BIT_DMA_EP_RX_ICR_RX_DONE
 #define WIL6210_IMC_TX         (BIT_DMA_EP_TX_ICR_TX_DONE | \
                                BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
-#define WIL6210_IMC_MISC       (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT)
+#define WIL6210_IMC_MISC       (ISR_MISC_FW_READY | \
+                                ISR_MISC_MBOX_EVT | \
+                                ISR_MISC_FW_ERROR)
 
 #define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \
                                        BIT_DMA_PSEUDO_CAUSE_TX | \
@@ -50,7 +52,6 @@
 
 static inline void wil_icr_clear(u32 x, void __iomem *addr)
 {
-
 }
 #else /* defined(CONFIG_WIL6210_ISR_COR) */
 /* configure to Write-1-to-Clear mode */
@@ -94,7 +95,7 @@ static void wil6210_mask_irq_misc(struct wil6210_priv *wil)
 
 static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
 {
-       wil_dbg_IRQ(wil, "%s()\n", __func__);
+       wil_dbg_irq(wil, "%s()\n", __func__);
 
        iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
                  HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
@@ -125,7 +126,7 @@ static void wil6210_unmask_irq_misc(struct wil6210_priv *wil)
 
 static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
 {
-       wil_dbg_IRQ(wil, "%s()\n", __func__);
+       wil_dbg_irq(wil, "%s()\n", __func__);
 
        set_bit(wil_status_irqen, &wil->status);
 
@@ -135,7 +136,7 @@ static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
 
 void wil6210_disable_irq(struct wil6210_priv *wil)
 {
-       wil_dbg_IRQ(wil, "%s()\n", __func__);
+       wil_dbg_irq(wil, "%s()\n", __func__);
 
        wil6210_mask_irq_tx(wil);
        wil6210_mask_irq_rx(wil);
@@ -145,7 +146,7 @@ void wil6210_disable_irq(struct wil6210_priv *wil)
 
 void wil6210_enable_irq(struct wil6210_priv *wil)
 {
-       wil_dbg_IRQ(wil, "%s()\n", __func__);
+       wil_dbg_irq(wil, "%s()\n", __func__);
 
        iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) +
                  offsetof(struct RGF_ICR, ICC));
@@ -167,7 +168,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
                                         HOSTADDR(RGF_DMA_EP_RX_ICR) +
                                         offsetof(struct RGF_ICR, ICR));
 
-       wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr);
+       wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
 
        if (!isr) {
                wil_err(wil, "spurious IRQ: RX\n");
@@ -177,7 +178,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
        wil6210_mask_irq_rx(wil);
 
        if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
-               wil_dbg_IRQ(wil, "RX done\n");
+               wil_dbg_irq(wil, "RX done\n");
                isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
                wil_rx_handle(wil);
        }
@@ -197,7 +198,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
                                         HOSTADDR(RGF_DMA_EP_TX_ICR) +
                                         offsetof(struct RGF_ICR, ICR));
 
-       wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr);
+       wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
 
        if (!isr) {
                wil_err(wil, "spurious IRQ: TX\n");
@@ -208,13 +209,13 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
 
        if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
                uint i;
-               wil_dbg_IRQ(wil, "TX done\n");
+               wil_dbg_irq(wil, "TX done\n");
                isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
                for (i = 0; i < 24; i++) {
                        u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i);
                        if (isr & mask) {
                                isr &= ~mask;
-                               wil_dbg_IRQ(wil, "TX done(%i)\n", i);
+                               wil_dbg_irq(wil, "TX done(%i)\n", i);
                                wil_tx_complete(wil, i);
                        }
                }
@@ -228,6 +229,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
        return IRQ_HANDLED;
 }
 
+static void wil_notify_fw_error(struct wil6210_priv *wil)
+{
+       struct device *dev = &wil_to_ndev(wil)->dev;
+       char *envp[3] = {
+               [0] = "SOURCE=wil6210",
+               [1] = "EVENT=FW_ERROR",
+               [2] = NULL,
+       };
+       kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+}
+
 static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
 {
        struct wil6210_priv *wil = cookie;
@@ -235,7 +247,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
                                         HOSTADDR(RGF_DMA_EP_MISC_ICR) +
                                         offsetof(struct RGF_ICR, ICR));
 
-       wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr);
+       wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr);
 
        if (!isr) {
                wil_err(wil, "spurious IRQ: MISC\n");
@@ -244,8 +256,15 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
 
        wil6210_mask_irq_misc(wil);
 
+       if (isr & ISR_MISC_FW_ERROR) {
+               wil_dbg_irq(wil, "IRQ: Firmware error\n");
+               clear_bit(wil_status_fwready, &wil->status);
+               wil_notify_fw_error(wil);
+               isr &= ~ISR_MISC_FW_ERROR;
+       }
+
        if (isr & ISR_MISC_FW_READY) {
-               wil_dbg_IRQ(wil, "IRQ: FW ready\n");
+               wil_dbg_irq(wil, "IRQ: FW ready\n");
                /**
                 * Actual FW ready indicated by the
                 * WMI_FW_READY_EVENTID
@@ -268,10 +287,10 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
        struct wil6210_priv *wil = cookie;
        u32 isr = wil->isr_misc;
 
-       wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr);
+       wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
 
        if (isr & ISR_MISC_MBOX_EVT) {
-               wil_dbg_IRQ(wil, "MBOX event\n");
+               wil_dbg_irq(wil, "MBOX event\n");
                wmi_recv_cmd(wil);
                isr &= ~ISR_MISC_MBOX_EVT;
        }
@@ -293,7 +312,7 @@ static irqreturn_t wil6210_thread_irq(int irq, void *cookie)
 {
        struct wil6210_priv *wil = cookie;
 
-       wil_dbg_IRQ(wil, "Thread IRQ\n");
+       wil_dbg_irq(wil, "Thread IRQ\n");
        /* Discover real IRQ cause */
        if (wil->isr_misc)
                wil6210_irq_misc_thread(irq, cookie);
@@ -370,6 +389,8 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
        if (wil6210_debug_irq_mask(wil, pseudo_cause))
                return IRQ_NONE;
 
+       wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause);
+
        wil6210_mask_irq_pseudo(wil);
 
        /* Discover real IRQ cause
@@ -401,8 +422,6 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
        if (rc != IRQ_WAKE_THREAD)
                wil6210_unmask_irq_pseudo(wil);
 
-       wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause);
-
        return rc;
 }
 
index 95fcd361322b06149ff0f7b95aa70ba08f6da181..761c389586d4cef581b1239d58f3e313e9f9e49a 100644 (file)
@@ -64,7 +64,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
        struct net_device *ndev = wil_to_ndev(wil);
        struct wireless_dev *wdev = wil->wdev;
 
-       wil_dbg(wil, "%s()\n", __func__);
+       wil_dbg_misc(wil, "%s()\n", __func__);
 
        wil_link_off(wil);
        clear_bit(wil_status_fwconnected, &wil->status);
@@ -80,11 +80,13 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
                                        GFP_KERNEL);
                break;
        default:
-               ;
+               break;
        }
 
        for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
                wil_vring_fini_tx(wil, i);
+
+       clear_bit(wil_status_dontscan, &wil->status);
 }
 
 static void wil_disconnect_worker(struct work_struct *work)
@@ -99,7 +101,7 @@ static void wil_connect_timer_fn(ulong x)
 {
        struct wil6210_priv *wil = (void *)x;
 
-       wil_dbg(wil, "Connect timeout\n");
+       wil_dbg_misc(wil, "Connect timeout\n");
 
        /* reschedule to thread context - disconnect won't
         * run from atomic context
@@ -107,9 +109,18 @@ static void wil_connect_timer_fn(ulong x)
        schedule_work(&wil->disconnect_worker);
 }
 
+static void wil_cache_mbox_regs(struct wil6210_priv *wil)
+{
+       /* make shadow copy of registers that should not change on run time */
+       wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
+                            sizeof(struct wil6210_mbox_ctl));
+       wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
+       wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+}
+
 int wil_priv_init(struct wil6210_priv *wil)
 {
-       wil_dbg(wil, "%s()\n", __func__);
+       wil_dbg_misc(wil, "%s()\n", __func__);
 
        mutex_init(&wil->mutex);
        mutex_init(&wil->wmi_mutex);
@@ -136,11 +147,7 @@ int wil_priv_init(struct wil6210_priv *wil)
                return -EAGAIN;
        }
 
-       /* make shadow copy of registers that should not change on run time */
-       wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
-                            sizeof(struct wil6210_mbox_ctl));
-       wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
-       wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+       wil_cache_mbox_regs(wil);
 
        return 0;
 }
@@ -162,7 +169,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)
 
 static void wil_target_reset(struct wil6210_priv *wil)
 {
-       wil_dbg(wil, "Resetting...\n");
+       wil_dbg_misc(wil, "Resetting...\n");
 
        /* register write */
 #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a))
@@ -202,7 +209,7 @@ static void wil_target_reset(struct wil6210_priv *wil)
 
        msleep(2000);
 
-       wil_dbg(wil, "Reset completed\n");
+       wil_dbg_misc(wil, "Reset completed\n");
 
 #undef W
 #undef S
@@ -225,8 +232,8 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
                wil_err(wil, "Firmware not ready\n");
                return -ETIME;
        } else {
-               wil_dbg(wil, "FW ready after %d ms\n",
-                       jiffies_to_msecs(to-left));
+               wil_dbg_misc(wil, "FW ready after %d ms\n",
+                            jiffies_to_msecs(to-left));
        }
        return 0;
 }
@@ -243,13 +250,13 @@ int wil_reset(struct wil6210_priv *wil)
        cancel_work_sync(&wil->disconnect_worker);
        wil6210_disconnect(wil, NULL);
 
+       wil6210_disable_irq(wil);
+       wil->status = 0;
+
        wmi_event_flush(wil);
 
-       flush_workqueue(wil->wmi_wq);
        flush_workqueue(wil->wmi_wq_conn);
-
-       wil6210_disable_irq(wil);
-       wil->status = 0;
+       flush_workqueue(wil->wmi_wq);
 
        /* TODO: put MAC in reset */
        wil_target_reset(wil);
@@ -258,11 +265,7 @@ int wil_reset(struct wil6210_priv *wil)
        wil->pending_connect_cid = -1;
        INIT_COMPLETION(wil->wmi_ready);
 
-       /* make shadow copy of registers that should not change on run time */
-       wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
-                            sizeof(struct wil6210_mbox_ctl));
-       wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
-       wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+       wil_cache_mbox_regs(wil);
 
        /* TODO: release MAC reset */
        wil6210_enable_irq(wil);
@@ -278,7 +281,7 @@ void wil_link_on(struct wil6210_priv *wil)
 {
        struct net_device *ndev = wil_to_ndev(wil);
 
-       wil_dbg(wil, "%s()\n", __func__);
+       wil_dbg_misc(wil, "%s()\n", __func__);
 
        netif_carrier_on(ndev);
        netif_tx_wake_all_queues(ndev);
@@ -288,7 +291,7 @@ void wil_link_off(struct wil6210_priv *wil)
 {
        struct net_device *ndev = wil_to_ndev(wil);
 
-       wil_dbg(wil, "%s()\n", __func__);
+       wil_dbg_misc(wil, "%s()\n", __func__);
 
        netif_tx_stop_all_queues(ndev);
        netif_carrier_off(ndev);
@@ -311,27 +314,27 @@ static int __wil_up(struct wil6210_priv *wil)
        wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC);
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
-               wil_dbg(wil, "type: STATION\n");
+               wil_dbg_misc(wil, "type: STATION\n");
                bi = 0;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_AP:
-               wil_dbg(wil, "type: AP\n");
+               wil_dbg_misc(wil, "type: AP\n");
                bi = 100;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
-               wil_dbg(wil, "type: P2P_CLIENT\n");
+               wil_dbg_misc(wil, "type: P2P_CLIENT\n");
                bi = 0;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_P2P_GO:
-               wil_dbg(wil, "type: P2P_GO\n");
+               wil_dbg_misc(wil, "type: P2P_GO\n");
                bi = 100;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_MONITOR:
-               wil_dbg(wil, "type: Monitor\n");
+               wil_dbg_misc(wil, "type: Monitor\n");
                bi = 0;
                ndev->type = ARPHRD_IEEE80211_RADIOTAP;
                /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
@@ -354,7 +357,7 @@ static int __wil_up(struct wil6210_priv *wil)
                        wmi_set_channel(wil, channel->hw_value);
                break;
        default:
-               ;
+               break;
        }
 
        /* MAC address - pre-requisite for other commands */
index 3068b5cb53a7855754d808776b5a0071ee59fcbd..8ce2e33dce206065aaa5e5cee9ce3e4c3db3163a 100644 (file)
@@ -35,37 +35,12 @@ static int wil_stop(struct net_device *ndev)
        return wil_down(wil);
 }
 
-/*
- * AC to queue mapping
- *
- * AC_VO -> queue 3
- * AC_VI -> queue 2
- * AC_BE -> queue 1
- * AC_BK -> queue 0
- */
-static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb)
-{
-       static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
-       struct wil6210_priv *wil = ndev_to_wil(ndev);
-       u16 rc;
-
-       skb->priority = cfg80211_classify8021d(skb);
-
-       rc = wil_1d_to_queue[skb->priority];
-
-       wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority,
-                    (int)rc);
-
-       return rc;
-}
-
 static const struct net_device_ops wil_netdev_ops = {
        .ndo_open               = wil_open,
        .ndo_stop               = wil_stop,
        .ndo_start_xmit         = wil_start_xmit,
-       .ndo_select_queue       = wil_select_queue,
-       .ndo_set_mac_address    = eth_mac_addr,
-       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
 };
 
 void *wil_if_alloc(struct device *dev, void __iomem *csr)
@@ -97,7 +72,7 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
        ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels;
        cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT);
 
-       ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1);
+       ndev = alloc_netdev(0, "wlan%d", ether_setup);
        if (!ndev) {
                dev_err(dev, "alloc_netdev_mqs failed\n");
                rc = -ENOMEM;
index 0fc83edd6bad500f77aad37adbc0d80b43082ba3..81c35c6e3832460c694c15b687ceb2a137987a6b 100644 (file)
@@ -53,7 +53,7 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
        }
        wil->n_msi = use_msi;
        if (wil->n_msi) {
-               wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi);
+               wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi);
                rc = pci_enable_msi_block(pdev, wil->n_msi);
                if (rc && (wil->n_msi == 3)) {
                        wil_err(wil, "3 MSI mode failed, try 1 MSI\n");
@@ -65,7 +65,7 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
                        wil->n_msi = 0;
                }
        } else {
-               wil_dbg(wil, "MSI interrupts disabled, use INTx\n");
+               wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n");
        }
 
        rc = wil6210_init_irq(wil, pdev->irq);
index f29c294413cfd52a5875a7c560434240e3695a93..64b971fdc3cc8070050e5c02908851d9d952c3ad 100644 (file)
@@ -100,8 +100,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
                d->dma.status = TX_DMA_STATUS_DU;
        }
 
-       wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
-               vring->va, (unsigned long long)vring->pa, vring->ctx);
+       wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
+                    vring->va, (unsigned long long)vring->pa, vring->ctx);
 
        return 0;
 }
@@ -353,8 +353,8 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
        if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
                wil_rx_add_radiotap_header(wil, skb, d);
 
-       wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
-       wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4,
+       wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
+       wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
                          (const void *)d, sizeof(*d), false);
 
        wil_vring_advance_head(vring, 1);
@@ -369,7 +369,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
         */
        ftype = wil_rxdesc_ftype(d) << 2;
        if (ftype != IEEE80211_FTYPE_DATA) {
-               wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype);
+               wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype);
                /* TODO: process it */
                kfree_skb(skb);
                return NULL;
@@ -430,6 +430,8 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
        int rc;
        unsigned int len = skb->len;
 
+       skb_orphan(skb);
+
        if (in_interrupt())
                rc = netif_rx(skb);
        else
@@ -459,13 +461,11 @@ void wil_rx_handle(struct wil6210_priv *wil)
                wil_err(wil, "Rx IRQ while Rx not yet initialized\n");
                return;
        }
-       wil_dbg_TXRX(wil, "%s()\n", __func__);
+       wil_dbg_txrx(wil, "%s()\n", __func__);
        while (NULL != (skb = wil_vring_reap_rx(wil, v))) {
-               wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
+               wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
                                  skb->data, skb_headlen(skb), false);
 
-               skb_orphan(skb);
-
                if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
                        skb->dev = ndev;
                        skb_reset_mac_header(skb);
@@ -484,53 +484,18 @@ void wil_rx_handle(struct wil6210_priv *wil)
 
 int wil_rx_init(struct wil6210_priv *wil)
 {
-       struct net_device *ndev = wil_to_ndev(wil);
-       struct wireless_dev *wdev = wil->wdev;
        struct vring *vring = &wil->vring_rx;
        int rc;
-       struct wmi_cfg_rx_chain_cmd cmd = {
-               .action = WMI_RX_CHAIN_ADD,
-               .rx_sw_ring = {
-                       .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
-               },
-               .mid = 0, /* TODO - what is it? */
-               .decap_trans_type = WMI_DECAP_TYPE_802_3,
-       };
-       struct {
-               struct wil6210_mbox_hdr_wmi wmi;
-               struct wmi_cfg_rx_chain_done_event evt;
-       } __packed evt;
 
        vring->size = WIL6210_RX_RING_SIZE;
        rc = wil_vring_alloc(wil, vring);
        if (rc)
                return rc;
 
-       cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
-       cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size);
-       if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
-               struct ieee80211_channel *ch = wdev->preset_chandef.chan;
-
-               cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON);
-               if (ch)
-                       cmd.sniffer_cfg.channel = ch->hw_value - 1;
-               cmd.sniffer_cfg.phy_info_mode =
-                       cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP);
-               cmd.sniffer_cfg.phy_support =
-                       cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
-                                   ? WMI_SNIFFER_CP : WMI_SNIFFER_DP);
-       }
-       /* typical time for secure PCP is 840ms */
-       rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
-                     WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000);
+       rc = wmi_rx_chain_add(wil, vring);
        if (rc)
                goto err_free;
 
-       vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr);
-
-       wil_dbg(wil, "Rx init: status %d tail 0x%08x\n",
-               le32_to_cpu(evt.evt.status), vring->hwtail);
-
        rc = wil_rx_refill(wil, vring->size);
        if (rc)
                goto err_free;
@@ -546,25 +511,8 @@ void wil_rx_fini(struct wil6210_priv *wil)
 {
        struct vring *vring = &wil->vring_rx;
 
-       if (vring->va) {
-               int rc;
-               struct wmi_cfg_rx_chain_cmd cmd = {
-                       .action = cpu_to_le32(WMI_RX_CHAIN_DEL),
-                       .rx_sw_ring = {
-                               .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
-                       },
-               };
-               struct {
-                       struct wil6210_mbox_hdr_wmi wmi;
-                       struct wmi_cfg_rx_chain_done_event cfg;
-               } __packed wmi_rx_cfg_reply;
-
-               rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
-                             WMI_CFG_RX_CHAIN_DONE_EVENTID,
-                             &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply),
-                             100);
+       if (vring->va)
                wil_vring_free(wil, vring, 0);
-       }
 }
 
 int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
@@ -617,6 +565,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
        if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) {
                wil_err(wil, "Tx config failed, status 0x%02x\n",
                        reply.cmd.status);
+               rc = -EINVAL;
                goto out_free;
        }
        vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
@@ -689,7 +638,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        uint i = swhead;
        dma_addr_t pa;
 
-       wil_dbg_TXRX(wil, "%s()\n", __func__);
+       wil_dbg_txrx(wil, "%s()\n", __func__);
 
        if (avail < vring->size/8)
                netif_tx_stop_all_queues(wil_to_ndev(wil));
@@ -706,9 +655,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        pa = dma_map_single(dev, skb->data,
                        skb_headlen(skb), DMA_TO_DEVICE);
 
-       wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb),
+       wil_dbg_txrx(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb),
                     skb->data, (unsigned long long)pa);
-       wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
+       wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
                          skb->data, skb_headlen(skb), false);
 
        if (unlikely(dma_mapping_error(dev, pa)))
@@ -737,12 +686,12 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
        d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
 
-       wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4,
+       wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
                          (const void *)d, sizeof(*d), false);
 
        /* advance swhead */
        wil_vring_advance_head(vring, nr_frags + 1);
-       wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
+       wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
        iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
        /* hold reference to skb
         * to prevent skb release before accounting
@@ -775,7 +724,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        struct vring *vring;
        int rc;
 
-       wil_dbg_TXRX(wil, "%s()\n", __func__);
+       wil_dbg_txrx(wil, "%s()\n", __func__);
        if (!test_bit(wil_status_fwready, &wil->status)) {
                wil_err(wil, "FW not ready\n");
                goto drop;
@@ -802,15 +751,13 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        }
        switch (rc) {
        case 0:
-               ndev->stats.tx_packets++;
-               ndev->stats.tx_bytes += skb->len;
+               /* statistics will be updated on the tx_complete */
                dev_kfree_skb_any(skb);
                return NETDEV_TX_OK;
        case -ENOMEM:
                return NETDEV_TX_BUSY;
        default:
-               ; /* goto drop; */
-               break;
+               break; /* goto drop; */
        }
  drop:
        netif_tx_stop_all_queues(ndev);
@@ -827,6 +774,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
  */
 void wil_tx_complete(struct wil6210_priv *wil, int ringid)
 {
+       struct net_device *ndev = wil_to_ndev(wil);
        struct device *dev = wil_to_dev(wil);
        struct vring *vring = &wil->vring_tx[ringid];
 
@@ -835,7 +783,7 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid)
                return;
        }
 
-       wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid);
+       wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
 
        while (!wil_vring_is_empty(vring)) {
                volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx;
@@ -844,16 +792,23 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid)
                if (!(d->dma.status & TX_DMA_STATUS_DU))
                        break;
 
-               wil_dbg_TXRX(wil,
+               wil_dbg_txrx(wil,
                             "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
                             vring->swtail, d->dma.length, d->dma.status,
                             d->dma.error);
-               wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4,
+               wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
                                  (const void *)d, sizeof(*d), false);
 
                pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
                skb = vring->ctx[vring->swtail];
                if (skb) {
+                       if (d->dma.error == 0) {
+                               ndev->stats.tx_packets++;
+                               ndev->stats.tx_bytes += skb->len;
+                       } else {
+                               ndev->stats.tx_errors++;
+                       }
+
                        dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
                        dev_kfree_skb_any(skb);
                        vring->ctx[vring->swtail] = NULL;
index 9bcfffa4006c1d347c01e9c94d81acd799ec2aed..aea961ff8f08c20cdf0c0b0d82047c28d69d10ea 100644 (file)
@@ -36,8 +36,6 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
 
 #define WIL6210_MEM_SIZE (2*1024*1024UL)
 
-#define WIL6210_TX_QUEUES (4)
-
 #define WIL6210_RX_RING_SIZE (128)
 #define WIL6210_TX_RING_SIZE (128)
 #define WIL6210_MAX_TX_RINGS (24)
@@ -101,8 +99,7 @@ struct RGF_ICR {
 #define RGF_DMA_EP_MISC_ICR            (0x881bec) /* struct RGF_ICR */
        #define BIT_DMA_EP_MISC_ICR_RX_HTRSH    BIT(0)
        #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT   BIT(1)
-       #define BIT_DMA_EP_MISC_ICR_FW_INT0     BIT(28)
-       #define BIT_DMA_EP_MISC_ICR_FW_INT1     BIT(29)
+       #define BIT_DMA_EP_MISC_ICR_FW_INT(n)   BIT(28+n) /* n = [0..3] */
 
 /* Interrupt moderation control */
 #define RGF_DMA_ITR_CNT_TRSH           (0x881c5c)
@@ -121,8 +118,9 @@ struct RGF_ICR {
 #define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2
 
 /* ISR register bits */
-#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0
-#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1
+#define ISR_MISC_FW_READY      BIT_DMA_EP_MISC_ICR_FW_INT(0)
+#define ISR_MISC_MBOX_EVT      BIT_DMA_EP_MISC_ICR_FW_INT(1)
+#define ISR_MISC_FW_ERROR      BIT_DMA_EP_MISC_ICR_FW_INT(3)
 
 /* Hardware definitions end */
 
@@ -272,17 +270,18 @@ struct wil6210_priv {
 #define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg)
 #define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg)
 
-#define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
-#define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
-#define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg)
+#define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
+#define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
+#define wil_dbg_wmi(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg)
+#define wil_dbg_misc(wil, fmt, arg...) wil_dbg(wil, "DBG[MISC]" fmt, ##arg)
 
-#define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize,    \
+#define wil_hex_dump_txrx(prefix_str, prefix_type, rowsize,    \
                          groupsize, buf, len, ascii)           \
                          wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\
                                         prefix_type, rowsize,  \
                                         groupsize, buf, len, ascii)
 
-#define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize,     \
+#define wil_hex_dump_wmi(prefix_str, prefix_type, rowsize,     \
                         groupsize, buf, len, ascii)            \
                         wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\
                                        prefix_type, rowsize,   \
@@ -328,6 +327,7 @@ int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
                       const void *mac_addr, int key_len, const void *key);
 int wmi_echo(struct wil6210_priv *wil);
 int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie);
+int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring);
 
 int wil6210_init_irq(struct wil6210_priv *wil, int irq);
 void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
index 12915f6e7617627ceabf025b02c9769d9ac31305..0b70e17cd1fb3c195d2c8e024acca7e6c499394c 100644 (file)
 #include <linux/io.h>
 #include <linux/list.h>
 #include <linux/etherdevice.h>
+#include <linux/if_arp.h>
 
 #include "wil6210.h"
+#include "txrx.h"
 #include "wmi.h"
 
 /**
@@ -186,7 +188,6 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
                wil_err(wil, "WMI size too large: %d bytes, max is %d\n",
                        (int)(sizeof(cmd) + len), r->entry_size);
                return -ERANGE;
-
        }
 
        might_sleep();
@@ -213,7 +214,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
        }
        /* next head */
        next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size);
-       wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head);
+       wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head);
        /* wait till FW finish with previous command */
        for (retry = 5; retry > 0; retry--) {
                r->tail = ioread32(wil->csr + HOST_MBOX +
@@ -234,10 +235,10 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
        }
        cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq);
        /* set command */
-       wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len);
-       wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd,
+       wil_dbg_wmi(wil, "WMI command 0x%04x [%d]\n", cmdid, len);
+       wil_hex_dump_wmi("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd,
                         sizeof(cmd), true);
-       wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf,
+       wil_hex_dump_wmi("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf,
                         len, true);
        wil_memcpy_toio_32(dst, &cmd, sizeof(cmd));
        wil_memcpy_toio_32(dst + sizeof(cmd), buf, len);
@@ -273,7 +274,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
        struct wmi_ready_event *evt = d;
        u32 ver = le32_to_cpu(evt->sw_version);
 
-       wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac);
+       wil_dbg_wmi(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac);
 
        if (!is_valid_ether_addr(ndev->dev_addr)) {
                memcpy(ndev->dev_addr, evt->mac, ETH_ALEN);
@@ -286,7 +287,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
 static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
                             int len)
 {
-       wil_dbg_WMI(wil, "WMI: FW ready\n");
+       wil_dbg_wmi(wil, "WMI: FW ready\n");
 
        set_bit(wil_status_fwready, &wil->status);
        /* reuse wmi_ready for the firmware ready indication */
@@ -309,11 +310,11 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
        u32 d_len = le32_to_cpu(data->info.len);
        u16 d_status = le16_to_cpu(data->info.status);
 
-       wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n",
+       wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d\n",
                    data->info.channel, data->info.mcs, data->info.snr);
-       wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len,
+       wil_dbg_wmi(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len,
                    le16_to_cpu(data->info.stype));
-       wil_dbg_WMI(wil, "qid %d mid %d cid %d\n",
+       wil_dbg_wmi(wil, "qid %d mid %d cid %d\n",
                    data->info.qid, data->info.mid, data->info.cid);
 
        if (!channel) {
@@ -329,13 +330,13 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
                const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable;
                size_t ie_len = d_len - offsetof(struct ieee80211_mgmt,
                                                 u.beacon.variable);
-               wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap);
+               wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);
 
                bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid,
                                          tsf, cap, bi, ie_buf, ie_len,
                                          signal, GFP_KERNEL);
                if (bss) {
-                       wil_dbg_WMI(wil, "Added BSS %pM\n",
+                       wil_dbg_wmi(wil, "Added BSS %pM\n",
                                    rx_mgmt_frame->bssid);
                        cfg80211_put_bss(bss);
                } else {
@@ -351,7 +352,7 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
                struct wmi_scan_complete_event *data = d;
                bool aborted = (data->status != 0);
 
-               wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
+               wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
                cfg80211_scan_done(wil->scan_request, aborted);
                wil->scan_request = NULL;
        } else {
@@ -386,9 +387,9 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
                return;
        }
        ch = evt->channel + 1;
-       wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n",
+       wil_dbg_wmi(wil, "Connect %pM channel [%d] cid %d\n",
                    evt->bssid, ch, evt->cid);
-       wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1,
+       wil_hex_dump_wmi("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1,
                         evt->assoc_info, len - sizeof(*evt), true);
 
        /* figure out IE's */
@@ -450,14 +451,13 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
 {
        struct wmi_disconnect_event *evt = d;
 
-       wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n",
+       wil_dbg_wmi(wil, "Disconnect %pM reason %d proto %d wmi\n",
                    evt->bssid,
                    evt->protocol_reason_status, evt->disconnect_reason);
 
        wil->sinfo_gen++;
 
        wil6210_disconnect(wil, evt->bssid);
-       clear_bit(wil_status_dontscan, &wil->status);
 }
 
 static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
@@ -476,7 +476,7 @@ static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
        wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector);
        wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector);
        wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector);
-       wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n"
+       wil_dbg_wmi(wil, "Link status, MCS %d TSF 0x%016llx\n"
                    "BF status 0x%08x SNR 0x%08x\n"
                    "Tx Tpt %d goodput %d Rx goodput %d\n"
                    "Sectors(rx:tx) my %d:%d peer %d:%d\n",
@@ -501,7 +501,7 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
        struct sk_buff *skb;
        struct ethhdr *eth;
 
-       wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len,
+       wil_dbg_wmi(wil, "EAPOL len %d from %pM\n", eapol_len,
                    evt->src_mac);
 
        if (eapol_len > 196) { /* TODO: revisit size limit */
@@ -599,15 +599,15 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                iowrite32(0, wil->csr + HOSTADDR(r->tail) +
                          offsetof(struct wil6210_mbox_ring_desc, sync));
                /* indicate */
-               wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n",
+               wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
                            le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type),
                            hdr.flags);
                if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
                    (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
-                       wil_dbg_WMI(wil, "WMI event 0x%04x\n",
+                       wil_dbg_wmi(wil, "WMI event 0x%04x\n",
                                    evt->event.wmi.id);
                }
-               wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1,
+               wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
                                 &evt->event.hdr, sizeof(hdr) + len, true);
 
                /* advance tail */
@@ -623,7 +623,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                {
                        int q = queue_work(wil->wmi_wq,
                                           &wil->wmi_event_worker);
-                       wil_dbg_WMI(wil, "queue_work -> %d\n", q);
+                       wil_dbg_wmi(wil, "queue_work -> %d\n", q);
                }
        }
 }
@@ -650,7 +650,7 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
                        cmdid, reply_id, to_msec);
                rc = -ETIME;
        } else {
-               wil_dbg_WMI(wil,
+               wil_dbg_wmi(wil,
                            "wmi_call(0x%04x->0x%04x) completed in %d msec\n",
                            cmdid, reply_id,
                            to_msec - jiffies_to_msecs(remain));
@@ -680,7 +680,7 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr)
 
        memcpy(cmd.mac, addr, ETH_ALEN);
 
-       wil_dbg_WMI(wil, "Set MAC %pM\n", addr);
+       wil_dbg_wmi(wil, "Set MAC %pM\n", addr);
 
        return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd));
 }
@@ -778,7 +778,7 @@ int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb)
 
        skb_set_mac_header(skb, 0);
        eth = eth_hdr(skb);
-       wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest);
+       wil_dbg_wmi(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest);
        for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
                if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0)
                        goto found_dest;
@@ -853,11 +853,60 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
        return rc;
 }
 
+int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
+{
+       struct wireless_dev *wdev = wil->wdev;
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wmi_cfg_rx_chain_cmd cmd = {
+               .action = WMI_RX_CHAIN_ADD,
+               .rx_sw_ring = {
+                       .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
+                       .ring_mem_base = cpu_to_le64(vring->pa),
+                       .ring_size = cpu_to_le16(vring->size),
+               },
+               .mid = 0, /* TODO - what is it? */
+               .decap_trans_type = WMI_DECAP_TYPE_802_3,
+       };
+       struct {
+               struct wil6210_mbox_hdr_wmi wmi;
+               struct wmi_cfg_rx_chain_done_event evt;
+       } __packed evt;
+       int rc;
+
+       if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
+               struct ieee80211_channel *ch = wdev->preset_chandef.chan;
+
+               cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON);
+               if (ch)
+                       cmd.sniffer_cfg.channel = ch->hw_value - 1;
+               cmd.sniffer_cfg.phy_info_mode =
+                       cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP);
+               cmd.sniffer_cfg.phy_support =
+                       cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
+                                   ? WMI_SNIFFER_CP : WMI_SNIFFER_DP);
+       }
+       /* typical time for secure PCP is 840ms */
+       rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
+                     WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000);
+       if (rc)
+               return rc;
+
+       vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr);
+
+       wil_dbg_misc(wil, "Rx init: status %d tail 0x%08x\n",
+                    le32_to_cpu(evt.evt.status), vring->hwtail);
+
+       if (le32_to_cpu(evt.evt.status) != WMI_CFG_RX_CHAIN_SUCCESS)
+               rc = -EINVAL;
+
+       return rc;
+}
+
 void wmi_event_flush(struct wil6210_priv *wil)
 {
        struct pending_wmi_event *evt, *t;
 
-       wil_dbg_WMI(wil, "%s()\n", __func__);
+       wil_dbg_wmi(wil, "%s()\n", __func__);
 
        list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) {
                list_del(&evt->list);
@@ -899,7 +948,7 @@ static void wmi_event_handle(struct wil6210_priv *wil,
                                wmi_evt_call_handler(wil, id, evt_data,
                                                     len - sizeof(*wmi));
                        }
-                       wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id);
+                       wil_dbg_wmi(wil, "Complete WMI 0x%04x\n", id);
                        complete(&wil->wmi_ready);
                        return;
                }
@@ -964,7 +1013,7 @@ void wmi_connect_worker(struct work_struct *work)
                return;
        }
 
-       wil_dbg_WMI(wil, "Configure for connection CID %d\n",
+       wil_dbg_wmi(wil, "Configure for connection CID %d\n",
                    wil->pending_connect_cid);
 
        rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE,
index 97d4e27bf36f3c3f14b336086efe770dbf671f77..aaca60c6f57518a0adfc60b9f61ecbc6d074440a 100644 (file)
@@ -3226,8 +3226,6 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
 {
        struct nphy_gain_ctl_workaround_entry *e;
        u8 phy_idx;
-       u8 tr_iso = ghz5 ? dev->dev->bus_sprom->fem.ghz5.tr_iso :
-                          dev->dev->bus_sprom->fem.ghz2.tr_iso;
 
        if (!ghz5 && dev->phy.rev >= 6 && dev->phy.radio_rev == 11)
                return &nphy_gain_ctl_wa_phy6_radio11_ghz2;
@@ -3249,6 +3247,10 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
                    !b43_channel_type_is_40mhz(dev->phy.channel_type))
                        e->cliplo_gain = 0x2d;
        } else if (!ghz5 && dev->phy.rev >= 5) {
+               static const int gain_data[] = {0x0062, 0x0064, 0x006a, 0x106a,
+                                               0x106c, 0x1074, 0x107c, 0x207c};
+               u8 tr_iso = dev->dev->bus_sprom->fem.ghz2.tr_iso;
+
                if (ext_lna) {
                        e->rfseq_init[0] &= ~0x4000;
                        e->rfseq_init[1] &= ~0x4000;
@@ -3256,26 +3258,10 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
                        e->rfseq_init[3] &= ~0x4000;
                        e->init_gain &= ~0x4000;
                }
-               switch (tr_iso) {
-               case 0:
-                       e->cliplo_gain = 0x0062;
-               case 1:
-                       e->cliplo_gain = 0x0064;
-               case 2:
-                       e->cliplo_gain = 0x006a;
-               case 3:
-                       e->cliplo_gain = 0x106a;
-               case 4:
-                       e->cliplo_gain = 0x106c;
-               case 5:
-                       e->cliplo_gain = 0x1074;
-               case 6:
-                       e->cliplo_gain = 0x107c;
-               case 7:
-                       e->cliplo_gain = 0x207c;
-               default:
-                       e->cliplo_gain = 0x106a;
-               }
+               if (tr_iso > 7)
+                       tr_iso = 3;
+               e->cliplo_gain = gain_data[tr_iso];
+
        } else if (ghz5 && dev->phy.rev == 4 && ext_lna) {
                e->rfseq_init[0] &= ~0x4000;
                e->rfseq_init[1] &= ~0x4000;
index be35a2f99b1cf3fce453e89bdacddacc84806ed1..11fd1c735589f31ab12789ec1067ad143c9a75ce 100644 (file)
@@ -15,8 +15,6 @@
  */
 /* ****************** SDIO CARD Interface Functions **************************/
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
 #include <linux/types.h>
 #include <linux/netdevice.h>
 #include <linux/export.h>
index d33e5598611bbad54808a69aa85b492137ce9df9..d92d373733d74a93b8d9fe6440792ebda5496d7d 100644 (file)
@@ -14,8 +14,6 @@
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
 #include <linux/types.h>
 #include <linux/netdevice.h>
 #include <linux/mmc/sdio.h>
index fd672bf53867f6b7aac803fae15686d0d750ccee..a2f32fb990fa9baa6d2d8b3803f6139bdb32996d 100644 (file)
@@ -39,6 +39,7 @@
 #define BRCMF_C_GET_BSSID                      23
 #define BRCMF_C_GET_SSID                       25
 #define BRCMF_C_SET_SSID                       26
+#define BRCMF_C_TERMINATED                     28
 #define BRCMF_C_GET_CHANNEL                    29
 #define BRCMF_C_SET_CHANNEL                    30
 #define BRCMF_C_GET_SRL                                31
@@ -480,36 +481,14 @@ struct brcmf_pub {
        unsigned long drv_version;      /* Version of dongle-resident driver */
        u8 mac[ETH_ALEN];               /* MAC address obtained from dongle */
 
-       /* Additional stats for the bus level */
-
        /* Multicast data packets sent to dongle */
        unsigned long tx_multicast;
-       /* Packets flushed due to unscheduled sendup thread */
-       unsigned long rx_flushed;
-       /* Number of times dpc scheduled by watchdog timer */
-       unsigned long wd_dpc_sched;
-
-       /* Number of flow control pkts recvd */
-       unsigned long fc_packets;
-
-       /* Last error return */
-       int bcmerror;
-
-       /* Last error from dongle */
-       int dongle_error;
-
-       /* Suspend disable flag  flag */
-       int suspend_disable_flag;       /* "1" to disable all extra powersaving
-                                        during suspend */
-       int in_suspend;         /* flag set to 1 when early suspend called */
-       int dtim_skip;          /* dtim skip , default 0 means wake each dtim */
 
        struct brcmf_if *iflist[BRCMF_MAX_IFS];
 
        struct mutex proto_block;
        unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
 
-       u8 macvalue[ETH_ALEN];
        atomic_t pend_8021x_cnt;
        wait_queue_head_t pend_8021x_wait;
 
@@ -519,11 +498,6 @@ struct brcmf_pub {
 #endif
 };
 
-struct bcmevent_name {
-       uint event;
-       const char *name;
-};
-
 struct brcmf_if_event {
        u8 ifidx;
        u8 action;
@@ -557,13 +531,6 @@ struct brcmf_if {
        u8 mac_addr[ETH_ALEN];
 };
 
-static inline s32 brcmf_ndev_bssidx(struct net_device *ndev)
-{
-       struct brcmf_if *ifp = netdev_priv(ndev);
-       return ifp->bssidx;
-}
-
-extern const struct bcmevent_name bcmevent_names[];
 
 extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev);
 
@@ -576,6 +543,10 @@ extern int brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx,
 extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
                                    void *buf, uint len);
 
+/* Remove any protocol-specific data header. */
+extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx,
+                              struct sk_buff *rxp);
+
 extern int brcmf_net_attach(struct brcmf_if *ifp);
 extern struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, int ifidx,
                                     s32 bssidx, char *name, u8 *mac_addr);
index dd38b78a9726f75861bcb1f92ef3d511a81b92db..64c38f4226a3f145b57973a9369bd0f03f9040a4 100644 (file)
@@ -130,31 +130,18 @@ int brcmf_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint len)
  * interface functions from common layer
  */
 
-/* Remove any protocol-specific data header. */
-extern int brcmf_proto_hdrpull(struct device *dev, int *ifidx,
-                              struct sk_buff *rxp);
-
 extern bool brcmf_c_prec_enq(struct device *dev, struct pktq *q,
                         struct sk_buff *pkt, int prec);
 
 /* Receive frame for delivery to OS.  Callee disposes of rxp. */
-extern void brcmf_rx_frame(struct device *dev, u8 ifidx,
-                          struct sk_buff_head *rxlist);
-static inline void brcmf_rx_packet(struct device *dev, int ifidx,
-                                  struct sk_buff *pkt)
-{
-       struct sk_buff_head q;
-
-       skb_queue_head_init(&q);
-       skb_queue_tail(&q, pkt);
-       brcmf_rx_frame(dev, ifidx, &q);
-}
+extern void brcmf_rx_frames(struct device *dev, struct sk_buff_head *rxlist);
 
 /* Indication from bus module regarding presence/insertion of dongle. */
 extern int brcmf_attach(uint bus_hdrlen, struct device *dev);
 /* Indication from bus module regarding removal/absence of dongle */
 extern void brcmf_detach(struct device *dev);
-
+/* Indication from bus module that dongle should be reset */
+extern void brcmf_dev_reset(struct device *dev);
 /* Indication from bus module to change flow-control state */
 extern void brcmf_txflowblock(struct device *dev, bool state);
 
index 83923553f1ac2cff41acda4677a5fd7cb54d7efd..bb454cdab29dece758af990c35a20c2484cd78a7 100644 (file)
@@ -19,8 +19,6 @@
  * For certain dcmd codes, the dongle interprets string data from the host.
  ******************************************************************************/
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
 #include <linux/types.h>
 #include <linux/netdevice.h>
 
@@ -94,8 +92,6 @@ struct brcmf_proto_bdc_header {
 
 struct brcmf_proto {
        u16 reqid;
-       u8 pending;
-       u32 lastcmd;
        u8 bus_header[BUS_HEADER_LEN];
        struct brcmf_proto_cdc_dcmd msg;
        unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN];
@@ -107,7 +103,7 @@ static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr)
        int len = le32_to_cpu(prot->msg.len) +
                        sizeof(struct brcmf_proto_cdc_dcmd);
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(CDC, "Enter\n");
 
        /* NOTE : cdc->msg.len holds the desired length of the buffer to be
         *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
@@ -125,7 +121,7 @@ static int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
        int ret;
        struct brcmf_proto *prot = drvr->prot;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(CDC, "Enter\n");
        len += sizeof(struct brcmf_proto_cdc_dcmd);
        do {
                ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&prot->msg,
@@ -147,20 +143,7 @@ brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
        int ret = 0, retries = 0;
        u32 id, flags;
 
-       brcmf_dbg(TRACE, "Enter\n");
-       brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len);
-
-       /* Respond "bcmerror" and "bcmerrorstr" with local cache */
-       if (cmd == BRCMF_C_GET_VAR && buf) {
-               if (!strcmp((char *)buf, "bcmerrorstr")) {
-                       strncpy((char *)buf, "bcm_error",
-                               BCME_STRLEN);
-                       goto done;
-               } else if (!strcmp((char *)buf, "bcmerror")) {
-                       *(int *)buf = drvr->dongle_error;
-                       goto done;
-               }
-       }
+       brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len);
 
        memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
 
@@ -210,11 +193,8 @@ retry:
        }
 
        /* Check the ERROR flag */
-       if (flags & CDC_DCMD_ERROR) {
+       if (flags & CDC_DCMD_ERROR)
                ret = le32_to_cpu(msg->status);
-               /* Cache error from dongle */
-               drvr->dongle_error = ret;
-       }
 
 done:
        return ret;
@@ -228,8 +208,7 @@ int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
        int ret = 0;
        u32 flags, id;
 
-       brcmf_dbg(TRACE, "Enter\n");
-       brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len);
+       brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len);
 
        memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
 
@@ -262,11 +241,8 @@ int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
        }
 
        /* Check the ERROR flag */
-       if (flags & CDC_DCMD_ERROR) {
+       if (flags & CDC_DCMD_ERROR)
                ret = le32_to_cpu(msg->status);
-               /* Cache error from dongle */
-               drvr->dongle_error = ret;
-       }
 
 done:
        return ret;
@@ -287,7 +263,7 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
 {
        struct brcmf_proto_bdc_header *h;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(CDC, "Enter\n");
 
        /* Push BDC header used to convey priority for buses that don't */
 
@@ -305,14 +281,12 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
        BDC_SET_IF_IDX(h, ifidx);
 }
 
-int brcmf_proto_hdrpull(struct device *dev, int *ifidx,
+int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx,
                        struct sk_buff *pktbuf)
 {
        struct brcmf_proto_bdc_header *h;
-       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
-       struct brcmf_pub *drvr = bus_if->drvr;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(CDC, "Enter\n");
 
        /* Pop BDC header used to convey priority for buses that don't */
 
@@ -338,7 +312,7 @@ int brcmf_proto_hdrpull(struct device *dev, int *ifidx,
        }
 
        if (h->flags & BDC_FLAG_SUM_GOOD) {
-               brcmf_dbg(INFO, "%s: BDC packet received with good rx-csum, flags 0x%x\n",
+               brcmf_dbg(CDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
                          brcmf_ifname(drvr, *ifidx), h->flags);
                pkt_set_sum_good(pktbuf, true);
        }
@@ -348,6 +322,8 @@ int brcmf_proto_hdrpull(struct device *dev, int *ifidx,
        skb_pull(pktbuf, BDC_HEADER_LEN);
        skb_pull(pktbuf, h->data_offset << 2);
 
+       if (pktbuf->len == 0)
+               return -ENODATA;
        return 0;
 }
 
index f8b52e5b941a7b3c738701a067a92eae2b84c7e9..4544342a04281d84e67fd93222a50ce3ac2fdc7c 100644 (file)
@@ -14,8 +14,6 @@
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/netdevice.h>
index f2ab01cd796600643c94f6e98da09429d2cbedaf..bc013cbe06f611647829694b97277423e43a7d5e 100644 (file)
 #define _BRCMF_DBG_H_
 
 /* message levels */
-#define BRCMF_TRACE_VAL        0x0002
-#define BRCMF_INFO_VAL 0x0004
-#define BRCMF_DATA_VAL 0x0008
-#define BRCMF_CTL_VAL  0x0010
-#define BRCMF_TIMER_VAL        0x0020
-#define BRCMF_HDRS_VAL 0x0040
-#define BRCMF_BYTES_VAL        0x0080
-#define BRCMF_INTR_VAL 0x0100
-#define BRCMF_GLOM_VAL 0x0200
-#define BRCMF_EVENT_VAL        0x0400
-#define BRCMF_BTA_VAL  0x0800
-#define BRCMF_FIL_VAL  0x1000
-#define BRCMF_USB_VAL  0x2000
-#define BRCMF_SCAN_VAL 0x4000
-#define BRCMF_CONN_VAL 0x8000
+#define BRCMF_TRACE_VAL        0x00000002
+#define BRCMF_INFO_VAL 0x00000004
+#define BRCMF_DATA_VAL 0x00000008
+#define BRCMF_CTL_VAL  0x00000010
+#define BRCMF_TIMER_VAL        0x00000020
+#define BRCMF_HDRS_VAL 0x00000040
+#define BRCMF_BYTES_VAL        0x00000080
+#define BRCMF_INTR_VAL 0x00000100
+#define BRCMF_GLOM_VAL 0x00000200
+#define BRCMF_EVENT_VAL        0x00000400
+#define BRCMF_BTA_VAL  0x00000800
+#define BRCMF_FIL_VAL  0x00001000
+#define BRCMF_USB_VAL  0x00002000
+#define BRCMF_SCAN_VAL 0x00004000
+#define BRCMF_CONN_VAL 0x00008000
+#define BRCMF_CDC_VAL  0x00010000
+
+/* set default print format */
+#undef pr_fmt
+#define pr_fmt(fmt)            KBUILD_MODNAME ": " fmt
 
 /* Macro for error messages. net_ratelimit() is used when driver
  * debugging is not selected. When debugging the driver error
index 74a616b4de8e027c5f877f2910a526fed9d29aa3..e3326a58bdb17241a9c29e786550dbcccacd09d9 100644 (file)
@@ -14,8 +14,6 @@
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
 #include <linux/kernel.h>
 #include <linux/etherdevice.h>
 #include <linux/module.h>
@@ -162,28 +160,31 @@ static void brcmf_netdev_set_multicast_list(struct net_device *ndev)
        schedule_work(&ifp->multicast_work);
 }
 
-static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
+                                          struct net_device *ndev)
 {
        int ret;
        struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_pub *drvr = ifp->drvr;
+       struct ethhdr *eh;
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       /* Reject if down */
-       if (!drvr->bus_if->drvr_up ||
-           (drvr->bus_if->state != BRCMF_BUS_DATA)) {
-               brcmf_err("xmit rejected drvup=%d state=%d\n",
-                         drvr->bus_if->drvr_up,
-                         drvr->bus_if->state);
+       /* Can the device send data? */
+       if (drvr->bus_if->state != BRCMF_BUS_DATA) {
+               brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state);
                netif_stop_queue(ndev);
-               return -ENODEV;
+               dev_kfree_skb(skb);
+               ret = -ENODEV;
+               goto done;
        }
 
        if (!drvr->iflist[ifp->idx]) {
                brcmf_err("bad ifidx %d\n", ifp->idx);
                netif_stop_queue(ndev);
-               return -ENODEV;
+               dev_kfree_skb(skb);
+               ret = -ENODEV;
+               goto done;
        }
 
        /* Make sure there's enough room for any header */
@@ -204,17 +205,20 @@ static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                }
        }
 
-       /* Update multicast statistic */
-       if (skb->len >= ETH_ALEN) {
-               u8 *pktdata = (u8 *)(skb->data);
-               struct ethhdr *eh = (struct ethhdr *)pktdata;
-
-               if (is_multicast_ether_addr(eh->h_dest))
-                       drvr->tx_multicast++;
-               if (ntohs(eh->h_proto) == ETH_P_PAE)
-                       atomic_inc(&drvr->pend_8021x_cnt);
+       /* validate length for ether packet */
+       if (skb->len < sizeof(*eh)) {
+               ret = -EINVAL;
+               dev_kfree_skb(skb);
+               goto done;
        }
 
+       /* handle ethernet header */
+       eh = (struct ethhdr *)(skb->data);
+       if (is_multicast_ether_addr(eh->h_dest))
+               drvr->tx_multicast++;
+       if (ntohs(eh->h_proto) == ETH_P_PAE)
+               atomic_inc(&drvr->pend_8021x_cnt);
+
        /* If the protocol uses a data header, apply it */
        brcmf_proto_hdrpush(drvr, ifp->idx, skb);
 
@@ -228,7 +232,7 @@ done:
                drvr->bus_if->dstats.tx_packets++;
 
        /* Return ok: we always eat the packet */
-       return 0;
+       return NETDEV_TX_OK;
 }
 
 void brcmf_txflowblock(struct device *dev, bool state)
@@ -250,8 +254,7 @@ void brcmf_txflowblock(struct device *dev, bool state)
                }
 }
 
-void brcmf_rx_frame(struct device *dev, u8 ifidx,
-                   struct sk_buff_head *skb_list)
+void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
 {
        unsigned char *eth;
        uint len;
@@ -259,12 +262,24 @@ void brcmf_rx_frame(struct device *dev, u8 ifidx,
        struct brcmf_if *ifp;
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_pub *drvr = bus_if->drvr;
+       u8 ifidx;
+       int ret;
 
        brcmf_dbg(TRACE, "Enter\n");
 
        skb_queue_walk_safe(skb_list, skb, pnext) {
                skb_unlink(skb, skb_list);
 
+               /* process and remove protocol-specific header
+                */
+               ret = brcmf_proto_hdrpull(drvr, &ifidx, skb);
+               if (ret < 0) {
+                       if (ret != -ENODATA)
+                               bus_if->dstats.rx_errors++;
+                       brcmu_pkt_buf_free_skb(skb);
+                       continue;
+               }
+
                /* Get the protocol, maintain skb around eth_type_trans()
                 * The main reason for this hack is for the limitation of
                 * Linux 2.4 where 'eth_type_trans' uses the
@@ -328,13 +343,13 @@ void brcmf_rx_frame(struct device *dev, u8 ifidx,
 
 void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
 {
-       uint ifidx;
+       u8 ifidx;
        struct ethhdr *eh;
        u16 type;
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_pub *drvr = bus_if->drvr;
 
-       brcmf_proto_hdrpull(dev, &ifidx, txp);
+       brcmf_proto_hdrpull(drvr, &ifidx, txp);
 
        eh = (struct ethhdr *)(txp->data);
        type = ntohs(eh->h_proto);
@@ -450,7 +465,7 @@ static int brcmf_ethtool(struct brcmf_if *ifp, void __user *uaddr)
                sprintf(info.version, "%lu", drvr->drv_version);
                if (copy_to_user(uaddr, &info, sizeof(info)))
                        return -EFAULT;
-               brcmf_dbg(CTL, "given %*s, returning %s\n",
+               brcmf_dbg(TRACE, "given %*s, returning %s\n",
                          (int)sizeof(drvname), drvname, info.driver);
                break;
 
@@ -570,14 +585,9 @@ static int brcmf_netdev_open(struct net_device *ndev)
        /* Get current TOE mode from dongle */
        if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0
            && (toe_ol & TOE_TX_CSUM_OL) != 0)
-               drvr->iflist[ifp->idx]->ndev->features |=
-                       NETIF_F_IP_CSUM;
+               ndev->features |= NETIF_F_IP_CSUM;
        else
-               drvr->iflist[ifp->idx]->ndev->features &=
-                       ~NETIF_F_IP_CSUM;
-
-       /* make sure RF is ready for work */
-       brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
+               ndev->features &= ~NETIF_F_IP_CSUM;
 
        /* Allow transmit calls */
        netif_start_queue(ndev);
@@ -845,6 +855,17 @@ static void brcmf_bus_detach(struct brcmf_pub *drvr)
        }
 }
 
+void brcmf_dev_reset(struct device *dev)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_pub *drvr = bus_if->drvr;
+
+       if (drvr == NULL)
+               return;
+
+       brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1);
+}
+
 void brcmf_detach(struct device *dev)
 {
        int i;
@@ -866,9 +887,8 @@ void brcmf_detach(struct device *dev)
 
        brcmf_bus_detach(drvr);
 
-       if (drvr->prot) {
+       if (drvr->prot)
                brcmf_proto_detach(drvr);
-       }
 
        brcmf_debugfs_detach(drvr);
        bus_if->drvr = NULL;
index cf857f1edf8c3b84d2d92c5f2b41874fae88ea82..7fef9b5ba00364b2c65d88049e580a06288e0032 100644 (file)
@@ -14,8 +14,6 @@
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/kthread.h>
@@ -1169,7 +1167,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
        int errcode;
        u8 doff, sfdoff;
 
-       int ifidx = 0;
        bool usechain = bus->use_rxchain;
 
        struct brcmf_sdio_read rd_new;
@@ -1388,13 +1385,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                                skb_unlink(pfirst, &bus->glom);
                                brcmu_pkt_buf_free_skb(pfirst);
                                continue;
-                       } else if (brcmf_proto_hdrpull(bus->sdiodev->dev,
-                                                      &ifidx, pfirst) != 0) {
-                               brcmf_err("rx protocol error\n");
-                               bus->sdiodev->bus_if->dstats.rx_errors++;
-                               skb_unlink(pfirst, &bus->glom);
-                               brcmu_pkt_buf_free_skb(pfirst);
-                               continue;
                        }
 
                        brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
@@ -1407,7 +1397,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                }
                /* sent any remaining packets up */
                if (bus->glom.qlen)
-                       brcmf_rx_frame(bus->sdiodev->dev, ifidx, &bus->glom);
+                       brcmf_rx_frames(bus->sdiodev->dev, &bus->glom);
 
                bus->sdcnt.rxglomframes++;
                bus->sdcnt.rxglompkts += bus->glom.qlen;
@@ -1558,10 +1548,10 @@ static void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen)
 static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
 {
        struct sk_buff *pkt;            /* Packet for event or data frames */
+       struct sk_buff_head pktlist;    /* needed for bus interface */
        u16 pad;                /* Number of pad bytes to read */
        uint rxleft = 0;        /* Remaining number of frames allowed */
        int sdret;              /* Return code from calls */
-       int ifidx = 0;
        uint rxcount = 0;       /* Total frames read */
        struct brcmf_sdio_read *rd = &bus->cur_read, rd_new;
        u8 head_read = 0;
@@ -1760,15 +1750,11 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                if (pkt->len == 0) {
                        brcmu_pkt_buf_free_skb(pkt);
                        continue;
-               } else if (brcmf_proto_hdrpull(bus->sdiodev->dev, &ifidx,
-                          pkt) != 0) {
-                       brcmf_err("rx protocol error\n");
-                       brcmu_pkt_buf_free_skb(pkt);
-                       bus->sdiodev->bus_if->dstats.rx_errors++;
-                       continue;
                }
 
-               brcmf_rx_packet(bus->sdiodev->dev, ifidx, pkt);
+               skb_queue_head_init(&pktlist);
+               skb_queue_tail(&pktlist, pkt);
+               brcmf_rx_frames(bus->sdiodev->dev, &pktlist);
        }
 
        rxcount = maxframes - rxleft;
index b1bb46c49799f6391503fdca17ee81116eb32583..14be2d5530cebe3498a010cec9ba8bd09cabd0b6 100644 (file)
@@ -15,8 +15,6 @@
  */
 /* ***** SDIO interface chip backplane handle functions ***** */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
 #include <linux/types.h>
 #include <linux/netdevice.h>
 #include <linux/mmc/card.h>
index 914c56fe6c5f72ec260e27779ebac182b3d2dd0a..e15630cc3889fcb5f4644e9332ad60ecb68d679f 100644 (file)
@@ -443,14 +443,15 @@ static void brcmf_usb_rx_complete(struct urb *urb)
        struct brcmf_usbreq  *req = (struct brcmf_usbreq *)urb->context;
        struct brcmf_usbdev_info *devinfo = req->devinfo;
        struct sk_buff *skb;
-       int ifidx = 0;
+       struct sk_buff_head skbq;
 
        brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status);
        brcmf_usb_del_fromq(devinfo, req);
        skb = req->skb;
        req->skb = NULL;
 
-       if (urb->status == 0) {
+       /* zero lenght packets indicate usb "failure". Do not refill */
+       if (urb->status == 0 && urb->actual_length) {
                devinfo->bus_pub.bus->dstats.rx_packets++;
        } else {
                devinfo->bus_pub.bus->dstats.rx_errors++;
@@ -460,13 +461,10 @@ static void brcmf_usb_rx_complete(struct urb *urb)
        }
 
        if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
+               skb_queue_head_init(&skbq);
+               skb_queue_tail(&skbq, skb);
                skb_put(skb, urb->actual_length);
-               if (brcmf_proto_hdrpull(devinfo->dev, &ifidx, skb) != 0) {
-                       brcmf_err("rx protocol error\n");
-                       brcmu_pkt_buf_free_skb(skb);
-                       devinfo->bus_pub.bus->dstats.rx_errors++;
-               } else
-                       brcmf_rx_packet(devinfo->dev, ifidx, skb);
+               brcmf_rx_frames(devinfo->dev, &skbq);
                brcmf_usb_rx_refill(devinfo, req);
        } else {
                brcmu_pkt_buf_free_skb(skb);
@@ -1520,10 +1518,23 @@ static void brcmf_release_fw(struct list_head *q)
        }
 }
 
+static int brcmf_usb_reset_device(struct device *dev, void *notused)
+{
+       /* device past is the usb interface so we
+        * need to use parent here.
+        */
+       brcmf_dev_reset(dev->parent);
+       return 0;
+}
 
 void brcmf_usb_exit(void)
 {
+       struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver;
+       int ret;
+
        brcmf_dbg(USB, "Enter\n");
+       ret = driver_for_each_device(drv, NULL, NULL,
+                                    brcmf_usb_reset_device);
        usb_deregister(&brcmf_usbdrvr);
        brcmf_release_fw(&fw_image_list);
 }
index 75464ad4fbd188ce1fb135302539dfa02caa3fbb..62a528e8b958bf03200fe1d87520db5bbb2b5cb9 100644 (file)
@@ -16,8 +16,6 @@
 
 /* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
 #include <linux/kernel.h>
 #include <linux/etherdevice.h>
 #include <net/cfg80211.h>
@@ -2011,67 +2009,6 @@ done:
        return err;
 }
 
-static s32
-brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *ndev,
-                            const u8 *addr,
-                            const struct cfg80211_bitrate_mask *mask)
-{
-       struct brcmf_if *ifp = netdev_priv(ndev);
-       struct brcm_rateset_le rateset_le;
-       s32 rate;
-       s32 val;
-       s32 err_bg;
-       s32 err_a;
-       u32 legacy;
-       s32 err = 0;
-
-       brcmf_dbg(TRACE, "Enter\n");
-       if (!check_vif_up(ifp->vif))
-               return -EIO;
-
-       /* addr param is always NULL. ignore it */
-       /* Get current rateset */
-       err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_CURR_RATESET,
-                                    &rateset_le, sizeof(rateset_le));
-       if (err) {
-               brcmf_err("could not get current rateset (%d)\n", err);
-               goto done;
-       }
-
-       legacy = ffs(mask->control[IEEE80211_BAND_2GHZ].legacy & 0xFFFF);
-       if (!legacy)
-               legacy = ffs(mask->control[IEEE80211_BAND_5GHZ].legacy &
-                            0xFFFF);
-
-       val = wl_g_rates[legacy - 1].bitrate * 100000;
-
-       if (val < le32_to_cpu(rateset_le.count))
-               /* Select rate by rateset index */
-               rate = rateset_le.rates[val] & 0x7f;
-       else
-               /* Specified rate in bps */
-               rate = val / 500000;
-
-       brcmf_dbg(CONN, "rate %d mbps\n", rate / 2);
-
-       /*
-        *
-        *      Set rate override,
-        *      Since the is a/b/g-blind, both a/bg_rate are enforced.
-        */
-       err_bg = brcmf_fil_iovar_int_set(ifp, "bg_rate", rate);
-       err_a = brcmf_fil_iovar_int_set(ifp, "a_rate", rate);
-       if (err_bg && err_a) {
-               brcmf_err("could not set fixed rate (%d) (%d)\n", err_bg,
-                         err_a);
-               err = err_bg | err_a;
-       }
-
-done:
-       brcmf_dbg(TRACE, "Exit\n");
-       return err;
-}
-
 static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
                                   struct brcmf_bss_info_le *bi)
 {
@@ -3704,7 +3641,6 @@ static struct cfg80211_ops wl_cfg80211_ops = {
        .set_default_key = brcmf_cfg80211_config_default_key,
        .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
        .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
-       .set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask,
        .connect = brcmf_cfg80211_connect,
        .disconnect = brcmf_cfg80211_disconnect,
        .suspend = brcmf_cfg80211_suspend,
@@ -4330,9 +4266,8 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
 }
 
 static s32
-brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
+brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout)
 {
-       struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err = 0;
        __le32 roamtrigger[2];
        __le32 roam_delta[2];
@@ -4383,10 +4318,9 @@ dongle_rom_out:
 }
 
 static s32
-brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
+brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
                      s32 scan_unassoc_time, s32 scan_passive_time)
 {
-       struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err = 0;
 
        err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
@@ -4456,6 +4390,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
 {
        struct net_device *ndev;
        struct wireless_dev *wdev;
+       struct brcmf_if *ifp;
        s32 power_mode;
        s32 err = 0;
 
@@ -4464,35 +4399,34 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
 
        ndev = cfg_to_ndev(cfg);
        wdev = ndev->ieee80211_ptr;
+       ifp = netdev_priv(ndev);
 
-       brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME,
-                       WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
+       /* make sure RF is ready for work */
+       brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
+
+       brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
+                             WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
 
        power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
-       err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PM,
-                                   power_mode);
+       err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
        if (err)
                goto default_conf_out;
        brcmf_dbg(INFO, "power save set to %s\n",
                  (power_mode ? "enabled" : "disabled"));
 
-       err = brcmf_dongle_roam(ndev, (cfg->roam_on ? 0 : 1),
-                               WL_BEACON_TIMEOUT);
+       err = brcmf_dongle_roam(ifp, (cfg->roam_on ? 0 : 1), WL_BEACON_TIMEOUT);
        if (err)
                goto default_conf_out;
        err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
                                          NULL, NULL);
-       if (err && err != -EINPROGRESS)
+       if (err)
                goto default_conf_out;
        err = brcmf_dongle_probecap(cfg);
        if (err)
                goto default_conf_out;
 
-       /* -EINPROGRESS: Call commit handler */
-
-default_conf_out:
-
        cfg->dongle_up = true;
+default_conf_out:
 
        return err;
 
@@ -4501,8 +4435,6 @@ default_conf_out:
 static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
 {
        set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
-       if (ifp->idx)
-               return 0;
 
        return brcmf_config_dongle(ifp->drvr->config);
 }
index 1de94f30564fe8291c99657ced06d33ff6af2e5b..1585cc5bf866b847e86f6210074b4c89b6c0b26f 100644 (file)
@@ -961,7 +961,6 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
                        /* if acked then clear bit and free packet */
                        if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
                            && isset(bitmap, bindex)) {
-                               ini->tx_in_transit--;
                                ini->txretry[index] = 0;
 
                                /*
@@ -990,7 +989,6 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
                        if (retry && (ini->txretry[index] < (int)retry_limit)) {
                                int ret;
                                ini->txretry[index]++;
-                               ini->tx_in_transit--;
                                ret = brcms_c_txfifo(wlc, queue, p);
                                /*
                                 * We shouldn't be out of space in the DMA
@@ -1000,7 +998,6 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
                                WARN_ONCE(ret, "queue %d out of txds\n", queue);
                        } else {
                                /* Retry timeout */
-                               ini->tx_in_transit--;
                                ieee80211_tx_info_clear_status(tx_info);
                                tx_info->status.ampdu_ack_len = 0;
                                tx_info->status.ampdu_len = 1;
@@ -1009,8 +1006,8 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
                                skb_pull(p, D11_PHY_HDR_LEN);
                                skb_pull(p, D11_TXH_LEN);
                                brcms_dbg_ht(wlc->hw->d11core,
-                                            "BA Timeout, seq %d, in_transit %d\n",
-                                            seq, ini->tx_in_transit);
+                                            "BA Timeout, seq %d\n",
+                                            seq);
                                ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
                                                            p);
                        }
index a90b72202ec5a298b9cf6483cd5380ae5ef74ef0..cdb62b8ccc79c6c6ad6ea68269d3f03008d6e3e3 100644 (file)
@@ -670,7 +670,7 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy,
        struct ieee80211_supported_band *sband;
        struct ieee80211_channel *ch;
        const struct ieee80211_reg_rule *rule;
-       int band, i, ret;
+       int band, i;
 
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                sband = wiphy->bands[band];
@@ -685,9 +685,8 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy,
                                continue;
 
                        if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
-                               ret = freq_reg_info(wiphy, ch->center_freq,
-                                                   0, &rule);
-                               if (ret)
+                               rule = freq_reg_info(wiphy, ch->center_freq);
+                               if (IS_ERR(rule))
                                        continue;
 
                                if (!(rule->flags & NL80211_RRF_NO_IBSS))
@@ -703,8 +702,8 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy,
        }
 }
 
-static int brcms_reg_notifier(struct wiphy *wiphy,
-                             struct regulatory_request *request)
+static void brcms_reg_notifier(struct wiphy *wiphy,
+                              struct regulatory_request *request)
 {
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct brcms_info *wl = hw->priv;
@@ -745,8 +744,6 @@ static int brcms_reg_notifier(struct wiphy *wiphy,
        if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G)
                wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi,
                                        brcms_c_japan_ccode(request->alpha2));
-
-       return 0;
 }
 
 void brcms_c_regd_init(struct brcms_c_info *wlc)
index e5fd20994bec256df4ec88f96182006e54ada0d6..c6451c61407a8c4510c97056bde23ef1ee88c060 100644 (file)
@@ -363,8 +363,11 @@ brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
                return -EOPNOTSUPP;
        }
 
+       spin_lock_bh(&wl->lock);
+       memcpy(wl->pub->cur_etheraddr, vif->addr, sizeof(vif->addr));
        wl->mute_tx = false;
        brcms_c_mute(wl->wlc, false);
+       spin_unlock_bh(&wl->lock);
 
        return 0;
 }
@@ -540,9 +543,8 @@ brcms_ops_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_ARP_FILTER) {
                /* Hardware ARP filter address list or state changed */
-               brcms_err(core, "%s: arp filtering: enabled %s, count %d"
-                         " (implement)\n", __func__, info->arp_filter_enabled ?
-                         "true" : "false", info->arp_addr_cnt);
+               brcms_err(core, "%s: arp filtering: %d addresses"
+                         " (implement)\n", __func__, info->arp_addr_cnt);
        }
 
        if (changed & BSS_CHANGED_QOS) {
@@ -669,7 +671,9 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw,
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                break;
 
-       case IEEE80211_AMPDU_TX_STOP:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
                spin_lock_bh(&wl->lock);
                brcms_c_ampdu_flush(wl->wlc, sta, tid);
                spin_unlock_bh(&wl->lock);
index 8b5839008af32a11afef6d4042db67a0d8a9c16d..62be5502b95deae29cfb0ca1bd7618efa5aa9bdd 100644 (file)
@@ -2466,6 +2466,7 @@ static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw,
 static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx)
 {
        static const u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
+       u8 *ethaddr = wlc_hw->wlc->pub->cur_etheraddr;
 
        if (mute_tx) {
                /* suspend tx fifos */
@@ -2475,8 +2476,7 @@ static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx)
                brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_VI_FIFO);
 
                /* zero the address match register so we do not send ACKs */
-               brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET,
-                                      null_ether_addr);
+               brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, null_ether_addr);
        } else {
                /* resume tx fifos */
                brcms_b_tx_fifo_resume(wlc_hw, TX_DATA_FIFO);
@@ -2485,8 +2485,7 @@ static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx)
                brcms_b_tx_fifo_resume(wlc_hw, TX_AC_VI_FIFO);
 
                /* Restore address */
-               brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET,
-                                      wlc_hw->etheraddr);
+               brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, ethaddr);
        }
 
        wlc_phy_mute_upd(wlc_hw->band->pi, mute_tx, 0);
@@ -7617,7 +7616,7 @@ brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound)
 
        uint n = 0;
        uint bound_limit = bound ? RXBND : -1;
-       bool morepending;
+       bool morepending = false;
 
        skb_queue_head_init(&recv_frames);
 
index 51c79c7239b7c12bd4888036e3a93e9673d0c017..3a3d73699f83aad95431e224e7861c0a19a1a069 100644 (file)
@@ -36,7 +36,6 @@
 
 /* structure to store per-tid state for the ampdu initiator */
 struct scb_ampdu_tid_ini {
-       u8 tx_in_transit; /* number of pending mpdus in transit in driver */
        u8 tid;           /* initiator tid for easy lookup */
        /* tx retry count; indexed by seq modulo */
        u8 txretry[AMPDU_TX_BA_MAX_WSIZE];
index 3726cd6fcd754812d65cb38d89782e96aace0de8..83856d1a61012b05645ab2a336d2194c671bcdf4 100644 (file)
@@ -1001,12 +1001,12 @@ il3945_rx_allocate(struct il_priv *il, gfp_t priority)
        struct list_head *element;
        struct il_rx_buf *rxb;
        struct page *page;
+       dma_addr_t page_dma;
        unsigned long flags;
        gfp_t gfp_mask = priority;
 
        while (1) {
                spin_lock_irqsave(&rxq->lock, flags);
-
                if (list_empty(&rxq->rx_used)) {
                        spin_unlock_irqrestore(&rxq->lock, flags);
                        return;
@@ -1035,26 +1035,34 @@ il3945_rx_allocate(struct il_priv *il, gfp_t priority)
                        break;
                }
 
+               /* Get physical address of RB/SKB */
+               page_dma =
+                   pci_map_page(il->pci_dev, page, 0,
+                                PAGE_SIZE << il->hw_params.rx_page_order,
+                                PCI_DMA_FROMDEVICE);
+
+               if (unlikely(pci_dma_mapping_error(il->pci_dev, page_dma))) {
+                       __free_pages(page, il->hw_params.rx_page_order);
+                       break;
+               }
+
                spin_lock_irqsave(&rxq->lock, flags);
+
                if (list_empty(&rxq->rx_used)) {
                        spin_unlock_irqrestore(&rxq->lock, flags);
+                       pci_unmap_page(il->pci_dev, page_dma,
+                                      PAGE_SIZE << il->hw_params.rx_page_order,
+                                      PCI_DMA_FROMDEVICE);
                        __free_pages(page, il->hw_params.rx_page_order);
                        return;
                }
+
                element = rxq->rx_used.next;
                rxb = list_entry(element, struct il_rx_buf, list);
                list_del(element);
-               spin_unlock_irqrestore(&rxq->lock, flags);
 
                rxb->page = page;
-               /* Get physical address of RB/SKB */
-               rxb->page_dma =
-                   pci_map_page(il->pci_dev, page, 0,
-                                PAGE_SIZE << il->hw_params.rx_page_order,
-                                PCI_DMA_FROMDEVICE);
-
-               spin_lock_irqsave(&rxq->lock, flags);
-
+               rxb->page_dma = page_dma;
                list_add_tail(&rxb->list, &rxq->rx_free);
                rxq->free_count++;
                il->alloc_rxb_page++;
@@ -1284,8 +1292,15 @@ il3945_rx_handle(struct il_priv *il)
                            pci_map_page(il->pci_dev, rxb->page, 0,
                                         PAGE_SIZE << il->hw_params.
                                         rx_page_order, PCI_DMA_FROMDEVICE);
-                       list_add_tail(&rxb->list, &rxq->rx_free);
-                       rxq->free_count++;
+                       if (unlikely(pci_dma_mapping_error(il->pci_dev,
+                                                          rxb->page_dma))) {
+                               __il_free_pages(il, rxb->page);
+                               rxb->page = NULL;
+                               list_add_tail(&rxb->list, &rxq->rx_used);
+                       } else {
+                               list_add_tail(&rxb->list, &rxq->rx_free);
+                               rxq->free_count++;
+                       }
                } else
                        list_add_tail(&rxb->list, &rxq->rx_used);
 
@@ -3474,6 +3489,7 @@ struct ieee80211_ops il3945_mac_ops = {
        .sta_add = il3945_mac_sta_add,
        .sta_remove = il_mac_sta_remove,
        .tx_last_beacon = il_mac_tx_last_beacon,
+       .flush = il_mac_flush,
 };
 
 static int
@@ -3548,7 +3564,8 @@ il3945_setup_mac(struct il_priv *il)
        hw->vif_data_size = sizeof(struct il_vif_priv);
 
        /* Tell mac80211 our characteristics */
-       hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SPECTRUM_MGMT;
+       hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SPECTRUM_MGMT |
+                   IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
 
        hw->wiphy->interface_modes =
            BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC);
@@ -3557,6 +3574,8 @@ il3945_setup_mac(struct il_priv *il)
            WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS |
            WIPHY_FLAG_IBSS_RSN;
 
+       hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
        hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX_3945;
        /* we create the 802.11 header and a zero-length SSID element */
        hw->wiphy->max_scan_ie_len = IL3945_MAX_PROBE_REQUEST - 24 - 2;
index c3fbf6717564a3295ea2a87ecdc669e80d2e9d89..9741ac10a3343391e4e455da2ffda0f86446d520 100644 (file)
@@ -319,6 +319,7 @@ il4965_rx_allocate(struct il_priv *il, gfp_t priority)
        struct list_head *element;
        struct il_rx_buf *rxb;
        struct page *page;
+       dma_addr_t page_dma;
        unsigned long flags;
        gfp_t gfp_mask = priority;
 
@@ -356,33 +357,35 @@ il4965_rx_allocate(struct il_priv *il, gfp_t priority)
                        return;
                }
 
+               /* Get physical address of the RB */
+               page_dma =
+                   pci_map_page(il->pci_dev, page, 0,
+                                PAGE_SIZE << il->hw_params.rx_page_order,
+                                PCI_DMA_FROMDEVICE);
+               if (unlikely(pci_dma_mapping_error(il->pci_dev, page_dma))) {
+                       __free_pages(page, il->hw_params.rx_page_order);
+                       break;
+               }
+
                spin_lock_irqsave(&rxq->lock, flags);
 
                if (list_empty(&rxq->rx_used)) {
                        spin_unlock_irqrestore(&rxq->lock, flags);
+                       pci_unmap_page(il->pci_dev, page_dma,
+                                      PAGE_SIZE << il->hw_params.rx_page_order,
+                                      PCI_DMA_FROMDEVICE);
                        __free_pages(page, il->hw_params.rx_page_order);
                        return;
                }
+
                element = rxq->rx_used.next;
                rxb = list_entry(element, struct il_rx_buf, list);
                list_del(element);
 
-               spin_unlock_irqrestore(&rxq->lock, flags);
-
                BUG_ON(rxb->page);
-               rxb->page = page;
-               /* Get physical address of the RB */
-               rxb->page_dma =
-                   pci_map_page(il->pci_dev, page, 0,
-                                PAGE_SIZE << il->hw_params.rx_page_order,
-                                PCI_DMA_FROMDEVICE);
-               /* dma address must be no more than 36 bits */
-               BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36));
-               /* and also 256 byte aligned! */
-               BUG_ON(rxb->page_dma & DMA_BIT_MASK(8));
-
-               spin_lock_irqsave(&rxq->lock, flags);
 
+               rxb->page = page;
+               rxb->page_dma = page_dma;
                list_add_tail(&rxb->list, &rxq->rx_free);
                rxq->free_count++;
                il->alloc_rxb_page++;
@@ -725,6 +728,16 @@ il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
        if (rate_n_flags & RATE_MCS_SGI_MSK)
                rx_status.flag |= RX_FLAG_SHORT_GI;
 
+       if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) {
+               /* We know which subframes of an A-MPDU belong
+                * together since we get a single PHY response
+                * from the firmware for all of them.
+                */
+
+               rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
+               rx_status.ampdu_reference = il->_4965.ampdu_ref;
+       }
+
        il4965_pass_packet_to_mac80211(il, header, len, ampdu_status, rxb,
                                       &rx_status);
 }
@@ -736,6 +749,7 @@ il4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb)
 {
        struct il_rx_pkt *pkt = rxb_addr(rxb);
        il->_4965.last_phy_res_valid = true;
+       il->_4965.ampdu_ref++;
        memcpy(&il->_4965.last_phy_res, pkt->u.raw,
               sizeof(struct il_rx_phy_res));
 }
@@ -4281,8 +4295,16 @@ il4965_rx_handle(struct il_priv *il)
                            pci_map_page(il->pci_dev, rxb->page, 0,
                                         PAGE_SIZE << il->hw_params.
                                         rx_page_order, PCI_DMA_FROMDEVICE);
-                       list_add_tail(&rxb->list, &rxq->rx_free);
-                       rxq->free_count++;
+
+                       if (unlikely(pci_dma_mapping_error(il->pci_dev,
+                                                          rxb->page_dma))) {
+                               __il_free_pages(il, rxb->page);
+                               rxb->page = NULL;
+                               list_add_tail(&rxb->list, &rxq->rx_used);
+                       } else {
+                               list_add_tail(&rxb->list, &rxq->rx_free);
+                               rxq->free_count++;
+                       }
                } else
                        list_add_tail(&rxb->list, &rxq->rx_used);
 
@@ -5712,8 +5734,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length)
        hw->flags =
            IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION |
            IEEE80211_HW_NEED_DTIM_PERIOD | IEEE80211_HW_SPECTRUM_MGMT |
-           IEEE80211_HW_REPORTS_TX_ACK_STATUS;
-
+           IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS |
+           IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
        if (il->cfg->sku & IL_SKU_N)
                hw->flags |=
                    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
@@ -5968,7 +5990,9 @@ il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                D_HT("start Tx\n");
                ret = il4965_tx_agg_start(il, vif, sta, tid, ssn);
                break;
-       case IEEE80211_AMPDU_TX_STOP:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
                D_HT("stop Tx\n");
                ret = il4965_tx_agg_stop(il, vif, sta, tid);
                if (test_bit(S_EXIT_PENDING, &il->status))
@@ -6306,6 +6330,7 @@ const struct ieee80211_ops il4965_mac_ops = {
        .sta_remove = il_mac_sta_remove,
        .channel_switch = il4965_mac_channel_switch,
        .tx_last_beacon = il_mac_tx_last_beacon,
+       .flush = il_mac_flush,
 };
 
 static int
@@ -6553,6 +6578,7 @@ il4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        il4965_prepare_card_hw(il);
        if (!il->hw_ready) {
                IL_WARN("Failed, HW not ready\n");
+               err = -EIO;
                goto out_iounmap;
        }
 
@@ -6569,9 +6595,6 @@ il4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (err)
                goto out_free_eeprom;
 
-       if (err)
-               goto out_free_eeprom;
-
        /* extract MAC Address */
        il4965_eeprom_get_mac(il, il->addresses[0].addr);
        D_INFO("MAC address: %pM\n", il->addresses[0].addr);
index 5db11714e04705cd9f68c061c59dfdab6abecfc6..91eb2d07fdb82aff24b490dc106c10b60f6be6ee 100644 (file)
@@ -1748,7 +1748,6 @@ static void
 il4965_post_associate(struct il_priv *il)
 {
        struct ieee80211_vif *vif = il->vif;
-       struct ieee80211_conf *conf = NULL;
        int ret = 0;
 
        if (!vif || !il->is_open)
@@ -1759,8 +1758,6 @@ il4965_post_associate(struct il_priv *il)
 
        il_scan_cancel_timeout(il, 200);
 
-       conf = &il->hw->conf;
-
        il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
        il_commit_rxon(il);
 
index 25dd7d28d022c3e5e3ad74759e841c2b9de87f7b..3b6c994008920e76ee455ef3512a8e380d20b4b6 100644 (file)
@@ -1134,8 +1134,9 @@ struct il_wep_cmd {
 #define RX_RES_PHY_FLAGS_MOD_CCK_MSK           cpu_to_le16(1 << 1)
 #define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK    cpu_to_le16(1 << 2)
 #define RX_RES_PHY_FLAGS_NARROW_BAND_MSK       cpu_to_le16(1 << 3)
-#define RX_RES_PHY_FLAGS_ANTENNA_MSK           0xf0
+#define RX_RES_PHY_FLAGS_ANTENNA_MSK           0x70
 #define RX_RES_PHY_FLAGS_ANTENNA_POS           4
+#define RX_RES_PHY_FLAGS_AGG_MSK       cpu_to_le16(1 << 7)
 
 #define RX_RES_STATUS_SEC_TYPE_MSK     (0x7 << 8)
 #define RX_RES_STATUS_SEC_TYPE_NONE    (0x0 << 8)
index 90b8970eadf0fa7c296be194c2d18ef9266b1043..1f598604a79c1215da9bd612fbe231f3cd4cfb05 100644 (file)
@@ -4700,6 +4700,42 @@ out:
 }
 EXPORT_SYMBOL(il_mac_change_interface);
 
+void
+il_mac_flush(struct ieee80211_hw *hw, bool drop)
+{
+       struct il_priv *il = hw->priv;
+       unsigned long timeout = jiffies + msecs_to_jiffies(500);
+       int i;
+
+       mutex_lock(&il->mutex);
+       D_MAC80211("enter\n");
+
+       if (il->txq == NULL)
+               goto out;
+
+       for (i = 0; i < il->hw_params.max_txq_num; i++) {
+               struct il_queue *q;
+
+               if (i == il->cmd_queue)
+                       continue;
+
+               q = &il->txq[i].q;
+               if (q->read_ptr == q->write_ptr)
+                       continue;
+
+               if (time_after(jiffies, timeout)) {
+                       IL_ERR("Failed to flush queue %d\n", q->id);
+                       break;
+               }
+
+               msleep(20);
+       }
+out:
+       D_MAC80211("leave\n");
+       mutex_unlock(&il->mutex);
+}
+EXPORT_SYMBOL(il_mac_flush);
+
 /*
  * On every watchdog tick we check (latest) time stamp. If it does not
  * change during timeout period and queue is not empty we reset firmware.
index a9a569f432fb3517d5421ea1c4d43cdd96a4d816..96f2025d936e340dcbc0cb3693f5d38bb8817c0c 100644 (file)
@@ -1356,6 +1356,7 @@ struct il_priv {
                struct {
                        struct il_rx_phy_res last_phy_res;
                        bool last_phy_res_valid;
+                       u32 ampdu_ref;
 
                        struct completion firmware_loading_complete;
 
@@ -1723,6 +1724,7 @@ void il_mac_remove_interface(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif);
 int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                            enum nl80211_iftype newtype, bool newp2p);
+void il_mac_flush(struct ieee80211_hw *hw, bool drop);
 int il_alloc_txq_mem(struct il_priv *il);
 void il_free_txq_mem(struct il_priv *il);
 
index 5cf43236421ee57a4b3145ded322e49fe05ca6f3..ba319cba3f1eed94c56c2e671f21828f26b147c4 100644 (file)
@@ -43,8 +43,20 @@ config IWLWIFI
          module will be called iwlwifi.
 
 config IWLDVM
-       tristate "Intel Wireless WiFi"
+       tristate "Intel Wireless WiFi DVM Firmware support"
        depends on IWLWIFI
+       help
+         This is the driver supporting the DVM firmware which is
+         currently the only firmware available for existing devices.
+
+config IWLMVM
+       tristate "Intel Wireless WiFi MVM Firmware support"
+       depends on IWLWIFI
+       help
+         This is the driver supporting the MVM firmware which is
+         currently only available for 7000 series devices.
+
+         Say yes if you have such a device.
 
 menu "Debugging Options"
        depends on IWLWIFI
index 170ec330d2a9928532376fc1a1767f9d91c0bf1a..6c7800044a04d0287e3e896792f9660d0ee83f3b 100644 (file)
@@ -5,8 +5,10 @@ iwlwifi-objs           += iwl-drv.o
 iwlwifi-objs           += iwl-debug.o
 iwlwifi-objs           += iwl-notif-wait.o
 iwlwifi-objs           += iwl-eeprom-read.o iwl-eeprom-parse.o
+iwlwifi-objs           += iwl-phy-db.o iwl-nvm-parse.o
 iwlwifi-objs           += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
 iwlwifi-objs           += pcie/1000.o pcie/2000.o pcie/5000.o pcie/6000.o
+iwlwifi-objs           += pcie/7000.o
 
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o
@@ -15,5 +17,6 @@ ccflags-y += -D__CHECK_ENDIAN__ -I$(src)
 
 
 obj-$(CONFIG_IWLDVM)   += dvm/
+obj-$(CONFIG_IWLMVM)   += mvm/
 
 CFLAGS_iwl-devtrace.o := -I$(src)
index 33b3ad2e546bc9c4fbad24516c2ab1d279888041..f41ae79e6bc0a940af62b3d8625b5a2f57ac2875 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index de54713b680c386cb3287354bf0801de57e47562..6468de8634b01be6fb85d4daabbd58a40d1159f6 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 2349f393cc42b966cb6c0fc6529445100740b2a8..65e920cab2b78780c7813d030950001403c40821 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 71ab76b2b39d18da16518e99b5d36ab90ac87042..8bce4b0148e088b14293ae021f0411e03b1f3bc2 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -3695,7 +3695,7 @@ struct iwl_bt_uart_msg {
        u8 frame5;
        u8 frame6;
        u8 frame7;
-} __attribute__((packed));
+} __packed;
 
 struct iwl_bt_coex_profile_notif {
        struct iwl_bt_uart_msg last_bt_uart_msg;
@@ -3703,7 +3703,7 @@ struct iwl_bt_coex_profile_notif {
        u8 bt_traffic_load; /* 0 .. 3? */
        u8 bt_ci_compliance; /* 0 - not complied, 1 - complied */
        u8 reserved;
-} __attribute__((packed));
+} __packed;
 
 #define IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS        0
 #define IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_MSK        0x1
@@ -3752,7 +3752,7 @@ enum bt_coex_prio_table_priorities {
 
 struct iwl_bt_coex_prio_table_cmd {
        u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
-} __attribute__((packed));
+} __packed;
 
 #define IWL_BT_COEX_ENV_CLOSE  0
 #define IWL_BT_COEX_ENV_OPEN   1
@@ -3764,7 +3764,7 @@ struct iwl_bt_coex_prot_env_cmd {
        u8 action; /* 0 = closed, 1 = open */
        u8 type; /* 0 .. 15 */
        u8 reserved[2];
-} __attribute__((packed));
+} __packed;
 
 /*
  * REPLY_D3_CONFIG
index 5b9533eef54dd7bb173c250ac0eb42666ff06e87..20806cae11b72a50eee5525e3384613ad3c3181f 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -157,7 +157,7 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
        sram = priv->dbgfs_sram_offset & ~0x3;
 
        /* read the first u32 from sram */
-       val = iwl_read_targ_mem(priv->trans, sram);
+       val = iwl_trans_read_mem32(priv->trans, sram);
 
        for (; len; len--) {
                /* put the address at the start of every line */
@@ -176,7 +176,7 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
                if (++offset == 4) {
                        sram += 4;
                        offset = 0;
-                       val = iwl_read_targ_mem(priv->trans, sram);
+                       val = iwl_trans_read_mem32(priv->trans, sram);
                }
 
                /* put in extra spaces and split lines for human readability */
index 2653a891cc7e713ebd7f5708b252f977de4e09ed..71ea77576d222cc0154d062d42d53191a8c4fc67 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 8c72be3f37c16ad9a8d77c0ef04d8f0face8a8b3..15cca2ef9294f6c87f3249df485a90ad97f16aa7 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index bf479f709091d6954e779c079c379e48a578c2f5..33c7e15d24f5c64584e1f3c870a6d3a851258751 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -69,7 +69,7 @@ static const struct ieee80211_tpt_blink iwl_blink[] = {
 /* Set led register off */
 void iwlagn_led_enable(struct iwl_priv *priv)
 {
-       iwl_write32(priv->trans, CSR_LED_REG, CSR_LED_REG_TRUN_ON);
+       iwl_write32(priv->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON);
 }
 
 /*
index b02a853103d380397b940afdfb1068c0ef37632a..8749dcfe695fd22407fbeb653a5fa249c9832e36 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 6ff46605ad4fc1c60d34ffedf28dc706f93abd07..86ea5f4c39398077efa5e13c4a16a7dcf769bf05 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
index 3163e0f38c25109a3c5f5a2e7c71f39f37b40eda..c2f03ecd4bf8549a45a4f8711d371000c4d1b81a 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -206,7 +206,8 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
 
 #ifdef CONFIG_PM_SLEEP
        if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
-           priv->trans->ops->wowlan_suspend &&
+           priv->trans->ops->d3_suspend &&
+           priv->trans->ops->d3_resume &&
            device_can_wakeup(priv->trans->dev)) {
                hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
                                          WIPHY_WOWLAN_DISCONNECT |
@@ -426,7 +427,7 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
        if (ret)
                goto error;
 
-       iwl_trans_wowlan_suspend(priv->trans);
+       iwl_trans_d3_suspend(priv->trans);
 
        goto out;
 
@@ -458,14 +459,12 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
 
        base = priv->device_pointers.error_event_table;
        if (iwlagn_hw_valid_rtc_data_addr(base)) {
-               spin_lock_irqsave(&priv->trans->reg_lock, flags);
-               ret = iwl_grab_nic_access_silent(priv->trans);
-               if (likely(ret == 0)) {
+               if (iwl_trans_grab_nic_access(priv->trans, true, &flags)) {
                        iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, base);
                        status = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
-                       iwl_release_nic_access(priv->trans);
+                       iwl_trans_release_nic_access(priv->trans, &flags);
+                       ret = 0;
                }
-               spin_unlock_irqrestore(&priv->trans->reg_lock, flags);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
                if (ret == 0) {
@@ -479,7 +478,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
                        }
 
                        if (priv->wowlan_sram)
-                               _iwl_read_targ_mem_dwords(
+                               iwl_trans_read_mem(
                                      priv->trans, 0x800000,
                                      priv->wowlan_sram,
                                      img->sec[IWL_UCODE_SECTION_DATA].len / 4);
@@ -520,9 +519,6 @@ static void iwlagn_mac_tx(struct ieee80211_hw *hw,
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
 
-       IWL_DEBUG_TX(priv, "dev->xmit(%d bytes) at rate 0x%02x\n", skb->len,
-                    ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate);
-
        if (iwlagn_tx_skb(priv, control->sta, skb))
                ieee80211_free_txskb(hw, skb);
 }
@@ -679,7 +675,9 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
                IWL_DEBUG_HT(priv, "start Tx\n");
                ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn);
                break;
-       case IEEE80211_AMPDU_TX_STOP:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
                IWL_DEBUG_HT(priv, "stop Tx\n");
                ret = iwlagn_tx_agg_stop(priv, vif, sta, tid);
                if ((ret == 0) && (priv->agg_tids_count > 0)) {
@@ -1154,6 +1152,7 @@ static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
 }
 
 static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
                                     enum ieee80211_rssi_event rssi_event)
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
index faa05932efae92e80c322b4a28d606da7cb9808b..b9e3517652d649f4514ea9b3608f479cc5c81991 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -353,11 +353,8 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
                ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32));
 
        /* Make sure device is powered up for SRAM reads */
-       spin_lock_irqsave(&priv->trans->reg_lock, reg_flags);
-       if (unlikely(!iwl_grab_nic_access(priv->trans))) {
-               spin_unlock_irqrestore(&priv->trans->reg_lock, reg_flags);
+       if (!iwl_trans_grab_nic_access(priv->trans, false, &reg_flags))
                return;
-       }
 
        /* Set starting address; reads will auto-increment */
        iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, ptr);
@@ -388,8 +385,7 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
                }
        }
        /* Allow device to power down */
-       iwl_release_nic_access(priv->trans);
-       spin_unlock_irqrestore(&priv->trans->reg_lock, reg_flags);
+       iwl_trans_release_nic_access(priv->trans, &reg_flags);
 }
 
 static void iwl_continuous_event_trace(struct iwl_priv *priv)
@@ -408,7 +404,8 @@ static void iwl_continuous_event_trace(struct iwl_priv *priv)
 
        base = priv->device_pointers.log_event_table;
        if (iwlagn_hw_valid_rtc_data_addr(base)) {
-               iwl_read_targ_mem_bytes(priv->trans, base, &read, sizeof(read));
+               iwl_trans_read_mem_bytes(priv->trans, base,
+                                        &read, sizeof(read));
                capacity = read.capacity;
                mode = read.mode;
                num_wraps = read.wrap_counter;
@@ -1627,7 +1624,7 @@ static void iwl_dump_nic_error_log(struct iwl_priv *priv)
        }
 
        /*TODO: Update dbgfs with ISR error stats obtained below */
-       iwl_read_targ_mem_bytes(trans, base, &table, sizeof(table));
+       iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
 
        if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
                IWL_ERR(trans, "Start IWL Error Log Dump:\n");
@@ -1716,9 +1713,8 @@ static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
        ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
 
        /* Make sure device is powered up for SRAM reads */
-       spin_lock_irqsave(&trans->reg_lock, reg_flags);
-       if (unlikely(!iwl_grab_nic_access(trans)))
-               goto out_unlock;
+       if (!iwl_trans_grab_nic_access(trans, false, &reg_flags))
+               return pos;
 
        /* Set starting address; reads will auto-increment */
        iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr);
@@ -1756,9 +1752,7 @@ static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
        }
 
        /* Allow device to power down */
-       iwl_release_nic_access(trans);
-out_unlock:
-       spin_unlock_irqrestore(&trans->reg_lock, reg_flags);
+       iwl_trans_release_nic_access(trans, &reg_flags);
        return pos;
 }
 
@@ -1835,10 +1829,10 @@ int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
        }
 
        /* event log header */
-       capacity = iwl_read_targ_mem(trans, base);
-       mode = iwl_read_targ_mem(trans, base + (1 * sizeof(u32)));
-       num_wraps = iwl_read_targ_mem(trans, base + (2 * sizeof(u32)));
-       next_entry = iwl_read_targ_mem(trans, base + (3 * sizeof(u32)));
+       capacity = iwl_trans_read_mem32(trans, base);
+       mode = iwl_trans_read_mem32(trans, base + (1 * sizeof(u32)));
+       num_wraps = iwl_trans_read_mem32(trans, base + (2 * sizeof(u32)));
+       next_entry = iwl_trans_read_mem32(trans, base + (3 * sizeof(u32)));
 
        if (capacity > logsize) {
                IWL_ERR(priv, "Log capacity %d is bogus, limit to %d "
@@ -1990,13 +1984,13 @@ static void iwl_nic_config(struct iwl_op_mode *op_mode)
        struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
 
        /* SKU Control */
-       iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
-                         CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
-                         CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP,
-                         (CSR_HW_REV_STEP(priv->trans->hw_rev) <<
-                               CSR_HW_IF_CONFIG_REG_POS_MAC_STEP) |
-                         (CSR_HW_REV_DASH(priv->trans->hw_rev) <<
-                               CSR_HW_IF_CONFIG_REG_POS_MAC_DASH));
+       iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
+                               CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
+                               CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP,
+                               (CSR_HW_REV_STEP(priv->trans->hw_rev) <<
+                                       CSR_HW_IF_CONFIG_REG_POS_MAC_STEP) |
+                               (CSR_HW_REV_DASH(priv->trans->hw_rev) <<
+                                       CSR_HW_IF_CONFIG_REG_POS_MAC_DASH));
 
        /* write radio config values to register */
        if (priv->nvm_data->radio_cfg_type <= EEPROM_RF_CONFIG_TYPE_MAX) {
@@ -2008,10 +2002,11 @@ static void iwl_nic_config(struct iwl_op_mode *op_mode)
                        priv->nvm_data->radio_cfg_dash <<
                                CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
 
-               iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
-                                 CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
-                                 CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
-                                 CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH, reg_val);
+               iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
+                                       CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
+                                       CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
+                                       CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH,
+                                       reg_val);
 
                IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n",
                         priv->nvm_data->radio_cfg_type,
index 518cf37158090805cb36510f09f2a9def79d7aea..bd69018d07a95f03b0da607548b2d0b97f677e3e 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index a2cee7f04848be6e58e8a793154d32f21082069f..7b03e1342d47076aa0ad8cf662e543c45b1e8be1 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index f3dd0da60d8a43247084cd0f798b132e4b28ba7a..a131227c49e970bfcbbd18b5a7f05f6af21b81f3 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -411,8 +411,9 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv,
         * BT traffic, as they would just be disrupted by BT.
         */
        if (priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) {
-               IWL_ERR(priv, "BT traffic (%d), no aggregation allowed\n",
-                       priv->bt_traffic_load);
+               IWL_DEBUG_COEX(priv,
+                              "BT traffic (%d), no aggregation allowed\n",
+                              priv->bt_traffic_load);
                return ret;
        }
 
index ad3aea8f626aaa5d8f20288d64cf59a1290ee662..5d83cab22d625084389b8880b46a4abd21fb9f44 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index cac4f37cc427531871049d775a6fba7f315638a8..e8d5b90abf5ccfb56c4c59fff0324e3a456b5cf7 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portionhelp of the ieee80211 subsystem header files.
index 9a891e6e60e888bc3a49453246a3d064ec4f9835..9fabd26997ca937a200047f84311718c41dc7be4 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 610ed2204e1f197d7bbcb22e76de3ec0776fa05a..3a4aa5239c4562c0c10a3b98004b6b11f907486c 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
index bdba9543c3516095d82cfc19c9f9123c6debb178..ab768045696b48598ba91f889a22c87403f73c21 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index 57b918ce3b5fcc462c6bd554893067c10d765838..dc6f965a123a872f6ebeca8dbafe8635f244ccec 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index eb864433e59de0ec749a3693d950c4065f2d6b32..67e2e1321b407cb2a1afd75ddd4ae2dac11123cc 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -185,10 +185,8 @@ static void iwl_tt_check_exit_ct_kill(unsigned long data)
                        priv->thermal_throttle.ct_kill_toggle = true;
                }
                iwl_read32(priv->trans, CSR_UCODE_DRV_GP1);
-               spin_lock_irqsave(&priv->trans->reg_lock, flags);
-               if (likely(iwl_grab_nic_access(priv->trans)))
-                       iwl_release_nic_access(priv->trans);
-               spin_unlock_irqrestore(&priv->trans->reg_lock, flags);
+               if (iwl_trans_grab_nic_access(priv->trans, false, &flags))
+                       iwl_trans_release_nic_access(priv->trans, &flags);
 
                /* Reschedule the ct_kill timer to occur in
                 * CT_KILL_EXIT_DURATION seconds to ensure we get a
index 44c7c8f30a2da2a768d7fe18c0fe89dbafe71189..9356c4b908ca58b55652843e6b7c7e5a43895f9e 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index 279796419ea0ca036800483aad59321e881efbe5..492722c0e25c9300b394d33665ffd2099b2b12a4 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -231,13 +231,11 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
                memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
                if (info->flags & IEEE80211_TX_CTL_AMPDU)
                        tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK;
-               IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n");
                break;
 
        case WLAN_CIPHER_SUITE_TKIP:
                tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
                ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
-               IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n");
                break;
 
        case WLAN_CIPHER_SUITE_WEP104:
@@ -355,8 +353,6 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
                }
        }
 
-       IWL_DEBUG_TX(priv, "station Id %d\n", sta_id);
-
        if (sta)
                sta_priv = (void *)sta->drv_priv;
 
@@ -472,6 +468,9 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
        WARN_ON_ONCE(is_agg &&
                     priv->queue_to_mac80211[txq_id] != info->hw_queue);
 
+       IWL_DEBUG_TX(priv, "TX to [%d|%d] Q:%d - seq: 0x%x\n", sta_id, tid,
+                    txq_id, seq_number);
+
        if (iwl_trans_tx(priv->trans, skb, dev_cmd, txq_id))
                goto drop_unlock_sta;
 
@@ -541,9 +540,9 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
        spin_lock_bh(&priv->sta_lock);
 
        tid_data = &priv->tid_data[sta_id][tid];
-       txq_id = priv->tid_data[sta_id][tid].agg.txq_id;
+       txq_id = tid_data->agg.txq_id;
 
-       switch (priv->tid_data[sta_id][tid].agg.state) {
+       switch (tid_data->agg.state) {
        case IWL_EMPTYING_HW_QUEUE_ADDBA:
                /*
                * This can happen if the peer stops aggregation
@@ -563,9 +562,9 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
        case IWL_AGG_ON:
                break;
        default:
-               IWL_WARN(priv, "Stopping AGG while state not ON "
-                        "or starting for %d on %d (%d)\n", sta_id, tid,
-                        priv->tid_data[sta_id][tid].agg.state);
+               IWL_WARN(priv,
+                        "Stopping AGG while state not ON or starting for %d on %d (%d)\n",
+                        sta_id, tid, tid_data->agg.state);
                spin_unlock_bh(&priv->sta_lock);
                return 0;
        }
@@ -578,12 +577,11 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
                        "stopping AGG on STA/TID %d/%d but hwq %d not used\n",
                        sta_id, tid, txq_id);
        } else if (tid_data->agg.ssn != tid_data->next_reclaimed) {
-               IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, "
-                                   "next_recl = %d\n",
+               IWL_DEBUG_TX_QUEUES(priv,
+                                   "Can't proceed: ssn %d, next_recl = %d\n",
                                    tid_data->agg.ssn,
                                    tid_data->next_reclaimed);
-               priv->tid_data[sta_id][tid].agg.state =
-                       IWL_EMPTYING_HW_QUEUE_DELBA;
+               tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_DELBA;
                spin_unlock_bh(&priv->sta_lock);
                return 0;
        }
@@ -591,8 +589,8 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
        IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n",
                            tid_data->agg.ssn);
 turn_off:
-       agg_state = priv->tid_data[sta_id][tid].agg.state;
-       priv->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF;
+       agg_state = tid_data->agg.state;
+       tid_data->agg.state = IWL_AGG_OFF;
 
        spin_unlock_bh(&priv->sta_lock);
 
@@ -954,12 +952,6 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
                if (status & (AGG_TX_STATE_FEW_BYTES_MSK |
                              AGG_TX_STATE_ABORT_MSK))
                        continue;
-
-               IWL_DEBUG_TX_REPLY(priv, "status %s (0x%08x), "
-                                  "try-count (0x%08x)\n",
-                                  iwl_get_agg_tx_fail_reason(fstatus),
-                                  fstatus & AGG_TX_STATUS_MSK,
-                                  fstatus & AGG_TX_TRY_MSK);
        }
 }
 
@@ -1215,16 +1207,27 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                        freed++;
                }
 
-               WARN_ON(!is_agg && freed != 1);
+               if (!is_agg && freed != 1)
+                       IWL_ERR(priv, "Q: %d, freed %d\n", txq_id, freed);
 
                /*
                 * An offchannel frame can be send only on the AUX queue, where
                 * there is no aggregation (and reordering) so it only is single
                 * skb is expected to be processed.
                 */
-               WARN_ON(is_offchannel_skb && freed != 1);
+               if (is_offchannel_skb && freed != 1)
+                       IWL_ERR(priv, "OFFCHANNEL SKB freed %d\n", freed);
        }
 
+       IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id,
+                          iwl_get_tx_fail_reason(status), status);
+
+       IWL_DEBUG_TX_REPLY(priv,
+                          "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d seq_ctl=0x%x\n",
+                          le32_to_cpu(tx_resp->rate_n_flags),
+                          tx_resp->failure_frame, SEQ_TO_INDEX(sequence), ssn,
+                          le16_to_cpu(tx_resp->seq_ctl));
+
        iwl_check_abort_status(priv, tx_resp->frame_count, status);
        spin_unlock(&priv->sta_lock);
 
index c6467e5554f5c2992c27a0871b418680225dfdc6..736fe9bb140ebab643e065742469ef0a9f667e31 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -286,89 +286,6 @@ static int iwl_alive_notify(struct iwl_priv *priv)
        return iwl_send_calib_results(priv);
 }
 
-
-/**
- * iwl_verify_inst_sparse - verify runtime uCode image in card vs. host,
- *   using sample data 100 bytes apart.  If these sample points are good,
- *   it's a pretty good bet that everything between them is good, too.
- */
-static int iwl_verify_sec_sparse(struct iwl_priv *priv,
-                                 const struct fw_desc *fw_desc)
-{
-       __le32 *image = (__le32 *)fw_desc->data;
-       u32 len = fw_desc->len;
-       u32 val;
-       u32 i;
-
-       IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len);
-
-       for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) {
-               /* read data comes through single port, auto-incr addr */
-               /* NOTE: Use the debugless read so we don't flood kernel log
-                * if IWL_DL_IO is set */
-               iwl_write_direct32(priv->trans, HBUS_TARG_MEM_RADDR,
-                       i + fw_desc->offset);
-               val = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
-               if (val != le32_to_cpu(*image))
-                       return -EIO;
-       }
-
-       return 0;
-}
-
-static void iwl_print_mismatch_sec(struct iwl_priv *priv,
-                                   const struct fw_desc *fw_desc)
-{
-       __le32 *image = (__le32 *)fw_desc->data;
-       u32 len = fw_desc->len;
-       u32 val;
-       u32 offs;
-       int errors = 0;
-
-       IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len);
-
-       iwl_write_direct32(priv->trans, HBUS_TARG_MEM_RADDR,
-                               fw_desc->offset);
-
-       for (offs = 0;
-            offs < len && errors < 20;
-            offs += sizeof(u32), image++) {
-               /* read data comes through single port, auto-incr addr */
-               val = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
-               if (val != le32_to_cpu(*image)) {
-                       IWL_ERR(priv, "uCode INST section at "
-                               "offset 0x%x, is 0x%x, s/b 0x%x\n",
-                               offs, val, le32_to_cpu(*image));
-                       errors++;
-               }
-       }
-}
-
-/**
- * iwl_verify_ucode - determine which instruction image is in SRAM,
- *    and verify its contents
- */
-static int iwl_verify_ucode(struct iwl_priv *priv,
-                           enum iwl_ucode_type ucode_type)
-{
-       const struct fw_img *img = iwl_get_ucode_image(priv, ucode_type);
-
-       if (!img) {
-               IWL_ERR(priv, "Invalid ucode requested (%d)\n", ucode_type);
-               return -EINVAL;
-       }
-
-       if (!iwl_verify_sec_sparse(priv, &img->sec[IWL_UCODE_SECTION_INST])) {
-               IWL_DEBUG_FW(priv, "uCode is good in inst SRAM\n");
-               return 0;
-       }
-
-       IWL_ERR(priv, "UCODE IMAGE IN INSTRUCTION SRAM NOT VALID!!\n");
-
-       iwl_print_mismatch_sec(priv, &img->sec[IWL_UCODE_SECTION_INST]);
-       return -EIO;
-}
-
 struct iwl_alive_data {
        bool valid;
        u8 subtype;
@@ -426,7 +343,7 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
                                   alive_cmd, ARRAY_SIZE(alive_cmd),
                                   iwl_alive_fn, &alive_data);
 
-       ret = iwl_trans_start_fw(priv->trans, fw);
+       ret = iwl_trans_start_fw(priv->trans, fw, false);
        if (ret) {
                priv->cur_ucode = old_type;
                iwl_remove_notification(&priv->notif_wait, &alive_wait);
@@ -450,18 +367,7 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
                return -EIO;
        }
 
-       /*
-        * This step takes a long time (60-80ms!!) and
-        * WoWLAN image should be loaded quickly, so
-        * skip it for WoWLAN.
-        */
        if (ucode_type != IWL_UCODE_WOWLAN) {
-               ret = iwl_verify_ucode(priv, ucode_type);
-               if (ret) {
-                       priv->cur_ucode = old_type;
-                       return ret;
-               }
-
                /* delay a bit to give rfkill time to run */
                msleep(5);
        }
index 7960a52f6ad46c63ef79f7efe4f12d35d2e15b8d..e9975c54c276755e5edbaa4102ebdc28b8edfdb9 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 864219d2136aab118c08d95a23472440d94a4b40..743b483433588a6818aa05a2c285f4c40e8e6666 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -83,6 +83,7 @@ enum iwl_device_family {
        IWL_DEVICE_FAMILY_6030,
        IWL_DEVICE_FAMILY_6050,
        IWL_DEVICE_FAMILY_6150,
+       IWL_DEVICE_FAMILY_7000,
 };
 
 /*
index 34a5287dfc2f6d1c366e6555a14ee284dad8bd5e..df3463a38704d76c3a30662fa14eee996da01ebc 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 
 /* LED */
 #define CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF)
-#define CSR_LED_REG_TRUN_ON (0x78)
-#define CSR_LED_REG_TRUN_OFF (0x38)
+#define CSR_LED_REG_TURN_ON (0x60)
+#define CSR_LED_REG_TURN_OFF (0x20)
 
 /* ANA_PLL */
 #define CSR50_ANA_PLL_CFG_VAL        (0x00880300)
index 42b20b0e83bc379c346251baccbe7087d8377750..8cf5db7fb5c9ce309349add2dd2222df3c75ebe2 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project.
  *
@@ -116,6 +116,7 @@ do {                                                                \
 #define IWL_DL_HCMD            0x00000004
 #define IWL_DL_STATE           0x00000008
 /* 0x000000F0 - 0x00000010 */
+#define IWL_DL_TE              0x00000020
 #define IWL_DL_EEPROM          0x00000040
 #define IWL_DL_RADIO           0x00000080
 /* 0x00000F00 - 0x00000100 */
@@ -156,6 +157,7 @@ do {                                                                \
 #define IWL_DEBUG_LED(p, f, a...)      IWL_DEBUG(p, IWL_DL_LED, f, ## a)
 #define IWL_DEBUG_WEP(p, f, a...)      IWL_DEBUG(p, IWL_DL_WEP, f, ## a)
 #define IWL_DEBUG_HC(p, f, a...)       IWL_DEBUG(p, IWL_DL_HCMD, f, ## a)
+#define IWL_DEBUG_TE(p, f, a...)       IWL_DEBUG(p, IWL_DL_TE, f, ## a)
 #define IWL_DEBUG_EEPROM(d, f, a...)   IWL_DEBUG_DEV(d, IWL_DL_EEPROM, f, ## a)
 #define IWL_DEBUG_CALIB(p, f, a...)    IWL_DEBUG(p, IWL_DL_CALIB, f, ## a)
 #define IWL_DEBUG_FW(p, f, a...)       IWL_DEBUG(p, IWL_DL_FW, f, ## a)
index 70191ddbd8f6ac1cbcd787fee1a511a13f7f655f..8f61c717f619c4fc0e979c6c57c005529550529e 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2009 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2009 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index c85eb37243b92cbe55a9cdfc5812a0940cb8a3c9..10f01793d7a605cf4dc7e4d6312ac4af8f4caa64 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2009 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2009 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index d3549f493a17dc60ab49ea649fd06eeab03a8b48..6f228bb2b84431e61cd8292d1563f81faa91efc6 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -139,8 +139,10 @@ struct iwl_drv {
 #endif
 };
 
-#define DVM_OP_MODE    0
-#define MVM_OP_MODE    1
+enum {
+       DVM_OP_MODE =   0,
+       MVM_OP_MODE =   1,
+};
 
 /* Protects the table contents, i.e. the ops pointer & drv list */
 static struct mutex iwlwifi_opmode_table_mtx;
@@ -149,8 +151,8 @@ static struct iwlwifi_opmode_table {
        const struct iwl_op_mode_ops *ops;      /* pointer to op_mode ops */
        struct list_head drv;           /* list of devices using this op_mode */
 } iwlwifi_opmode_table[] = {           /* ops set when driver is initialized */
-       { .name = "iwldvm", .ops = NULL },
-       { .name = "iwlmvm", .ops = NULL },
+       [DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL },
+       [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL },
 };
 
 /*
@@ -268,7 +270,7 @@ struct fw_sec_parsing {
  */
 struct iwl_tlv_calib_data {
        __le32 ucode_type;
-       __le64 calib;
+       struct iwl_tlv_calib_ctrl calib;
 } __packed;
 
 struct iwl_firmware_pieces {
@@ -358,7 +360,11 @@ static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data)
                        ucode_type);
                return -EINVAL;
        }
-       drv->fw.default_calib[ucode_type] = le64_to_cpu(def_calib->calib);
+       drv->fw.default_calib[ucode_type].flow_trigger =
+               def_calib->calib.flow_trigger;
+       drv->fw.default_calib[ucode_type].event_trigger =
+               def_calib->calib.event_trigger;
+
        return 0;
 }
 
@@ -959,7 +965,10 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        release_firmware(ucode_raw);
 
        mutex_lock(&iwlwifi_opmode_table_mtx);
-       op = &iwlwifi_opmode_table[DVM_OP_MODE];
+       if (fw->mvm_fw)
+               op = &iwlwifi_opmode_table[MVM_OP_MODE];
+       else
+               op = &iwlwifi_opmode_table[DVM_OP_MODE];
 
        /* add this device to the list of devices using this op_mode */
        list_add_tail(&drv->list, &op->drv);
index 285de5f68c051e39c9244188f234639dd8115328..594a5c71b272ed3c703f522f283d43b5bbc7af53 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -66,7 +66,7 @@
 /* for all modules */
 #define DRV_NAME        "iwlwifi"
 #define IWLWIFI_VERSION "in-tree:"
-#define DRV_COPYRIGHT  "Copyright(c) 2003-2012 Intel Corporation"
+#define DRV_COPYRIGHT  "Copyright(c) 2003-2013 Intel Corporation"
 #define DRV_AUTHOR     "<ilw@linux.intel.com>"
 
 
index 471986690cf042eec370ba3e84a9e86730216402..034f2ff4f43d381d93f0a680c24f1edcf35805fc 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -703,9 +703,9 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
        return n_channels;
 }
 
-static int iwl_init_sband_channels(struct iwl_nvm_data *data,
-                                  struct ieee80211_supported_band *sband,
-                                  int n_channels, enum ieee80211_band band)
+int iwl_init_sband_channels(struct iwl_nvm_data *data,
+                           struct ieee80211_supported_band *sband,
+                           int n_channels, enum ieee80211_band band)
 {
        struct ieee80211_channel *chan = &data->channels[0];
        int n = 0, idx = 0;
@@ -728,10 +728,10 @@ static int iwl_init_sband_channels(struct iwl_nvm_data *data,
 #define MAX_BIT_RATE_40_MHZ    150 /* Mbps */
 #define MAX_BIT_RATE_20_MHZ    72 /* Mbps */
 
-static void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
-                                struct iwl_nvm_data *data,
-                                struct ieee80211_sta_ht_cap *ht_info,
-                                enum ieee80211_band band)
+void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
+                         struct iwl_nvm_data *data,
+                         struct ieee80211_sta_ht_cap *ht_info,
+                         enum ieee80211_band band)
 {
        int max_bit_rate = 0;
        u8 rx_chains;
index 555f0eb61d4886b9fc54a93f86ad64a9b2c787ed..683fe6a8c58fe5e173df2c78eee16dfc128220c1 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -126,4 +126,13 @@ static inline void iwl_free_nvm_data(struct iwl_nvm_data *data)
 int iwl_nvm_check_version(struct iwl_nvm_data *data,
                          struct iwl_trans *trans);
 
+int iwl_init_sband_channels(struct iwl_nvm_data *data,
+                           struct ieee80211_supported_band *sband,
+                           int n_channels, enum ieee80211_band band);
+
+void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
+                         struct iwl_nvm_data *data,
+                         struct ieee80211_sta_ht_cap *ht_info,
+                         enum ieee80211_band band);
+
 #endif /* __iwl_eeprom_parse_h__ */
index 27c7da3c6ed1566f49eb8058fbfa1d02f769ed03..ef4806f27cf86fe5a6a66b44ac6136739962c4da 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 1337c9d36fee4c64465141e3a27c5efd2595783f..b2588c5cbf931b044b145bf9d0584b0de01a1a72 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index ec48563d3c6ad3e05f652ce936ff5fabc8cb2b2b..f5592fb3b1ed42cb7c2e86935e61512914e373b1 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -225,6 +225,8 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl)
 #define FH_RSCSR_CHNL0_RBDCB_WPTR_REG  (FH_MEM_RSCSR_CHNL0 + 0x008)
 #define FH_RSCSR_CHNL0_WPTR        (FH_RSCSR_CHNL0_RBDCB_WPTR_REG)
 
+#define FW_RSCSR_CHNL0_RXDCB_RDPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x00c)
+#define FH_RSCSR_CHNL0_RDPTR           FW_RSCSR_CHNL0_RXDCB_RDPTR_REG
 
 /**
  * Rx Config/Status Registers (RCSR)
@@ -257,6 +259,8 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl)
 #define FH_MEM_RCSR_CHNL0            (FH_MEM_RCSR_LOWER_BOUND)
 
 #define FH_MEM_RCSR_CHNL0_CONFIG_REG   (FH_MEM_RCSR_CHNL0)
+#define FH_MEM_RCSR_CHNL0_RBDCB_WPTR   (FH_MEM_RCSR_CHNL0 + 0x8)
+#define FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ (FH_MEM_RCSR_CHNL0 + 0x10)
 
 #define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */
 #define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK   (0x00001000) /* bits 12 */
@@ -410,6 +414,7 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl)
  *     uCode/driver must write "1" in order to clear this flag
  */
 #define FH_TSSR_TX_ERROR_REG           (FH_TSSR_LOWER_BOUND + 0x018)
+#define FH_TSSR_TX_MSG_CONFIG_REG      (FH_TSSR_LOWER_BOUND + 0x008)
 
 #define FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16)
 
index e71564053e7f822ac3a1ea8b092512dbf91fe36f..90873eca35f77ddf029b530e7fe633acb318f11b 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index d1a86b66bc51ee6fab8e797e8aa458b9db3812ea..de3c24a5a62047c5449844c51a4c3c7d67e1ab1c 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -139,6 +139,19 @@ struct fw_img {
 #define IWL_UCODE_API(ver)     (((ver) & 0x0000FF00) >> 8)
 #define IWL_UCODE_SERIAL(ver)  ((ver) & 0x000000FF)
 
+/*
+ * Calibration control struct.
+ * Sent as part of the phy configuration command.
+ * @flow_trigger: bitmap for which calibrations to perform according to
+ *             flow triggers.
+ * @event_trigger: bitmap for which calibrations to perform according to
+ *             event triggers.
+ */
+struct iwl_tlv_calib_ctrl {
+       __le32 flow_trigger;
+       __le32 event_trigger;
+} __packed;
+
 /**
  * struct iwl_fw - variables associated with the firmware
  *
@@ -153,6 +166,7 @@ struct fw_img {
  * @inst_evtlog_ptr: event log offset for runtime ucode.
  * @inst_evtlog_size: event log size for runtime ucode.
  * @inst_errlog_ptr: error log offfset for runtime ucode.
+ * @mvm_fw: indicates this is MVM firmware
  */
 struct iwl_fw {
        u32 ucode_ver;
@@ -168,7 +182,7 @@ struct iwl_fw {
        u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
        u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
 
-       u64 default_calib[IWL_UCODE_TYPE_MAX];
+       struct iwl_tlv_calib_ctrl default_calib[IWL_UCODE_TYPE_MAX];
        u32 phy_config;
 
        bool mvm_fw;
index cdaff9572059bd162beb8e050a0215d3f632ebcd..276410d82de498186bb009788ddbb5ff5484c235 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project.
  *
 
 #define IWL_POLL_INTERVAL 10   /* microseconds */
 
-static inline void __iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask)
-{
-       iwl_write32(trans, reg, iwl_read32(trans, reg) | mask);
-}
-
-static inline void __iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask)
-{
-       iwl_write32(trans, reg, iwl_read32(trans, reg) & ~mask);
-}
-
-void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       __iwl_set_bit(trans, reg, mask);
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
-}
-EXPORT_SYMBOL_GPL(iwl_set_bit);
-
-void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       __iwl_clear_bit(trans, reg, mask);
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
-}
-EXPORT_SYMBOL_GPL(iwl_clear_bit);
-
-void iwl_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value)
-{
-       unsigned long flags;
-       u32 v;
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-       WARN_ON_ONCE(value & ~mask);
-#endif
-
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       v = iwl_read32(trans, reg);
-       v &= ~mask;
-       v |= value;
-       iwl_write32(trans, reg, v);
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
-}
-EXPORT_SYMBOL_GPL(iwl_set_bits_mask);
-
 int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
                 u32 bits, u32 mask, int timeout)
 {
@@ -99,87 +51,14 @@ int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
 }
 EXPORT_SYMBOL_GPL(iwl_poll_bit);
 
-int iwl_grab_nic_access_silent(struct iwl_trans *trans)
-{
-       int ret;
-
-       lockdep_assert_held(&trans->reg_lock);
-
-       /* this bit wakes up the NIC */
-       __iwl_set_bit(trans, CSR_GP_CNTRL,
-                     CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-
-       /*
-        * These bits say the device is running, and should keep running for
-        * at least a short while (at least as long as MAC_ACCESS_REQ stays 1),
-        * but they do not indicate that embedded SRAM is restored yet;
-        * 3945 and 4965 have volatile SRAM, and must save/restore contents
-        * to/from host DRAM when sleeping/waking for power-saving.
-        * Each direction takes approximately 1/4 millisecond; with this
-        * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a
-        * series of register accesses are expected (e.g. reading Event Log),
-        * to keep device from sleeping.
-        *
-        * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that
-        * SRAM is okay/restored.  We don't check that here because this call
-        * is just for hardware register access; but GP1 MAC_SLEEP check is a
-        * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log).
-        *
-        * 5000 series and later (including 1000 series) have non-volatile SRAM,
-        * and do not save/restore SRAM when power cycling.
-        */
-       ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
-                          CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
-                          (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
-                           CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
-       if (ret < 0) {
-               iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
-               return -EIO;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(iwl_grab_nic_access_silent);
-
-bool iwl_grab_nic_access(struct iwl_trans *trans)
-{
-       int ret = iwl_grab_nic_access_silent(trans);
-       if (unlikely(ret)) {
-               u32 val = iwl_read32(trans, CSR_GP_CNTRL);
-               WARN_ONCE(1, "Timeout waiting for hardware access "
-                            "(CSR_GP_CNTRL 0x%08x)\n", val);
-               return false;
-       }
-
-       return true;
-}
-EXPORT_SYMBOL_GPL(iwl_grab_nic_access);
-
-void iwl_release_nic_access(struct iwl_trans *trans)
-{
-       lockdep_assert_held(&trans->reg_lock);
-       __iwl_clear_bit(trans, CSR_GP_CNTRL,
-                       CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-       /*
-        * Above we read the CSR_GP_CNTRL register, which will flush
-        * any previous writes, but we need the write that clears the
-        * MAC_ACCESS_REQ bit to be performed before any other writes
-        * scheduled on different CPUs (after we drop reg_lock).
-        */
-       mmiowb();
-}
-EXPORT_SYMBOL_GPL(iwl_release_nic_access);
-
 u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg)
 {
-       u32 value;
+       u32 value = 0x5a5a5a5a;
        unsigned long flags;
-
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       iwl_grab_nic_access(trans);
-       value = iwl_read32(trans, reg);
-       iwl_release_nic_access(trans);
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
+               value = iwl_read32(trans, reg);
+               iwl_trans_release_nic_access(trans, &flags);
+       }
 
        return value;
 }
@@ -189,12 +68,10 @@ void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (likely(iwl_grab_nic_access(trans))) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                iwl_write32(trans, reg, value);
-               iwl_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(iwl_write_direct32);
 
@@ -230,13 +107,12 @@ static inline void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
 u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs)
 {
        unsigned long flags;
-       u32 val;
+       u32 val = 0x5a5a5a5a;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       iwl_grab_nic_access(trans);
-       val = __iwl_read_prph(trans, ofs);
-       iwl_release_nic_access(trans);
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
+               val = __iwl_read_prph(trans, ofs);
+               iwl_trans_release_nic_access(trans, &flags);
+       }
        return val;
 }
 EXPORT_SYMBOL_GPL(iwl_read_prph);
@@ -245,12 +121,10 @@ void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (likely(iwl_grab_nic_access(trans))) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                __iwl_write_prph(trans, ofs, val);
-               iwl_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(iwl_write_prph);
 
@@ -258,13 +132,11 @@ void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (likely(iwl_grab_nic_access(trans))) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                __iwl_write_prph(trans, ofs,
                                 __iwl_read_prph(trans, ofs) | mask);
-               iwl_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(iwl_set_bits_prph);
 
@@ -273,13 +145,11 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (likely(iwl_grab_nic_access(trans))) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                __iwl_write_prph(trans, ofs,
                                 (__iwl_read_prph(trans, ofs) & mask) | bits);
-               iwl_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(iwl_set_bits_mask_prph);
 
@@ -288,67 +158,10 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
        unsigned long flags;
        u32 val;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (likely(iwl_grab_nic_access(trans))) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                val = __iwl_read_prph(trans, ofs);
                __iwl_write_prph(trans, ofs, (val & ~mask));
-               iwl_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(iwl_clear_bits_prph);
-
-void _iwl_read_targ_mem_dwords(struct iwl_trans *trans, u32 addr,
-                              void *buf, int dwords)
-{
-       unsigned long flags;
-       int offs;
-       u32 *vals = buf;
-
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (likely(iwl_grab_nic_access(trans))) {
-               iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr);
-               for (offs = 0; offs < dwords; offs++)
-                       vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
-               iwl_release_nic_access(trans);
-       }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
-}
-EXPORT_SYMBOL_GPL(_iwl_read_targ_mem_dwords);
-
-u32 iwl_read_targ_mem(struct iwl_trans *trans, u32 addr)
-{
-       u32 value;
-
-       _iwl_read_targ_mem_dwords(trans, addr, &value, 1);
-
-       return value;
-}
-EXPORT_SYMBOL_GPL(iwl_read_targ_mem);
-
-int _iwl_write_targ_mem_dwords(struct iwl_trans *trans, u32 addr,
-                              const void *buf, int dwords)
-{
-       unsigned long flags;
-       int offs, result = 0;
-       const u32 *vals = buf;
-
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (likely(iwl_grab_nic_access(trans))) {
-               iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr);
-               for (offs = 0; offs < dwords; offs++)
-                       iwl_write32(trans, HBUS_TARG_MEM_WDAT, vals[offs]);
-               iwl_release_nic_access(trans);
-       } else
-               result = -EBUSY;
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
-
-       return result;
-}
-EXPORT_SYMBOL_GPL(_iwl_write_targ_mem_dwords);
-
-int iwl_write_targ_mem(struct iwl_trans *trans, u32 addr, u32 val)
-{
-       return _iwl_write_targ_mem_dwords(trans, addr, &val, 1);
-}
-EXPORT_SYMBOL_GPL(iwl_write_targ_mem);
index 48dc753e3742aab857af7f88617b81206dc163cc..fd9f5b97fff3c937f0d5c177737e043f6256cd51 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project.
  *
@@ -51,20 +51,21 @@ static inline u32 iwl_read32(struct iwl_trans *trans, u32 ofs)
        return val;
 }
 
-void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask);
-void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask);
+static inline void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask)
+{
+       iwl_trans_set_bits_mask(trans, reg, mask, mask);
+}
 
-void iwl_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value);
+static inline void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask)
+{
+       iwl_trans_set_bits_mask(trans, reg, mask, 0);
+}
 
 int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
                 u32 bits, u32 mask, int timeout);
 int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
                        int timeout);
 
-int iwl_grab_nic_access_silent(struct iwl_trans *trans);
-bool iwl_grab_nic_access(struct iwl_trans *trans);
-void iwl_release_nic_access(struct iwl_trans *trans);
-
 u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg);
 void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value);
 
@@ -76,19 +77,4 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
                            u32 bits, u32 mask);
 void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
 
-void _iwl_read_targ_mem_dwords(struct iwl_trans *trans, u32 addr,
-                              void *buf, int dwords);
-
-#define iwl_read_targ_mem_bytes(trans, addr, buf, bufsize)     \
-       do {                                                    \
-               BUILD_BUG_ON((bufsize) % sizeof(u32));          \
-               _iwl_read_targ_mem_dwords(trans, addr, buf,     \
-                                         (bufsize) / sizeof(u32));\
-       } while (0)
-
-int _iwl_write_targ_mem_dwords(struct iwl_trans *trans, u32 addr,
-                              const void *buf, int dwords);
-
-u32 iwl_read_targ_mem(struct iwl_trans *trans, u32 addr);
-int iwl_write_targ_mem(struct iwl_trans *trans, u32 addr, u32 val);
 #endif
index d9a86d6b2bd700b9430394c9573203043629d4fc..e5e3a79eae2fbd5e8f544db2cc9605522770dddb 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index c61f2070f15a0fab26d6f6caf20de846ca328010..c3affbc62cdf736c16018080005c4d9e08884d53 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 821523100cf1e615e227acfeeb37f22dd59a5fa2..c2ce764463a33bab64159fa7dbdc02e23f2b724a 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
new file mode 100644 (file)
index 0000000..a70213b
--- /dev/null
@@ -0,0 +1,346 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include "iwl-modparams.h"
+#include "iwl-nvm-parse.h"
+
+/* NVM offsets (in words) definitions */
+enum wkp_nvm_offsets {
+       /* NVM HW-Section offset (in words) definitions */
+       HW_ADDR = 0x15,
+
+/* NVM SW-Section offset (in words) definitions */
+       NVM_SW_SECTION = 0x1C0,
+       NVM_VERSION = 0,
+       RADIO_CFG = 1,
+       SKU = 2,
+       N_HW_ADDRS = 3,
+       NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION,
+
+/* NVM calibration section offset (in words) definitions */
+       NVM_CALIB_SECTION = 0x2B8,
+       XTAL_CALIB = 0x316 - NVM_CALIB_SECTION
+};
+
+/* SKU Capabilities (actual values from NVM definition) */
+enum nvm_sku_bits {
+       NVM_SKU_CAP_BAND_24GHZ  = BIT(0),
+       NVM_SKU_CAP_BAND_52GHZ  = BIT(1),
+       NVM_SKU_CAP_11N_ENABLE  = BIT(2),
+};
+
+/* radio config bits (actual values from NVM definition) */
+#define NVM_RF_CFG_DASH_MSK(x)   (x & 0x3)         /* bits 0-1   */
+#define NVM_RF_CFG_STEP_MSK(x)   ((x >> 2)  & 0x3) /* bits 2-3   */
+#define NVM_RF_CFG_TYPE_MSK(x)   ((x >> 4)  & 0x3) /* bits 4-5   */
+#define NVM_RF_CFG_PNUM_MSK(x)   ((x >> 6)  & 0x3) /* bits 6-7   */
+#define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8)  & 0xF) /* bits 8-11  */
+#define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
+
+/*
+ * These are the channel numbers in the order that they are stored in the NVM
+ */
+static const u8 iwl_nvm_channels[] = {
+       /* 2.4 GHz */
+       1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+       /* 5 GHz */
+       36, 40, 44 , 48, 52, 56, 60, 64,
+       100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
+       149, 153, 157, 161, 165
+};
+
+#define IWL_NUM_CHANNELS       ARRAY_SIZE(iwl_nvm_channels)
+#define NUM_2GHZ_CHANNELS      14
+#define FIRST_2GHZ_HT_MINUS    5
+#define LAST_2GHZ_HT_PLUS      9
+#define LAST_5GHZ_HT           161
+
+
+/* rate data (static) */
+static struct ieee80211_rate iwl_cfg80211_rates[] = {
+       { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, },
+       { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1,
+         .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+       { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2,
+         .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+       { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3,
+         .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+       { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, },
+       { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, },
+       { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, },
+       { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, },
+       { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, },
+       { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, },
+       { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, },
+       { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, },
+};
+#define RATES_24_OFFS  0
+#define N_RATES_24     ARRAY_SIZE(iwl_cfg80211_rates)
+#define RATES_52_OFFS  4
+#define N_RATES_52     (N_RATES_24 - RATES_52_OFFS)
+
+/**
+ * enum iwl_nvm_channel_flags - channel flags in NVM
+ * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo
+ * @NVM_CHANNEL_IBSS: usable as an IBSS channel
+ * @NVM_CHANNEL_ACTIVE: active scanning allowed
+ * @NVM_CHANNEL_RADAR: radar detection required
+ * @NVM_CHANNEL_DFS: dynamic freq selection candidate
+ * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
+ * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
+ */
+enum iwl_nvm_channel_flags {
+       NVM_CHANNEL_VALID = BIT(0),
+       NVM_CHANNEL_IBSS = BIT(1),
+       NVM_CHANNEL_ACTIVE = BIT(3),
+       NVM_CHANNEL_RADAR = BIT(4),
+       NVM_CHANNEL_DFS = BIT(7),
+       NVM_CHANNEL_WIDE = BIT(8),
+       NVM_CHANNEL_40MHZ = BIT(9),
+};
+
+#define CHECK_AND_PRINT_I(x)   \
+       ((ch_flags & NVM_CHANNEL_##x) ? # x " " : "")
+
+static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
+                               struct iwl_nvm_data *data,
+                               const __le16 * const nvm_ch_flags)
+{
+       int ch_idx;
+       int n_channels = 0;
+       struct ieee80211_channel *channel;
+       u16 ch_flags;
+       bool is_5ghz;
+
+       for (ch_idx = 0; ch_idx < IWL_NUM_CHANNELS; ch_idx++) {
+               ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
+               if (!(ch_flags & NVM_CHANNEL_VALID)) {
+                       IWL_DEBUG_EEPROM(dev,
+                                        "Ch. %d Flags %x [%sGHz] - No traffic\n",
+                                        iwl_nvm_channels[ch_idx],
+                                        ch_flags,
+                                        (ch_idx >= NUM_2GHZ_CHANNELS) ?
+                                        "5.2" : "2.4");
+                       continue;
+               }
+
+               channel = &data->channels[n_channels];
+               n_channels++;
+
+               channel->hw_value = iwl_nvm_channels[ch_idx];
+               channel->band = (ch_idx < NUM_2GHZ_CHANNELS) ?
+                               IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+               channel->center_freq =
+                       ieee80211_channel_to_frequency(
+                               channel->hw_value, channel->band);
+
+               /* TODO: Need to be dependent to the NVM */
+               channel->flags = IEEE80211_CHAN_NO_HT40;
+               if (ch_idx < NUM_2GHZ_CHANNELS &&
+                   (ch_flags & NVM_CHANNEL_40MHZ)) {
+                       if (iwl_nvm_channels[ch_idx] <= LAST_2GHZ_HT_PLUS)
+                               channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+                       if (iwl_nvm_channels[ch_idx] >= FIRST_2GHZ_HT_MINUS)
+                               channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+               } else if (iwl_nvm_channels[ch_idx] <= LAST_5GHZ_HT &&
+                          (ch_flags & NVM_CHANNEL_40MHZ)) {
+                       if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
+                               channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+                       else
+                               channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+               }
+
+               if (!(ch_flags & NVM_CHANNEL_IBSS))
+                       channel->flags |= IEEE80211_CHAN_NO_IBSS;
+
+               if (!(ch_flags & NVM_CHANNEL_ACTIVE))
+                       channel->flags |= IEEE80211_CHAN_PASSIVE_SCAN;
+
+               if (ch_flags & NVM_CHANNEL_RADAR)
+                       channel->flags |= IEEE80211_CHAN_RADAR;
+
+               /* Initialize regulatory-based run-time data */
+
+               /* TODO: read the real value from the NVM */
+               channel->max_power = 0;
+               is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
+               IWL_DEBUG_EEPROM(dev,
+                                "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
+                                channel->hw_value,
+                                is_5ghz ? "5.2" : "2.4",
+                                CHECK_AND_PRINT_I(VALID),
+                                CHECK_AND_PRINT_I(IBSS),
+                                CHECK_AND_PRINT_I(ACTIVE),
+                                CHECK_AND_PRINT_I(RADAR),
+                                CHECK_AND_PRINT_I(WIDE),
+                                CHECK_AND_PRINT_I(DFS),
+                                ch_flags,
+                                channel->max_power,
+                                ((ch_flags & NVM_CHANNEL_IBSS) &&
+                                 !(ch_flags & NVM_CHANNEL_RADAR))
+                                       ? "" : "not ");
+       }
+
+       return n_channels;
+}
+
+static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
+                           struct iwl_nvm_data *data, const __le16 *nvm_sw)
+{
+       int n_channels = iwl_init_channel_map(dev, cfg, data,
+                       &nvm_sw[NVM_CHANNELS]);
+       int n_used = 0;
+       struct ieee80211_supported_band *sband;
+
+       sband = &data->bands[IEEE80211_BAND_2GHZ];
+       sband->band = IEEE80211_BAND_2GHZ;
+       sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS];
+       sband->n_bitrates = N_RATES_24;
+       n_used += iwl_init_sband_channels(data, sband, n_channels,
+                                         IEEE80211_BAND_2GHZ);
+       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ);
+
+       sband = &data->bands[IEEE80211_BAND_5GHZ];
+       sband->band = IEEE80211_BAND_5GHZ;
+       sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS];
+       sband->n_bitrates = N_RATES_52;
+       n_used += iwl_init_sband_channels(data, sband, n_channels,
+                                         IEEE80211_BAND_5GHZ);
+       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ);
+
+       if (n_channels != n_used)
+               IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
+                           n_used, n_channels);
+}
+
+struct iwl_nvm_data *
+iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
+                  const __le16 *nvm_hw, const __le16 *nvm_sw,
+                  const __le16 *nvm_calib)
+{
+       struct iwl_nvm_data *data;
+       u8 hw_addr[ETH_ALEN];
+       u16 radio_cfg, sku;
+
+       data = kzalloc(sizeof(*data) +
+                      sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS,
+                      GFP_KERNEL);
+       if (!data)
+               return NULL;
+
+       data->nvm_version = le16_to_cpup(nvm_sw + NVM_VERSION);
+
+       radio_cfg = le16_to_cpup(nvm_sw + RADIO_CFG);
+       data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg);
+       data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg);
+       data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg);
+       data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg);
+       data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK(radio_cfg);
+       data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK(radio_cfg);
+
+       sku = le16_to_cpup(nvm_sw + SKU);
+       data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
+       data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
+       data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE;
+       if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
+               data->sku_cap_11n_enable = false;
+
+       /* check overrides (some devices have wrong NVM) */
+       if (cfg->valid_tx_ant)
+               data->valid_tx_ant = cfg->valid_tx_ant;
+       if (cfg->valid_rx_ant)
+               data->valid_rx_ant = cfg->valid_rx_ant;
+
+       if (!data->valid_tx_ant || !data->valid_rx_ant) {
+               IWL_ERR_DEV(dev, "invalid antennas (0x%x, 0x%x)\n",
+                           data->valid_tx_ant, data->valid_rx_ant);
+               kfree(data);
+               return NULL;
+       }
+
+       data->n_hw_addrs = le16_to_cpup(nvm_sw + N_HW_ADDRS);
+
+       data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB);
+       data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1);
+
+       /* The byte order is little endian 16 bit, meaning 214365 */
+       memcpy(hw_addr, nvm_hw + HW_ADDR, ETH_ALEN);
+       data->hw_addr[0] = hw_addr[1];
+       data->hw_addr[1] = hw_addr[0];
+       data->hw_addr[2] = hw_addr[3];
+       data->hw_addr[3] = hw_addr[2];
+       data->hw_addr[4] = hw_addr[5];
+       data->hw_addr[5] = hw_addr[4];
+
+       iwl_init_sbands(dev, cfg, data, nvm_sw);
+
+       data->calib_version = 255;   /* TODO:
+                                       this value will prevent some checks from
+                                       failing, we need to check if this
+                                       field is still needed, and if it does,
+                                       where is it in the NVM*/
+
+       return data;
+}
+EXPORT_SYMBOL_GPL(iwl_parse_nvm_data);
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
new file mode 100644 (file)
index 0000000..b2692bd
--- /dev/null
@@ -0,0 +1,80 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#ifndef __iwl_nvm_parse_h__
+#define __iwl_nvm_parse_h__
+
+#include "iwl-eeprom-parse.h"
+
+/**
+ * iwl_parse_nvm_data - parse NVM data and return values
+ *
+ * This function parses all NVM values we need and then
+ * returns a (newly allocated) struct containing all the
+ * relevant values for driver use. The struct must be freed
+ * later with iwl_free_nvm_data().
+ */
+struct iwl_nvm_data *
+iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
+                  const __le16 *nvm_hw, const __le16 *nvm_sw,
+                  const __le16 *nvm_calib);
+
+#endif /* __iwl_nvm_parse_h__ */
index c8d9b951746827b6ebd821eb0e72e2aef0c13a9c..dc792584f401f5429d27f4dcbc84048ebea15d1e 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -63,6 +63,8 @@
 #ifndef __iwl_op_mode_h__
 #define __iwl_op_mode_h__
 
+#include <linux/debugfs.h>
+
 struct iwl_op_mode;
 struct iwl_trans;
 struct sk_buff;
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
new file mode 100644 (file)
index 0000000..14fc8d3
--- /dev/null
@@ -0,0 +1,514 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/export.h>
+
+#include "iwl-phy-db.h"
+#include "iwl-debug.h"
+#include "iwl-op-mode.h"
+#include "iwl-trans.h"
+
+#define CHANNEL_NUM_SIZE       4       /* num of channels in calib_ch size */
+#define IWL_NUM_PAPD_CH_GROUPS 4
+#define IWL_NUM_TXP_CH_GROUPS  9
+
+struct iwl_phy_db_entry {
+       u16     size;
+       u8      *data;
+};
+
+/**
+ * struct iwl_phy_db - stores phy configuration and calibration data.
+ *
+ * @cfg: phy configuration.
+ * @calib_nch: non channel specific calibration data.
+ * @calib_ch: channel specific calibration data.
+ * @calib_ch_group_papd: calibration data related to papd channel group.
+ * @calib_ch_group_txp: calibration data related to tx power chanel group.
+ */
+struct iwl_phy_db {
+       struct iwl_phy_db_entry cfg;
+       struct iwl_phy_db_entry calib_nch;
+       struct iwl_phy_db_entry calib_ch;
+       struct iwl_phy_db_entry calib_ch_group_papd[IWL_NUM_PAPD_CH_GROUPS];
+       struct iwl_phy_db_entry calib_ch_group_txp[IWL_NUM_TXP_CH_GROUPS];
+
+       u32 channel_num;
+       u32 channel_size;
+
+       struct iwl_trans *trans;
+};
+
+enum iwl_phy_db_section_type {
+       IWL_PHY_DB_CFG = 1,
+       IWL_PHY_DB_CALIB_NCH,
+       IWL_PHY_DB_CALIB_CH,
+       IWL_PHY_DB_CALIB_CHG_PAPD,
+       IWL_PHY_DB_CALIB_CHG_TXP,
+       IWL_PHY_DB_MAX
+};
+
+#define PHY_DB_CMD 0x6c /* TEMP API - The actual is 0x8c */
+
+/*
+ * phy db - configure operational ucode
+ */
+struct iwl_phy_db_cmd {
+       __le16 type;
+       __le16 length;
+       u8 data[];
+} __packed;
+
+/* for parsing of tx power channel group data that comes from the firmware*/
+struct iwl_phy_db_chg_txp {
+       __le32 space;
+       __le16 max_channel_idx;
+} __packed;
+
+/*
+ * phy db - Receieve phy db chunk after calibrations
+ */
+struct iwl_calib_res_notif_phy_db {
+       __le16 type;
+       __le16 length;
+       u8 data[];
+} __packed;
+
+#define IWL_PHY_DB_STATIC_PIC cpu_to_le32(0x21436587)
+static inline void iwl_phy_db_test_pic(__le32 pic)
+{
+       WARN_ON(IWL_PHY_DB_STATIC_PIC != pic);
+}
+
+struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)
+{
+       struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
+                                           GFP_KERNEL);
+
+       if (!phy_db)
+               return phy_db;
+
+       phy_db->trans = trans;
+
+       /* TODO: add default values of the phy db. */
+       return phy_db;
+}
+EXPORT_SYMBOL(iwl_phy_db_init);
+
+/*
+ * get phy db section: returns a pointer to a phy db section specified by
+ * type and channel group id.
+ */
+static struct iwl_phy_db_entry *
+iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
+                      enum iwl_phy_db_section_type type,
+                      u16 chg_id)
+{
+       if (!phy_db || type >= IWL_PHY_DB_MAX)
+               return NULL;
+
+       switch (type) {
+       case IWL_PHY_DB_CFG:
+               return &phy_db->cfg;
+       case IWL_PHY_DB_CALIB_NCH:
+               return &phy_db->calib_nch;
+       case IWL_PHY_DB_CALIB_CH:
+               return &phy_db->calib_ch;
+       case IWL_PHY_DB_CALIB_CHG_PAPD:
+               if (chg_id >= IWL_NUM_PAPD_CH_GROUPS)
+                       return NULL;
+               return &phy_db->calib_ch_group_papd[chg_id];
+       case IWL_PHY_DB_CALIB_CHG_TXP:
+               if (chg_id >= IWL_NUM_TXP_CH_GROUPS)
+                       return NULL;
+               return &phy_db->calib_ch_group_txp[chg_id];
+       default:
+               return NULL;
+       }
+       return NULL;
+}
+
+static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
+                                   enum iwl_phy_db_section_type type,
+                                   u16 chg_id)
+{
+       struct iwl_phy_db_entry *entry =
+                               iwl_phy_db_get_section(phy_db, type, chg_id);
+       if (!entry)
+               return;
+
+       kfree(entry->data);
+       entry->data = NULL;
+       entry->size = 0;
+}
+
+void iwl_phy_db_free(struct iwl_phy_db *phy_db)
+{
+       int i;
+
+       if (!phy_db)
+               return;
+
+       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
+       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
+       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CH, 0);
+       for (i = 0; i < IWL_NUM_PAPD_CH_GROUPS; i++)
+               iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
+       for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++)
+               iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
+
+       kfree(phy_db);
+}
+EXPORT_SYMBOL(iwl_phy_db_free);
+
+int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
+                          gfp_t alloc_ctx)
+{
+       struct iwl_calib_res_notif_phy_db *phy_db_notif =
+                       (struct iwl_calib_res_notif_phy_db *)pkt->data;
+       enum iwl_phy_db_section_type type = le16_to_cpu(phy_db_notif->type);
+       u16 size  = le16_to_cpu(phy_db_notif->length);
+       struct iwl_phy_db_entry *entry;
+       u16 chg_id = 0;
+
+       if (!phy_db)
+               return -EINVAL;
+
+       if (type == IWL_PHY_DB_CALIB_CHG_PAPD ||
+           type == IWL_PHY_DB_CALIB_CHG_TXP)
+               chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
+
+       entry = iwl_phy_db_get_section(phy_db, type, chg_id);
+       if (!entry)
+               return -EINVAL;
+
+       kfree(entry->data);
+       entry->data = kmemdup(phy_db_notif->data, size, alloc_ctx);
+       if (!entry->data) {
+               entry->size = 0;
+               return -ENOMEM;
+       }
+
+       entry->size = size;
+
+       if (type == IWL_PHY_DB_CALIB_CH) {
+               phy_db->channel_num =
+                       le32_to_cpup((__le32 *)phy_db_notif->data);
+               phy_db->channel_size =
+                       (size - CHANNEL_NUM_SIZE) / phy_db->channel_num;
+       }
+
+       /* Test PIC */
+       if (type != IWL_PHY_DB_CFG)
+               iwl_phy_db_test_pic(*(((__le32 *)phy_db_notif->data) +
+                                     (size / sizeof(__le32)) - 1));
+
+       IWL_DEBUG_INFO(phy_db->trans,
+                      "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
+                      __func__, __LINE__, type, size);
+
+       return 0;
+}
+EXPORT_SYMBOL(iwl_phy_db_set_section);
+
+static int is_valid_channel(u16 ch_id)
+{
+       if (ch_id <= 14 ||
+           (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
+           (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
+           (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
+               return 1;
+       return 0;
+}
+
+static u8 ch_id_to_ch_index(u16 ch_id)
+{
+       if (WARN_ON(!is_valid_channel(ch_id)))
+               return 0xff;
+
+       if (ch_id <= 14)
+               return ch_id - 1;
+       if (ch_id <= 64)
+               return (ch_id + 20) / 4;
+       if (ch_id <= 140)
+               return (ch_id - 12) / 4;
+       return (ch_id - 13) / 4;
+}
+
+
+static u16 channel_id_to_papd(u16 ch_id)
+{
+       if (WARN_ON(!is_valid_channel(ch_id)))
+               return 0xff;
+
+       if (1 <= ch_id && ch_id <= 14)
+               return 0;
+       if (36 <= ch_id && ch_id <= 64)
+               return 1;
+       if (100 <= ch_id && ch_id <= 140)
+               return 2;
+       return 3;
+}
+
+static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
+{
+       struct iwl_phy_db_chg_txp *txp_chg;
+       int i;
+       u8 ch_index = ch_id_to_ch_index(ch_id);
+       if (ch_index == 0xff)
+               return 0xff;
+
+       for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++) {
+               txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
+               if (!txp_chg)
+                       return 0xff;
+               /*
+                * Looking for the first channel group that its max channel is
+                * higher then wanted channel.
+                */
+               if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
+                       return i;
+       }
+       return 0xff;
+}
+static
+int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
+                               u32 type, u8 **data, u16 *size, u16 ch_id)
+{
+       struct iwl_phy_db_entry *entry;
+       u32 channel_num;
+       u32 channel_size;
+       u16 ch_group_id = 0;
+       u16 index;
+
+       if (!phy_db)
+               return -EINVAL;
+
+       /* find wanted channel group */
+       if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
+               ch_group_id = channel_id_to_papd(ch_id);
+       else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
+               ch_group_id = channel_id_to_txp(phy_db, ch_id);
+
+       entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
+       if (!entry)
+               return -EINVAL;
+
+       if (type == IWL_PHY_DB_CALIB_CH) {
+               index = ch_id_to_ch_index(ch_id);
+               channel_num = phy_db->channel_num;
+               channel_size = phy_db->channel_size;
+               if (index >= channel_num) {
+                       IWL_ERR(phy_db->trans, "Wrong channel number %d\n",
+                               ch_id);
+                       return -EINVAL;
+               }
+               *data = entry->data + CHANNEL_NUM_SIZE + index * channel_size;
+               *size = channel_size;
+       } else {
+               *data = entry->data;
+               *size = entry->size;
+       }
+
+       /* Test PIC */
+       if (type != IWL_PHY_DB_CFG)
+               iwl_phy_db_test_pic(*(((__le32 *)*data) +
+                                     (*size / sizeof(__le32)) - 1));
+
+       IWL_DEBUG_INFO(phy_db->trans,
+                      "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
+                      __func__, __LINE__, type, *size);
+
+       return 0;
+}
+
+static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
+                              u16 length, void *data)
+{
+       struct iwl_phy_db_cmd phy_db_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = PHY_DB_CMD,
+               .flags = CMD_SYNC,
+       };
+
+       IWL_DEBUG_INFO(phy_db->trans,
+                      "Sending PHY-DB hcmd of type %d, of length %d\n",
+                      type, length);
+
+       /* Set phy db cmd variables */
+       phy_db_cmd.type = cpu_to_le16(type);
+       phy_db_cmd.length = cpu_to_le16(length);
+
+       /* Set hcmd variables */
+       cmd.data[0] = &phy_db_cmd;
+       cmd.len[0] = sizeof(struct iwl_phy_db_cmd);
+       cmd.data[1] = data;
+       cmd.len[1] = length;
+       cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
+
+       return iwl_trans_send_cmd(phy_db->trans, &cmd);
+}
+
+static int iwl_phy_db_send_all_channel_groups(
+                                       struct iwl_phy_db *phy_db,
+                                       enum iwl_phy_db_section_type type,
+                                       u8 max_ch_groups)
+{
+       u16 i;
+       int err;
+       struct iwl_phy_db_entry *entry;
+
+       /* Send all the  channel specific groups to operational fw */
+       for (i = 0; i < max_ch_groups; i++) {
+               entry = iwl_phy_db_get_section(phy_db,
+                                              type,
+                                              i);
+               if (!entry)
+                       return -EINVAL;
+
+               /* Send the requested PHY DB section */
+               err = iwl_send_phy_db_cmd(phy_db,
+                                         type,
+                                         entry->size,
+                                         entry->data);
+               if (err) {
+                       IWL_ERR(phy_db->trans,
+                               "Can't SEND phy_db section %d (%d), err %d",
+                               type, i, err);
+                       return err;
+               }
+
+               IWL_DEBUG_INFO(phy_db->trans,
+                              "Sent PHY_DB HCMD, type = %d num = %d",
+                              type, i);
+       }
+
+       return 0;
+}
+
+int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
+{
+       u8 *data = NULL;
+       u16 size = 0;
+       int err;
+
+       IWL_DEBUG_INFO(phy_db->trans,
+                      "Sending phy db data and configuration to runtime image\n");
+
+       /* Send PHY DB CFG section */
+       err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,
+                                         &data, &size, 0);
+       if (err) {
+               IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");
+               return err;
+       }
+
+       err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);
+       if (err) {
+               IWL_ERR(phy_db->trans,
+                       "Cannot send HCMD of  Phy DB cfg section\n");
+               return err;
+       }
+
+       err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,
+                                         &data, &size, 0);
+       if (err) {
+               IWL_ERR(phy_db->trans,
+                       "Cannot get Phy DB non specific channel section\n");
+               return err;
+       }
+
+       err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);
+       if (err) {
+               IWL_ERR(phy_db->trans,
+                       "Cannot send HCMD of Phy DB non specific channel section\n");
+               return err;
+       }
+
+       /* Send all the TXP channel specific data */
+       err = iwl_phy_db_send_all_channel_groups(phy_db,
+                                                IWL_PHY_DB_CALIB_CHG_PAPD,
+                                                IWL_NUM_PAPD_CH_GROUPS);
+       if (err) {
+               IWL_ERR(phy_db->trans,
+                       "Cannot send channel specific PAPD groups");
+               return err;
+       }
+
+       /* Send all the TXP channel specific data */
+       err = iwl_phy_db_send_all_channel_groups(phy_db,
+                                                IWL_PHY_DB_CALIB_CHG_TXP,
+                                                IWL_NUM_TXP_CH_GROUPS);
+       if (err) {
+               IWL_ERR(phy_db->trans,
+                       "Cannot send channel specific TX power groups");
+               return err;
+       }
+
+       IWL_DEBUG_INFO(phy_db->trans,
+                      "Finished sending phy db non channel data\n");
+       return 0;
+}
+EXPORT_SYMBOL(iwl_send_phy_db_data);
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/iwlwifi/iwl-phy-db.h
new file mode 100644 (file)
index 0000000..d0e43d9
--- /dev/null
@@ -0,0 +1,82 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __IWL_PHYDB_H__
+#define __IWL_PHYDB_H__
+
+#include <linux/types.h>
+
+#include "iwl-op-mode.h"
+#include "iwl-trans.h"
+
+struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans);
+
+void iwl_phy_db_free(struct iwl_phy_db *phy_db);
+
+int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
+                          gfp_t alloc_ctx);
+
+
+int iwl_send_phy_db_data(struct iwl_phy_db *phy_db);
+
+#endif /* __IWL_PHYDB_H__ */
index c3a4bb41e53370931832e8a7427d3361337e469d..f76e9cad7757f52db20d010519c040d4ed7185c1 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -97,6 +97,9 @@
 
 #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS         (0x00000800)
 
+/* Device system time */
+#define DEVICE_SYSTEM_TIME_REG 0xA0206C
+
 /**
  * Tx Scheduler
  *
index 81e8c7126d7293016d91e0a787d9a9991fea08ac..ce0c67b425ee8950a8e25fc68f2ca46a7860873c 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -466,19 +466,18 @@ static int iwl_test_indirect_read(struct iwl_test *tst, u32 addr, u32 size)
        /* Hard-coded periphery absolute address */
        if (IWL_ABS_PRPH_START <= addr &&
            addr < IWL_ABS_PRPH_START + PRPH_END) {
-                       spin_lock_irqsave(&trans->reg_lock, flags);
-                       iwl_grab_nic_access(trans);
+                       if (!iwl_trans_grab_nic_access(trans, false, &flags)) {
+                               return -EIO;
+                       }
                        iwl_write32(trans, HBUS_TARG_PRPH_RADDR,
                                    addr | (3 << 24));
                        for (i = 0; i < size; i += 4)
                                *(u32 *)(tst->mem.addr + i) =
                                        iwl_read32(trans, HBUS_TARG_PRPH_RDAT);
-                       iwl_release_nic_access(trans);
-                       spin_unlock_irqrestore(&trans->reg_lock, flags);
+                       iwl_trans_release_nic_access(trans, &flags);
        } else { /* target memory (SRAM) */
-               _iwl_read_targ_mem_dwords(trans, addr,
-                                         tst->mem.addr,
-                                         tst->mem.size / 4);
+               iwl_trans_read_mem(trans, addr, tst->mem.addr,
+                                  tst->mem.size / 4);
        }
 
        tst->mem.nchunks =
@@ -501,28 +500,25 @@ static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr,
 
        if (IWL_ABS_PRPH_START <= addr &&
            addr < IWL_ABS_PRPH_START + PRPH_END) {
-                       /* Periphery writes can be 1-3 bytes long, or DWORDs */
-                       if (size < 4) {
-                               memcpy(&val, buf, size);
-                               spin_lock_irqsave(&trans->reg_lock, flags);
-                               iwl_grab_nic_access(trans);
-                               iwl_write32(trans, HBUS_TARG_PRPH_WADDR,
-                                           (addr & 0x0000FFFF) |
-                                           ((size - 1) << 24));
-                               iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val);
-                               iwl_release_nic_access(trans);
-                               /* needed after consecutive writes w/o read */
-                               mmiowb();
-                               spin_unlock_irqrestore(&trans->reg_lock, flags);
-                       } else {
-                               if (size % 4)
-                                       return -EINVAL;
-                               for (i = 0; i < size; i += 4)
-                                       iwl_write_prph(trans, addr+i,
-                                                      *(u32 *)(buf+i));
-                       }
+               /* Periphery writes can be 1-3 bytes long, or DWORDs */
+               if (size < 4) {
+                       memcpy(&val, buf, size);
+                       if (!iwl_trans_grab_nic_access(trans, false, &flags))
+                                       return -EIO;
+                       iwl_write32(trans, HBUS_TARG_PRPH_WADDR,
+                                   (addr & 0x0000FFFF) |
+                                   ((size - 1) << 24));
+                       iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val);
+                       iwl_trans_release_nic_access(trans, &flags);
+               } else {
+                       if (size % 4)
+                               return -EINVAL;
+                       for (i = 0; i < size; i += 4)
+                               iwl_write_prph(trans, addr+i,
+                                              *(u32 *)(buf+i));
+               }
        } else if (iwl_test_valid_hw_addr(tst, addr)) {
-               _iwl_write_targ_mem_dwords(trans, addr, buf, size / 4);
+               iwl_trans_write_mem(trans, addr, buf, size / 4);
        } else {
                return -EINVAL;
        }
index e13ffa8acc02c201e62e8f2e77c7b0eef12278d3..7fbf4d717caa8a54d441ed07c71d2be855c5f498 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 6ba211b09426044df2a6564ae4cb08a749aa2d06..a963f45c6849cb12b00bc4984e49a9cc716c4df4 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index b76532e238c166f9c5095ac1ab5564c1e56a8aa0..0a3d4df5f434f7e698e9ff2790119f315fb1609a 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -193,11 +193,11 @@ struct iwl_rx_packet {
  * @CMD_ON_DEMAND: This command is sent by the test mode pipe.
  */
 enum CMD_MODE {
-       CMD_SYNC = 0,
-       CMD_ASYNC = BIT(0),
-       CMD_WANT_SKB = BIT(1),
-       CMD_WANT_HCMD = BIT(2),
-       CMD_ON_DEMAND = BIT(3),
+       CMD_SYNC                = 0,
+       CMD_ASYNC               = BIT(0),
+       CMD_WANT_SKB            = BIT(1),
+       CMD_WANT_HCMD           = BIT(2),
+       CMD_ON_DEMAND           = BIT(3),
 };
 
 #define DEF_CMD_PAYLOAD_SIZE 320
@@ -274,6 +274,7 @@ struct iwl_rx_cmd_buffer {
        struct page *_page;
        int _offset;
        bool _page_stolen;
+       u32 _rx_page_order;
        unsigned int truesize;
 };
 
@@ -294,6 +295,11 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
        return r->_page;
 }
 
+static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r)
+{
+       __free_pages(r->_page, r->_rx_page_order);
+}
+
 #define MAX_NO_RECLAIM_CMDS    6
 
 #define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo))))
@@ -307,6 +313,16 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
 #define IWL_MAX_TID_COUNT      8
 #define IWL_FRAME_LIMIT        64
 
+/**
+ * enum iwl_wowlan_status - WoWLAN image/device status
+ * @IWL_D3_STATUS_ALIVE: firmware is still running after resume
+ * @IWL_D3_STATUS_RESET: device was reset while suspended
+ */
+enum iwl_d3_status {
+       IWL_D3_STATUS_ALIVE,
+       IWL_D3_STATUS_RESET,
+};
+
 /**
  * struct iwl_trans_config - transport configuration
  *
@@ -321,6 +337,8 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
  * @n_no_reclaim_cmds: # of commands in list
  * @rx_buf_size_8k: 8 kB RX buffer size needed for A-MSDUs,
  *     if unset 4k will be the RX buffer size
+ * @bc_table_dword: set to true if the BC table expects the byte count to be
+ *     in DWORD (as opposed to bytes)
  * @queue_watchdog_timeout: time (in ms) after which queues
  *     are considered stuck and will trigger device restart
  * @command_names: array of command names, must be 256 entries
@@ -335,6 +353,7 @@ struct iwl_trans_config {
        int n_no_reclaim_cmds;
 
        bool rx_buf_size_8k;
+       bool bc_table_dword;
        unsigned int queue_watchdog_timeout;
        const char **command_names;
 };
@@ -360,9 +379,12 @@ struct iwl_trans;
  *     May sleep
  * @stop_device:stops the whole device (embedded CPU put to reset)
  *     May sleep
- * @wowlan_suspend: put the device into the correct mode for WoWLAN during
+ * @d3_suspend: put the device into the correct mode for WoWLAN during
  *     suspend. This is optional, if not implemented WoWLAN will not be
  *     supported. This callback may sleep.
+ * @d3_resume: resume the device after WoWLAN, enabling the opmode to
+ *     talk to the WoWLAN image to get its status. This is optional, if not
+ *     implemented WoWLAN will not be supported. This callback may sleep.
  * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted.
  *     If RFkill is asserted in the middle of a SYNC host command, it must
  *     return -ERFKILL straight away.
@@ -387,20 +409,31 @@ struct iwl_trans;
  * @read32: read a u32 register at offset ofs from the BAR
  * @read_prph: read a DWORD from a periphery register
  * @write_prph: write a DWORD to a periphery register
+ * @read_mem: read device's SRAM in DWORD
+ * @write_mem: write device's SRAM in DWORD. If %buf is %NULL, then the memory
+ *     will be zeroed.
  * @configure: configure parameters required by the transport layer from
  *     the op_mode. May be called several times before start_fw, can't be
  *     called after that.
  * @set_pmi: set the power pmi state
+ * @grab_nic_access: wake the NIC to be able to access non-HBUS regs.
+ *     Sleeping is not allowed between grab_nic_access and
+ *     release_nic_access.
+ * @release_nic_access: let the NIC go to sleep. The "flags" parameter
+ *     must be the same one that was sent before to the grab_nic_access.
+ * @set_bits_mask - set SRAM register according to value and mask.
  */
 struct iwl_trans_ops {
 
        int (*start_hw)(struct iwl_trans *iwl_trans);
        void (*stop_hw)(struct iwl_trans *iwl_trans, bool op_mode_leaving);
-       int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw);
+       int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw,
+                       bool run_in_rfkill);
        void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
        void (*stop_device)(struct iwl_trans *trans);
 
-       void (*wowlan_suspend)(struct iwl_trans *trans);
+       void (*d3_suspend)(struct iwl_trans *trans);
+       int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status);
 
        int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
 
@@ -424,9 +457,19 @@ struct iwl_trans_ops {
        u32 (*read32)(struct iwl_trans *trans, u32 ofs);
        u32 (*read_prph)(struct iwl_trans *trans, u32 ofs);
        void (*write_prph)(struct iwl_trans *trans, u32 ofs, u32 val);
+       int (*read_mem)(struct iwl_trans *trans, u32 addr,
+                       void *buf, int dwords);
+       int (*write_mem)(struct iwl_trans *trans, u32 addr,
+                        void *buf, int dwords);
        void (*configure)(struct iwl_trans *trans,
                          const struct iwl_trans_config *trans_cfg);
        void (*set_pmi)(struct iwl_trans *trans, bool state);
+       bool (*grab_nic_access)(struct iwl_trans *trans, bool silent,
+                               unsigned long *flags);
+       void (*release_nic_access)(struct iwl_trans *trans,
+                                  unsigned long *flags);
+       void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask,
+                             u32 value);
 };
 
 /**
@@ -446,7 +489,6 @@ enum iwl_trans_state {
  * @ops - pointer to iwl_trans_ops
  * @op_mode - pointer to the op_mode
  * @cfg - pointer to the configuration
- * @reg_lock - protect hw register access
  * @dev - pointer to struct device * that represents the device
  * @hw_id: a u32 with the ID of the device / subdevice.
  *     Set during transport allocation.
@@ -467,7 +509,6 @@ struct iwl_trans {
        struct iwl_op_mode *op_mode;
        const struct iwl_cfg *cfg;
        enum iwl_trans_state state;
-       spinlock_t reg_lock;
 
        struct device *dev;
        u32 hw_rev;
@@ -528,13 +569,14 @@ static inline void iwl_trans_fw_alive(struct iwl_trans *trans, u32 scd_addr)
 }
 
 static inline int iwl_trans_start_fw(struct iwl_trans *trans,
-                                    const struct fw_img *fw)
+                                    const struct fw_img *fw,
+                                    bool run_in_rfkill)
 {
        might_sleep();
 
        WARN_ON_ONCE(!trans->rx_mpdu_cmd);
 
-       return trans->ops->start_fw(trans, fw);
+       return trans->ops->start_fw(trans, fw, run_in_rfkill);
 }
 
 static inline void iwl_trans_stop_device(struct iwl_trans *trans)
@@ -546,10 +588,17 @@ static inline void iwl_trans_stop_device(struct iwl_trans *trans)
        trans->state = IWL_TRANS_NO_FW;
 }
 
-static inline void iwl_trans_wowlan_suspend(struct iwl_trans *trans)
+static inline void iwl_trans_d3_suspend(struct iwl_trans *trans)
+{
+       might_sleep();
+       trans->ops->d3_suspend(trans);
+}
+
+static inline int iwl_trans_d3_resume(struct iwl_trans *trans,
+                                     enum iwl_d3_status *status)
 {
        might_sleep();
-       trans->ops->wowlan_suspend(trans);
+       return trans->ops->d3_resume(trans, status);
 }
 
 static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
@@ -636,7 +685,7 @@ static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans)
 }
 
 static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans,
-                                           struct dentry *dir)
+                                          struct dentry *dir)
 {
        return trans->ops->dbgfs_register(trans, dir);
 }
@@ -679,11 +728,63 @@ static inline void iwl_trans_write_prph(struct iwl_trans *trans, u32 ofs,
        return trans->ops->write_prph(trans, ofs, val);
 }
 
+static inline int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr,
+                                    void *buf, int dwords)
+{
+       return trans->ops->read_mem(trans, addr, buf, dwords);
+}
+
+#define iwl_trans_read_mem_bytes(trans, addr, buf, bufsize)                  \
+       do {                                                                  \
+               if (__builtin_constant_p(bufsize))                            \
+                       BUILD_BUG_ON((bufsize) % sizeof(u32));                \
+               iwl_trans_read_mem(trans, addr, buf, (bufsize) / sizeof(u32));\
+       } while (0)
+
+static inline u32 iwl_trans_read_mem32(struct iwl_trans *trans, u32 addr)
+{
+       u32 value;
+
+       if (WARN_ON(iwl_trans_read_mem(trans, addr, &value, 1)))
+               return 0xa5a5a5a5;
+
+       return value;
+}
+
+static inline int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr,
+                                     void *buf, int dwords)
+{
+       return trans->ops->write_mem(trans, addr, buf, dwords);
+}
+
+static inline u32 iwl_trans_write_mem32(struct iwl_trans *trans, u32 addr,
+                                       u32 val)
+{
+       return iwl_trans_write_mem(trans, addr, &val, 1);
+}
+
 static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state)
 {
        trans->ops->set_pmi(trans, state);
 }
 
+static inline void
+iwl_trans_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value)
+{
+       trans->ops->set_bits_mask(trans, reg, mask, value);
+}
+
+#define iwl_trans_grab_nic_access(trans, silent, flags)        \
+       __cond_lock(nic_access,                         \
+                   likely((trans)->ops->grab_nic_access(trans, silent, flags)))
+
+static inline void __releases(nic_access)
+iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags)
+{
+       trans->ops->release_nic_access(trans, flags);
+       __release(nic_access);
+}
+
 /*****************************************************
 * driver (transport) register/unregister functions
 ******************************************************/
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile
new file mode 100644 (file)
index 0000000..807b250
--- /dev/null
@@ -0,0 +1,10 @@
+obj-$(CONFIG_IWLMVM)   += iwlmvm.o
+iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
+iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
+iwlmvm-y += scan.o time-event.o rs.o
+iwlmvm-y += power.o
+iwlmvm-y += led.o
+iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
+iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
+
+ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/iwlwifi/mvm/binding.c
new file mode 100644 (file)
index 0000000..73d24aa
--- /dev/null
@@ -0,0 +1,197 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/mac80211.h>
+#include "fw-api.h"
+#include "mvm.h"
+
+struct iwl_mvm_iface_iterator_data {
+       struct ieee80211_vif *ignore_vif;
+       int idx;
+
+       struct iwl_mvm_phy_ctxt *phyctxt;
+
+       u16 ids[MAX_MACS_IN_BINDING];
+       u16 colors[MAX_MACS_IN_BINDING];
+};
+
+static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action,
+                              struct iwl_mvm_iface_iterator_data *data)
+{
+       struct iwl_binding_cmd cmd;
+       struct iwl_mvm_phy_ctxt *phyctxt = data->phyctxt;
+       int i, ret;
+       u32 status;
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
+                                                          phyctxt->color));
+       cmd.action = cpu_to_le32(action);
+       cmd.phy = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
+                                                 phyctxt->color));
+
+       for (i = 0; i < MAX_MACS_IN_BINDING; i++)
+               cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID);
+       for (i = 0; i < data->idx; i++)
+               cmd.macs[i] = cpu_to_le32(FW_CMD_ID_AND_COLOR(data->ids[i],
+                                                             data->colors[i]));
+
+       status = 0;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD,
+                                         sizeof(cmd), &cmd, &status);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to send binding (action:%d): %d\n",
+                       action, ret);
+               return ret;
+       }
+
+       if (status) {
+               IWL_ERR(mvm, "Binding command failed: %u\n", status);
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
+static void iwl_mvm_iface_iterator(void *_data, u8 *mac,
+                                  struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_iface_iterator_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif == data->ignore_vif)
+               return;
+
+       if (mvmvif->phy_ctxt != data->phyctxt)
+               return;
+
+       if (WARN_ON_ONCE(data->idx >= MAX_MACS_IN_BINDING))
+               return;
+
+       data->ids[data->idx] = mvmvif->id;
+       data->colors[data->idx] = mvmvif->color;
+       data->idx++;
+}
+
+static int iwl_mvm_binding_update(struct iwl_mvm *mvm,
+                                 struct ieee80211_vif *vif,
+                                 struct iwl_mvm_phy_ctxt *phyctxt,
+                                 bool add)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_iface_iterator_data data = {
+               .ignore_vif = vif,
+               .phyctxt = phyctxt,
+       };
+       u32 action = FW_CTXT_ACTION_MODIFY;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_iface_iterator,
+                                                  &data);
+
+       /*
+        * If there are no other interfaces yet we
+        * need to create a new binding.
+        */
+       if (data.idx == 0) {
+               if (add)
+                       action = FW_CTXT_ACTION_ADD;
+               else
+                       action = FW_CTXT_ACTION_REMOVE;
+       }
+
+       if (add) {
+               if (WARN_ON_ONCE(data.idx >= MAX_MACS_IN_BINDING))
+                       return -EINVAL;
+
+               data.ids[data.idx] = mvmvif->id;
+               data.colors[data.idx] = mvmvif->color;
+               data.idx++;
+       }
+
+       return iwl_mvm_binding_cmd(mvm, action, &data);
+}
+
+int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
+               return -EINVAL;
+
+       return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true);
+}
+
+int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
+               return -EINVAL;
+
+       return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
new file mode 100644 (file)
index 0000000..9a95c37
--- /dev/null
@@ -0,0 +1,841 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/cfg80211.h>
+#include <net/ipv6.h>
+#include "iwl-modparams.h"
+#include "fw-api.h"
+#include "mvm.h"
+
+void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct cfg80211_gtk_rekey_data *data)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (iwlwifi_mod_params.sw_crypto)
+               return;
+
+       mutex_lock(&mvm->mutex);
+
+       memcpy(mvmvif->rekey_data.kek, data->kek, NL80211_KEK_LEN);
+       memcpy(mvmvif->rekey_data.kck, data->kck, NL80211_KCK_LEN);
+       mvmvif->rekey_data.replay_ctr =
+               cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr));
+       mvmvif->rekey_data.valid = true;
+
+       mutex_unlock(&mvm->mutex);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             struct inet6_dev *idev)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct inet6_ifaddr *ifa;
+       int idx = 0;
+
+       read_lock(&idev->lock);
+       list_for_each_entry(ifa, &idev->addr_list, if_list) {
+               mvmvif->target_ipv6_addrs[idx] = ifa->addr;
+               idx++;
+               if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS)
+                       break;
+       }
+       read_unlock(&idev->lock);
+
+       mvmvif->num_target_ipv6_addrs = idx;
+}
+#endif
+
+void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif, int idx)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mvmvif->tx_key_idx = idx;
+}
+
+static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out)
+{
+       int i;
+
+       for (i = 0; i < IWL_P1K_SIZE; i++)
+               out[i] = cpu_to_le16(p1k[i]);
+}
+
+struct wowlan_key_data {
+       struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc;
+       struct iwl_wowlan_tkip_params_cmd *tkip;
+       bool error, use_rsc_tsc, use_tkip;
+       int gtk_key_idx;
+};
+
+static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_sta *sta,
+                                       struct ieee80211_key_conf *key,
+                                       void *_data)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct wowlan_key_data *data = _data;
+       struct aes_sc *aes_sc, *aes_tx_sc = NULL;
+       struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL;
+       struct iwl_p1k_cache *rx_p1ks;
+       u8 *rx_mic_key;
+       struct ieee80211_key_seq seq;
+       u32 cur_rx_iv32 = 0;
+       u16 p1k[IWL_P1K_SIZE];
+       int ret, i;
+
+       mutex_lock(&mvm->mutex);
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104: { /* hack it for now */
+               struct {
+                       struct iwl_mvm_wep_key_cmd wep_key_cmd;
+                       struct iwl_mvm_wep_key wep_key;
+               } __packed wkc = {
+                       .wep_key_cmd.mac_id_n_color =
+                               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                               mvmvif->color)),
+                       .wep_key_cmd.num_keys = 1,
+                       /* firmware sets STA_KEY_FLG_WEP_13BYTES */
+                       .wep_key_cmd.decryption_type = STA_KEY_FLG_WEP,
+                       .wep_key.key_index = key->keyidx,
+                       .wep_key.key_size = key->keylen,
+               };
+
+               /*
+                * This will fail -- the key functions don't set support
+                * pairwise WEP keys. However, that's better than silently
+                * failing WoWLAN. Or maybe not?
+                */
+               if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+                       break;
+
+               memcpy(&wkc.wep_key.key[3], key->key, key->keylen);
+               if (key->keyidx == mvmvif->tx_key_idx) {
+                       /* TX key must be at offset 0 */
+                       wkc.wep_key.key_offset = 0;
+               } else {
+                       /* others start at 1 */
+                       data->gtk_key_idx++;
+                       wkc.wep_key.key_offset = data->gtk_key_idx;
+               }
+
+               ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, CMD_SYNC,
+                                          sizeof(wkc), &wkc);
+               data->error = ret != 0;
+
+               /* don't upload key again */
+               goto out_unlock;
+       }
+       default:
+               data->error = true;
+               goto out_unlock;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               /*
+                * Ignore CMAC keys -- the WoWLAN firmware doesn't support them
+                * but we also shouldn't abort suspend due to that. It does have
+                * support for the IGTK key renewal, but doesn't really use the
+                * IGTK for anything. This means we could spuriously wake up or
+                * be deauthenticated, but that was considered acceptable.
+                */
+               goto out_unlock;
+       case WLAN_CIPHER_SUITE_TKIP:
+               if (sta) {
+                       tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc;
+                       tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc;
+
+                       rx_p1ks = data->tkip->rx_uni;
+
+                       ieee80211_get_key_tx_seq(key, &seq);
+                       tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16);
+                       tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32);
+
+                       ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k);
+                       iwl_mvm_convert_p1k(p1k, data->tkip->tx.p1k);
+
+                       memcpy(data->tkip->mic_keys.tx,
+                              &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY],
+                              IWL_MIC_KEY_SIZE);
+
+                       rx_mic_key = data->tkip->mic_keys.rx_unicast;
+               } else {
+                       tkip_sc =
+                               data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc;
+                       rx_p1ks = data->tkip->rx_multi;
+                       rx_mic_key = data->tkip->mic_keys.rx_mcast;
+               }
+
+               /*
+                * For non-QoS this relies on the fact that both the uCode and
+                * mac80211 use TID 0 (as they need to to avoid replay attacks)
+                * for checking the IV in the frames.
+                */
+               for (i = 0; i < IWL_NUM_RSC; i++) {
+                       ieee80211_get_key_rx_seq(key, i, &seq);
+                       tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16);
+                       tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32);
+                       /* wrapping isn't allowed, AP must rekey */
+                       if (seq.tkip.iv32 > cur_rx_iv32)
+                               cur_rx_iv32 = seq.tkip.iv32;
+               }
+
+               ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid,
+                                         cur_rx_iv32, p1k);
+               iwl_mvm_convert_p1k(p1k, rx_p1ks[0].p1k);
+               ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid,
+                                         cur_rx_iv32 + 1, p1k);
+               iwl_mvm_convert_p1k(p1k, rx_p1ks[1].p1k);
+
+               memcpy(rx_mic_key,
+                      &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY],
+                      IWL_MIC_KEY_SIZE);
+
+               data->use_tkip = true;
+               data->use_rsc_tsc = true;
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               if (sta) {
+                       u8 *pn = seq.ccmp.pn;
+
+                       aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc;
+                       aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc;
+
+                       ieee80211_get_key_tx_seq(key, &seq);
+                       aes_tx_sc->pn = cpu_to_le64((u64)pn[5] |
+                                                   ((u64)pn[4] << 8) |
+                                                   ((u64)pn[3] << 16) |
+                                                   ((u64)pn[2] << 24) |
+                                                   ((u64)pn[1] << 32) |
+                                                   ((u64)pn[0] << 40));
+               } else {
+                       aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc;
+               }
+
+               /*
+                * For non-QoS this relies on the fact that both the uCode and
+                * mac80211 use TID 0 for checking the IV in the frames.
+                */
+               for (i = 0; i < IWL_NUM_RSC; i++) {
+                       u8 *pn = seq.ccmp.pn;
+
+                       ieee80211_get_key_rx_seq(key, i, &seq);
+                       aes_sc->pn = cpu_to_le64((u64)pn[5] |
+                                                ((u64)pn[4] << 8) |
+                                                ((u64)pn[3] << 16) |
+                                                ((u64)pn[2] << 24) |
+                                                ((u64)pn[1] << 32) |
+                                                ((u64)pn[0] << 40));
+               }
+               data->use_rsc_tsc = true;
+               break;
+       }
+
+       /*
+        * The D3 firmware hardcodes the key offset 0 as the key it uses
+        * to transmit packets to the AP, i.e. the PTK.
+        */
+       if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+               key->hw_key_idx = 0;
+       } else {
+               data->gtk_key_idx++;
+               key->hw_key_idx = data->gtk_key_idx;
+       }
+
+       ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, true);
+       data->error = ret != 0;
+out_unlock:
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
+                                struct cfg80211_wowlan *wowlan)
+{
+       struct iwl_wowlan_patterns_cmd *pattern_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = WOWLAN_PATTERNS,
+               .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+               .flags = CMD_SYNC,
+       };
+       int i, err;
+
+       if (!wowlan->n_patterns)
+               return 0;
+
+       cmd.len[0] = sizeof(*pattern_cmd) +
+               wowlan->n_patterns * sizeof(struct iwl_wowlan_pattern);
+
+       pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL);
+       if (!pattern_cmd)
+               return -ENOMEM;
+
+       pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns);
+
+       for (i = 0; i < wowlan->n_patterns; i++) {
+               int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
+
+               memcpy(&pattern_cmd->patterns[i].mask,
+                      wowlan->patterns[i].mask, mask_len);
+               memcpy(&pattern_cmd->patterns[i].pattern,
+                      wowlan->patterns[i].pattern,
+                      wowlan->patterns[i].pattern_len);
+               pattern_cmd->patterns[i].mask_size = mask_len;
+               pattern_cmd->patterns[i].pattern_size =
+                       wowlan->patterns[i].pattern_len;
+       }
+
+       cmd.data[0] = pattern_cmd;
+       err = iwl_mvm_send_cmd(mvm, &cmd);
+       kfree(pattern_cmd);
+       return err;
+}
+
+static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
+                                     struct ieee80211_vif *vif)
+{
+       struct iwl_proto_offload_cmd cmd = {};
+#if IS_ENABLED(CONFIG_IPV6)
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int i;
+
+       if (mvmvif->num_target_ipv6_addrs) {
+               cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_NS);
+               memcpy(cmd.ndp_mac_addr, vif->addr, ETH_ALEN);
+       }
+
+       BUILD_BUG_ON(sizeof(cmd.target_ipv6_addr[i]) !=
+                    sizeof(mvmvif->target_ipv6_addrs[i]));
+
+       for (i = 0; i < mvmvif->num_target_ipv6_addrs; i++)
+               memcpy(cmd.target_ipv6_addr[i],
+                      &mvmvif->target_ipv6_addrs[i],
+                      sizeof(cmd.target_ipv6_addr[i]));
+#endif
+
+       if (vif->bss_conf.arp_addr_cnt) {
+               cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_ARP);
+               cmd.host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
+               memcpy(cmd.arp_mac_addr, vif->addr, ETH_ALEN);
+       }
+
+       if (!cmd.enabled)
+               return 0;
+
+       return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC,
+                                   sizeof(cmd), &cmd);
+}
+
+struct iwl_d3_iter_data {
+       struct iwl_mvm *mvm;
+       struct ieee80211_vif *vif;
+       bool error;
+};
+
+static void iwl_mvm_d3_iface_iterator(void *_data, u8 *mac,
+                                     struct ieee80211_vif *vif)
+{
+       struct iwl_d3_iter_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return;
+
+       if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+               return;
+
+       if (data->vif) {
+               IWL_ERR(data->mvm, "More than one managed interface active!\n");
+               data->error = true;
+               return;
+       }
+
+       data->vif = vif;
+}
+
+static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               struct ieee80211_sta *ap_sta)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct ieee80211_chanctx_conf *ctx;
+       u8 chains_static, chains_dynamic;
+       struct cfg80211_chan_def chandef;
+       int ret, i;
+       struct iwl_binding_cmd binding_cmd = {};
+       struct iwl_time_quota_cmd quota_cmd = {};
+       u32 status;
+
+       /* add back the PHY */
+       if (WARN_ON(!mvmvif->phy_ctxt))
+               return -EINVAL;
+
+       rcu_read_lock();
+       ctx = rcu_dereference(vif->chanctx_conf);
+       if (WARN_ON(!ctx)) {
+               rcu_read_unlock();
+               return -EINVAL;
+       }
+       chandef = ctx->def;
+       chains_static = ctx->rx_chains_static;
+       chains_dynamic = ctx->rx_chains_dynamic;
+       rcu_read_unlock();
+
+       ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, &chandef,
+                                  chains_static, chains_dynamic);
+       if (ret)
+               return ret;
+
+       /* add back the MAC */
+       mvmvif->uploaded = false;
+
+       if (WARN_ON(!vif->bss_conf.assoc))
+               return -EINVAL;
+       /* hack */
+       vif->bss_conf.assoc = false;
+       ret = iwl_mvm_mac_ctxt_add(mvm, vif);
+       vif->bss_conf.assoc = true;
+       if (ret)
+               return ret;
+
+       /* add back binding - XXX refactor? */
+       binding_cmd.id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
+                                               mvmvif->phy_ctxt->color));
+       binding_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+       binding_cmd.phy =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
+                                               mvmvif->phy_ctxt->color));
+       binding_cmd.macs[0] = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                             mvmvif->color));
+       for (i = 1; i < MAX_MACS_IN_BINDING; i++)
+               binding_cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID);
+
+       status = 0;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD,
+                                         sizeof(binding_cmd), &binding_cmd,
+                                         &status);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to add binding: %d\n", ret);
+               return ret;
+       }
+
+       if (status) {
+               IWL_ERR(mvm, "Binding command failed: %u\n", status);
+               return -EIO;
+       }
+
+       ret = iwl_mvm_sta_add_to_fw(mvm, ap_sta);
+       if (ret)
+               return ret;
+       rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta);
+
+       ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+       if (ret)
+               return ret;
+
+       /* and some quota */
+       quota_cmd.quotas[0].id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
+                                               mvmvif->phy_ctxt->color));
+       quota_cmd.quotas[0].quota = cpu_to_le32(100);
+       quota_cmd.quotas[0].max_duration = cpu_to_le32(1000);
+
+       for (i = 1; i < MAX_BINDINGS; i++)
+               quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
+                                  sizeof(quota_cmd), &quota_cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
+
+       return 0;
+}
+
+int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_d3_iter_data suspend_iter_data = {
+               .mvm = mvm,
+       };
+       struct ieee80211_vif *vif;
+       struct iwl_mvm_vif *mvmvif;
+       struct ieee80211_sta *ap_sta;
+       struct iwl_mvm_sta *mvm_ap_sta;
+       struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
+       struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
+       struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
+       struct iwl_d3_manager_config d3_cfg_cmd = {};
+       struct wowlan_key_data key_data = {
+               .use_rsc_tsc = false,
+               .tkip = &tkip_cmd,
+               .use_tkip = false,
+       };
+       int ret, i;
+       u16 seq;
+       u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT;
+
+       if (WARN_ON(!wowlan))
+               return -EINVAL;
+
+       key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
+       if (!key_data.rsc_tsc)
+               return -ENOMEM;
+
+       mutex_lock(&mvm->mutex);
+
+       old_aux_sta_id = mvm->aux_sta.sta_id;
+
+       /* see if there's only a single BSS vif and it's associated */
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_d3_iface_iterator, &suspend_iter_data);
+
+       if (suspend_iter_data.error || !suspend_iter_data.vif) {
+               ret = 1;
+               goto out_noreset;
+       }
+
+       vif = suspend_iter_data.vif;
+       mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       ap_sta = rcu_dereference_protected(
+                       mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
+                       lockdep_is_held(&mvm->mutex));
+       if (IS_ERR_OR_NULL(ap_sta)) {
+               ret = -EINVAL;
+               goto out_noreset;
+       }
+
+       mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
+
+       /*
+        * The D3 firmware still hardcodes the AP station ID for the
+        * BSS we're associated with as 0. Store the real STA ID here
+        * and assign 0. When we leave this function, we'll restore
+        * the original value for the resume code.
+        */
+       old_ap_sta_id = mvm_ap_sta->sta_id;
+       mvm_ap_sta->sta_id = 0;
+       mvmvif->ap_sta_id = 0;
+
+       /* TODO: wowlan_config_cmd.wowlan_ba_teardown_tids */
+
+       wowlan_config_cmd.is_11n_connection = ap_sta->ht_cap.ht_supported;
+
+       /*
+        * We know the last used seqno, and the uCode expects to know that
+        * one, it will increment before TX.
+        */
+       seq = mvm_ap_sta->last_seq_ctl & IEEE80211_SCTL_SEQ;
+       wowlan_config_cmd.non_qos_seq = cpu_to_le16(seq);
+
+       /*
+        * For QoS counters, we store the one to use next, so subtract 0x10
+        * since the uCode will add 0x10 *before* using the value while we
+        * increment after using the value (i.e. store the next value to use).
+        */
+       for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+               seq = mvm_ap_sta->tid_data[i].seq_number;
+               seq -= 0x10;
+               wowlan_config_cmd.qos_seq[i] = cpu_to_le16(seq);
+       }
+
+       if (wowlan->disconnect)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
+                                   IWL_WOWLAN_WAKEUP_LINK_CHANGE);
+       if (wowlan->magic_pkt)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET);
+       if (wowlan->gtk_rekey_failure)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
+       if (wowlan->eap_identity_req)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ);
+       if (wowlan->four_way_handshake)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
+       if (wowlan->n_patterns)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
+
+       if (wowlan->rfkill_release)
+               d3_cfg_cmd.wakeup_flags |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
+
+       iwl_mvm_cancel_scan(mvm);
+
+       iwl_trans_stop_device(mvm->trans);
+
+       /*
+        * Set the HW restart bit -- this is mostly true as we're
+        * going to load new firmware and reprogram that, though
+        * the reprogramming is going to be manual to avoid adding
+        * all the MACs that aren't support.
+        * We don't have to clear up everything though because the
+        * reprogramming is manual. When we resume, we'll actually
+        * go through a proper restart sequence again to switch
+        * back to the runtime firmware image.
+        */
+       set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+
+       /* We reprogram keys and shouldn't allocate new key indices */
+       memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
+
+       /*
+        * The D3 firmware still hardcodes the AP station ID for the
+        * BSS we're associated with as 0. As a result, we have to move
+        * the auxiliary station to ID 1 so the ID 0 remains free for
+        * the AP station for later.
+        * We set the sta_id to 1 here, and reset it to its previous
+        * value (that we stored above) later.
+        */
+       mvm->aux_sta.sta_id = 1;
+
+       ret = iwl_mvm_load_d3_fw(mvm);
+       if (ret)
+               goto out;
+
+       ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta);
+       if (ret)
+               goto out;
+
+       if (!iwlwifi_mod_params.sw_crypto) {
+               /*
+                * This needs to be unlocked due to lock ordering
+                * constraints. Since we're in the suspend path
+                * that isn't really a problem though.
+                */
+               mutex_unlock(&mvm->mutex);
+               ieee80211_iter_keys(mvm->hw, vif,
+                                   iwl_mvm_wowlan_program_keys,
+                                   &key_data);
+               mutex_lock(&mvm->mutex);
+               if (key_data.error) {
+                       ret = -EIO;
+                       goto out;
+               }
+
+               if (key_data.use_rsc_tsc) {
+                       struct iwl_host_cmd rsc_tsc_cmd = {
+                               .id = WOWLAN_TSC_RSC_PARAM,
+                               .flags = CMD_SYNC,
+                               .data[0] = key_data.rsc_tsc,
+                               .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+                               .len[0] = sizeof(*key_data.rsc_tsc),
+                       };
+
+                       ret = iwl_mvm_send_cmd(mvm, &rsc_tsc_cmd);
+                       if (ret)
+                               goto out;
+               }
+
+               if (key_data.use_tkip) {
+                       ret = iwl_mvm_send_cmd_pdu(mvm,
+                                                  WOWLAN_TKIP_PARAM,
+                                                  CMD_SYNC, sizeof(tkip_cmd),
+                                                  &tkip_cmd);
+                       if (ret)
+                               goto out;
+               }
+
+               if (mvmvif->rekey_data.valid) {
+                       memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
+                       memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck,
+                              NL80211_KCK_LEN);
+                       kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
+                       memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek,
+                              NL80211_KEK_LEN);
+                       kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
+                       kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;
+
+                       ret = iwl_mvm_send_cmd_pdu(mvm,
+                                                  WOWLAN_KEK_KCK_MATERIAL,
+                                                  CMD_SYNC,
+                                                  sizeof(kek_kck_cmd),
+                                                  &kek_kck_cmd);
+                       if (ret)
+                               goto out;
+               }
+       }
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION,
+                                  CMD_SYNC, sizeof(wowlan_config_cmd),
+                                  &wowlan_config_cmd);
+       if (ret)
+               goto out;
+
+       ret = iwl_mvm_send_patterns(mvm, wowlan);
+       if (ret)
+               goto out;
+
+       ret = iwl_mvm_send_proto_offload(mvm, vif);
+       if (ret)
+               goto out;
+
+       /* must be last -- this switches firmware state */
+       ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC,
+                                  sizeof(d3_cfg_cmd), &d3_cfg_cmd);
+       if (ret)
+               goto out;
+
+       clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+
+       iwl_trans_d3_suspend(mvm->trans);
+ out:
+       mvm->aux_sta.sta_id = old_aux_sta_id;
+       mvm_ap_sta->sta_id = old_ap_sta_id;
+       mvmvif->ap_sta_id = old_ap_sta_id;
+ out_noreset:
+       kfree(key_data.rsc_tsc);
+       if (ret < 0)
+               ieee80211_restart_hw(mvm->hw);
+
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+int iwl_mvm_resume(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_d3_iter_data resume_iter_data = {
+               .mvm = mvm,
+       };
+       struct ieee80211_vif *vif = NULL;
+       u32 base;
+       int ret;
+       enum iwl_d3_status d3_status;
+       struct error_table_start {
+               /* cf. struct iwl_error_event_table */
+               u32 valid;
+               u32 error_id;
+       } err_info;
+
+       mutex_lock(&mvm->mutex);
+
+       /* get the BSS vif pointer again */
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_d3_iface_iterator, &resume_iter_data);
+
+       if (WARN_ON(resume_iter_data.error || !resume_iter_data.vif))
+               goto out_unlock;
+
+       vif = resume_iter_data.vif;
+
+       ret = iwl_trans_d3_resume(mvm->trans, &d3_status);
+       if (ret)
+               goto out_unlock;
+
+       if (d3_status != IWL_D3_STATUS_ALIVE) {
+               IWL_INFO(mvm, "Device was reset during suspend\n");
+               goto out_unlock;
+       }
+
+       base = mvm->error_event_table;
+
+       iwl_trans_read_mem_bytes(mvm->trans, base,
+                                &err_info, sizeof(err_info));
+
+       if (err_info.valid) {
+               IWL_INFO(mvm, "error table is valid (%d)\n",
+                        err_info.valid);
+               if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN)
+                       IWL_ERR(mvm, "this was due to RF-kill\n");
+               goto out_unlock;
+       }
+
+       /* TODO: get status and whatever else ... */
+       ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_GET_STATUSES, CMD_SYNC, 0, NULL);
+       if (ret)
+               IWL_ERR(mvm, "failed to query status (%d)\n", ret);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL);
+       if (ret)
+               IWL_ERR(mvm, "failed to query offloads (%d)\n", ret);
+
+ out_unlock:
+       mutex_unlock(&mvm->mutex);
+
+       if (vif)
+               ieee80211_resume_disconnect(vif);
+
+       /* return 1 to reconfigure the device */
+       set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+       return 1;
+}
+
+void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       device_set_wakeup_enable(mvm->trans->dev, enabled);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
new file mode 100644 (file)
index 0000000..c1bdb55
--- /dev/null
@@ -0,0 +1,378 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include "mvm.h"
+#include "sta.h"
+#include "iwl-io.h"
+
+struct iwl_dbgfs_mvm_ctx {
+       struct iwl_mvm *mvm;
+       struct ieee80211_vif *vif;
+};
+
+static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t iwl_dbgfs_tx_flush_write(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+
+       char buf[16];
+       int buf_size, ret;
+       u32 scd_q_msk;
+
+       if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
+               return -EIO;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       if (sscanf(buf, "%x", &scd_q_msk) != 1)
+               return -EINVAL;
+
+       IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk);
+
+       mutex_lock(&mvm->mutex);
+       ret =  iwl_mvm_flush_tx_path(mvm, scd_q_msk, true) ? : count;
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static ssize_t iwl_dbgfs_sta_drain_write(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       struct ieee80211_sta *sta;
+
+       char buf[8];
+       int buf_size, sta_id, drain, ret;
+
+       if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
+               return -EIO;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       if (sscanf(buf, "%d %d", &sta_id, &drain) != 2)
+               return -EINVAL;
+
+       mutex_lock(&mvm->mutex);
+
+       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                       lockdep_is_held(&mvm->mutex));
+       if (IS_ERR_OR_NULL(sta))
+               ret = -ENOENT;
+       else
+               ret = iwl_mvm_drain_sta(mvm, (void *)sta->drv_priv, drain) ? :
+                       count;
+
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       const struct fw_img *img;
+       int ofs, len, pos = 0;
+       size_t bufsz, ret;
+       char *buf;
+       u8 *ptr;
+
+       /* default is to dump the entire data segment */
+       if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) {
+               mvm->dbgfs_sram_offset = 0x800000;
+               if (!mvm->ucode_loaded)
+                       return -EINVAL;
+               img = &mvm->fw->img[mvm->cur_ucode];
+               mvm->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+       }
+       len = mvm->dbgfs_sram_len;
+
+       bufsz = len * 4 + 256;
+       buf = kzalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ptr = kzalloc(len, GFP_KERNEL);
+       if (!ptr) {
+               kfree(buf);
+               return -ENOMEM;
+       }
+
+       pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len);
+       pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n",
+                        mvm->dbgfs_sram_offset);
+
+       iwl_trans_read_mem_bytes(mvm->trans,
+                                mvm->dbgfs_sram_offset,
+                                ptr, len);
+       for (ofs = 0; ofs < len; ofs += 16) {
+               pos += scnprintf(buf + pos, bufsz - pos, "0x%.4x ", ofs);
+               hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos,
+                                  bufsz - pos, false);
+               pos += strlen(buf + pos);
+               if (bufsz - pos > 0)
+                       buf[pos++] = '\n';
+       }
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+
+       kfree(buf);
+       kfree(ptr);
+
+       return ret;
+}
+
+static ssize_t iwl_dbgfs_sram_write(struct file *file,
+                                   const char __user *user_buf, size_t count,
+                                   loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char buf[64];
+       int buf_size;
+       u32 offset, len;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) -  1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       if (sscanf(buf, "%x,%x", &offset, &len) == 2) {
+               if ((offset & 0x3) || (len & 0x3))
+                       return -EINVAL;
+               mvm->dbgfs_sram_offset = offset;
+               mvm->dbgfs_sram_len = len;
+       } else {
+               mvm->dbgfs_sram_offset = 0;
+               mvm->dbgfs_sram_len = 0;
+       }
+
+       return count;
+}
+
+static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       struct ieee80211_sta *sta;
+       char buf[400];
+       int i, pos = 0, bufsz = sizeof(buf);
+
+       mutex_lock(&mvm->mutex);
+
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+               pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i);
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+                                               lockdep_is_held(&mvm->mutex));
+               if (!sta)
+                       pos += scnprintf(buf + pos, bufsz - pos, "N/A\n");
+               else if (IS_ERR(sta))
+                       pos += scnprintf(buf + pos, bufsz - pos, "%ld\n",
+                                        PTR_ERR(sta));
+               else
+                       pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",
+                                        sta->addr);
+       }
+
+       mutex_unlock(&mvm->mutex);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_power_down_allow_write(struct file *file,
+                                               const char __user *user_buf,
+                                               size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char buf[8] = {};
+       int allow;
+
+       if (!mvm->ucode_loaded)
+               return -EIO;
+
+       if (copy_from_user(buf, user_buf, sizeof(buf)))
+               return -EFAULT;
+
+       if (sscanf(buf, "%d", &allow) != 1)
+               return -EINVAL;
+
+       IWL_DEBUG_POWER(mvm, "%s device power down\n",
+                       allow ? "allow" : "prevent");
+
+       /*
+        * TODO: Send REPLY_DEBUG_CMD (0xf0) when FW support it
+        */
+
+       return count;
+}
+
+static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file,
+                                                  const char __user *user_buf,
+                                                  size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char buf[8] = {};
+       int allow;
+
+       if (copy_from_user(buf, user_buf, sizeof(buf)))
+               return -EFAULT;
+
+       if (sscanf(buf, "%d", &allow) != 1)
+               return -EINVAL;
+
+       IWL_DEBUG_POWER(mvm, "%s device power down in d3\n",
+                       allow ? "allow" : "prevent");
+
+       /*
+        * TODO: When WoWLAN FW alive notification happens, driver will send
+        * REPLY_DEBUG_CMD setting power_down_allow flag according to
+        * mvm->prevent_power_down_d3
+        */
+       mvm->prevent_power_down_d3 = !allow;
+
+       return count;
+}
+
+#define MVM_DEBUGFS_READ_FILE_OPS(name)                                        \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+       .read = iwl_dbgfs_##name##_read,                                \
+       .open = iwl_dbgfs_open_file_generic,                            \
+       .llseek = generic_file_llseek,                                  \
+}
+
+#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name)                          \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+       .write = iwl_dbgfs_##name##_write,                              \
+       .read = iwl_dbgfs_##name##_read,                                \
+       .open = iwl_dbgfs_open_file_generic,                            \
+       .llseek = generic_file_llseek,                                  \
+};
+
+#define MVM_DEBUGFS_WRITE_FILE_OPS(name)                               \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+       .write = iwl_dbgfs_##name##_write,                              \
+       .open = iwl_dbgfs_open_file_generic,                            \
+       .llseek = generic_file_llseek,                                  \
+};
+
+#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) do {                  \
+               if (!debugfs_create_file(#name, mode, parent, mvm,      \
+                                        &iwl_dbgfs_##name##_ops))      \
+                       goto err;                                       \
+       } while (0)
+
+#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do {              \
+               if (!debugfs_create_file(#name, mode, parent, vif,      \
+                                        &iwl_dbgfs_##name##_ops))      \
+                       goto err;                                       \
+       } while (0)
+
+/* Device wide debugfs entries */
+MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush);
+MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram);
+MVM_DEBUGFS_READ_FILE_OPS(stations);
+MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
+MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
+
+int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
+{
+       char buf[100];
+
+       mvm->debugfs_dir = dbgfs_dir;
+
+       MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
+       MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
+       MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
+
+       /*
+        * Create a symlink with mac80211. It will be removed when mac80211
+        * exists (before the opmode exists which removes the target.)
+        */
+       snprintf(buf, 100, "../../%s/%s",
+                dbgfs_dir->d_parent->d_parent->d_name.name,
+                dbgfs_dir->d_parent->d_name.name);
+       if (!debugfs_create_symlink("iwlwifi", mvm->hw->wiphy->debugfsdir, buf))
+               goto err;
+
+       return 0;
+err:
+       IWL_ERR(mvm, "Can't create the mvm debugfs directory\n");
+       return -ENOMEM;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
new file mode 100644 (file)
index 0000000..cf6f9a0
--- /dev/null
@@ -0,0 +1,282 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_d3_h__
+#define __fw_api_d3_h__
+
+/**
+ * enum iwl_d3_wakeup_flags - D3 manager wakeup flags
+ * @IWL_WAKEUP_D3_CONFIG_FW_ERROR: wake up on firmware sysassert
+ */
+enum iwl_d3_wakeup_flags {
+       IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0),
+}; /* D3_MANAGER_WAKEUP_CONFIG_API_E_VER_3 */
+
+/**
+ * struct iwl_d3_manager_config - D3 manager configuration command
+ * @min_sleep_time: minimum sleep time (in usec)
+ * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags
+ *
+ * The structure is used for the D3_CONFIG_CMD command.
+ */
+struct iwl_d3_manager_config {
+       __le32 min_sleep_time;
+       __le32 wakeup_flags;
+} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_3 */
+
+
+/* TODO: OFFLOADS_QUERY_API_S_VER_1 */
+
+/**
+ * enum iwl_d3_proto_offloads - enabled protocol offloads
+ * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled
+ * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled
+ */
+enum iwl_proto_offloads {
+       IWL_D3_PROTO_OFFLOAD_ARP = BIT(0),
+       IWL_D3_PROTO_OFFLOAD_NS = BIT(1),
+};
+
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS       2
+
+/**
+ * struct iwl_proto_offload_cmd - ARP/NS offload configuration
+ * @enabled: enable flags
+ * @remote_ipv4_addr: remote address to answer to (or zero if all)
+ * @host_ipv4_addr: our IPv4 address to respond to queries for
+ * @arp_mac_addr: our MAC address for ARP responses
+ * @remote_ipv6_addr: remote address to answer to (or zero if all)
+ * @solicited_node_ipv6_addr: broken -- solicited node address exists
+ *     for each target address
+ * @target_ipv6_addr: our target addresses
+ * @ndp_mac_addr: neighbor soliciation response MAC address
+ */
+struct iwl_proto_offload_cmd {
+       __le32 enabled;
+       __be32 remote_ipv4_addr;
+       __be32 host_ipv4_addr;
+       u8 arp_mac_addr[ETH_ALEN];
+       __le16 reserved1;
+
+       u8 remote_ipv6_addr[16];
+       u8 solicited_node_ipv6_addr[16];
+       u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS][16];
+       u8 ndp_mac_addr[ETH_ALEN];
+       __le16 reserved2;
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */
+
+
+/*
+ * WOWLAN_PATTERNS
+ */
+#define IWL_WOWLAN_MIN_PATTERN_LEN     16
+#define IWL_WOWLAN_MAX_PATTERN_LEN     128
+
+struct iwl_wowlan_pattern {
+       u8 mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
+       u8 pattern[IWL_WOWLAN_MAX_PATTERN_LEN];
+       u8 mask_size;
+       u8 pattern_size;
+       __le16 reserved;
+} __packed; /* WOWLAN_PATTERN_API_S_VER_1 */
+
+#define IWL_WOWLAN_MAX_PATTERNS        20
+
+struct iwl_wowlan_patterns_cmd {
+       __le32 n_patterns;
+       struct iwl_wowlan_pattern patterns[];
+} __packed; /* WOWLAN_PATTERN_ARRAY_API_S_VER_1 */
+
+enum iwl_wowlan_wakeup_filters {
+       IWL_WOWLAN_WAKEUP_MAGIC_PACKET                  = BIT(0),
+       IWL_WOWLAN_WAKEUP_PATTERN_MATCH                 = BIT(1),
+       IWL_WOWLAN_WAKEUP_BEACON_MISS                   = BIT(2),
+       IWL_WOWLAN_WAKEUP_LINK_CHANGE                   = BIT(3),
+       IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL                = BIT(4),
+       IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ                 = BIT(5),
+       IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE                = BIT(6),
+       IWL_WOWLAN_WAKEUP_ENABLE_NET_DETECT             = BIT(7),
+       IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT              = BIT(8),
+       IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS              = BIT(9),
+       IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE        = BIT(10),
+       /* BIT(11) reserved */
+       IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET          = BIT(12),
+}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
+
+struct iwl_wowlan_config_cmd {
+       __le32 wakeup_filter;
+       __le16 non_qos_seq;
+       __le16 qos_seq[8];
+       u8 wowlan_ba_teardown_tids;
+       u8 is_11n_connection;
+} __packed; /* WOWLAN_CONFIG_API_S_VER_2 */
+
+/*
+ * WOWLAN_TSC_RSC_PARAMS
+ */
+#define IWL_NUM_RSC    16
+
+struct tkip_sc {
+       __le16 iv16;
+       __le16 pad;
+       __le32 iv32;
+} __packed; /* TKIP_SC_API_U_VER_1 */
+
+struct iwl_tkip_rsc_tsc {
+       struct tkip_sc unicast_rsc[IWL_NUM_RSC];
+       struct tkip_sc multicast_rsc[IWL_NUM_RSC];
+       struct tkip_sc tsc;
+} __packed; /* TKIP_TSC_RSC_API_S_VER_1 */
+
+struct aes_sc {
+       __le64 pn;
+} __packed; /* TKIP_AES_SC_API_U_VER_1 */
+
+struct iwl_aes_rsc_tsc {
+       struct aes_sc unicast_rsc[IWL_NUM_RSC];
+       struct aes_sc multicast_rsc[IWL_NUM_RSC];
+       struct aes_sc tsc;
+} __packed; /* AES_TSC_RSC_API_S_VER_1 */
+
+union iwl_all_tsc_rsc {
+       struct iwl_tkip_rsc_tsc tkip;
+       struct iwl_aes_rsc_tsc aes;
+}; /* ALL_TSC_RSC_API_S_VER_2 */
+
+struct iwl_wowlan_rsc_tsc_params_cmd {
+       union iwl_all_tsc_rsc all_tsc_rsc;
+} __packed; /* ALL_TSC_RSC_API_S_VER_2 */
+
+#define IWL_MIC_KEY_SIZE       8
+struct iwl_mic_keys {
+       u8 tx[IWL_MIC_KEY_SIZE];
+       u8 rx_unicast[IWL_MIC_KEY_SIZE];
+       u8 rx_mcast[IWL_MIC_KEY_SIZE];
+} __packed; /* MIC_KEYS_API_S_VER_1 */
+
+#define IWL_P1K_SIZE           5
+struct iwl_p1k_cache {
+       __le16 p1k[IWL_P1K_SIZE];
+} __packed;
+
+#define IWL_NUM_RX_P1K_CACHE   2
+
+struct iwl_wowlan_tkip_params_cmd {
+       struct iwl_mic_keys mic_keys;
+       struct iwl_p1k_cache tx;
+       struct iwl_p1k_cache rx_uni[IWL_NUM_RX_P1K_CACHE];
+       struct iwl_p1k_cache rx_multi[IWL_NUM_RX_P1K_CACHE];
+} __packed; /* WOWLAN_TKIP_SETTING_API_S_VER_1 */
+
+#define IWL_KCK_MAX_SIZE       32
+#define IWL_KEK_MAX_SIZE       32
+
+struct iwl_wowlan_kek_kck_material_cmd {
+       u8      kck[IWL_KCK_MAX_SIZE];
+       u8      kek[IWL_KEK_MAX_SIZE];
+       __le16  kck_len;
+       __le16  kek_len;
+       __le64  replay_ctr;
+} __packed; /* KEK_KCK_MATERIAL_API_S_VER_2 */
+
+#define RF_KILL_INDICATOR_FOR_WOWLAN   0x87
+
+enum iwl_wowlan_rekey_status {
+       IWL_WOWLAN_REKEY_POST_REKEY = 0,
+       IWL_WOWLAN_REKEY_WHILE_REKEY = 1,
+}; /* WOWLAN_REKEY_STATUS_API_E_VER_1 */
+
+enum iwl_wowlan_wakeup_reason {
+       IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS                       = 0,
+       IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET                       = BIT(0),
+       IWL_WOWLAN_WAKEUP_BY_PATTERN                            = BIT(1),
+       IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON     = BIT(2),
+       IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH            = BIT(3),
+       IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE                  = BIT(4),
+       IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED                  = BIT(5),
+       IWL_WOWLAN_WAKEUP_BY_UCODE_ERROR                        = BIT(6),
+       IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST                      = BIT(7),
+       IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE                 = BIT(8),
+       IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS                 = BIT(9),
+       IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE           = BIT(10),
+       IWL_WOWLAN_WAKEUP_BY_REM_WAKE_TCP_EXTERNAL              = BIT(11),
+       IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET             = BIT(12),
+}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
+
+struct iwl_wowlan_status {
+       __le64 replay_ctr;
+       __le16 pattern_number;
+       __le16 non_qos_seq_ctr;
+       __le16 qos_seq_ctr[8];
+       __le32 wakeup_reasons;
+       __le32 rekey_status;
+       __le32 num_of_gtk_rekeys;
+       __le32 transmitted_ndps;
+       __le32 received_beacons;
+       __le32 wake_packet_length;
+       __le32 wake_packet_bufsize;
+       u8 wake_packet[]; /* can be truncated from _length to _bufsize */
+} __packed; /* WOWLAN_STATUSES_API_S_VER_4 */
+
+/* TODO: NetDetect API */
+
+#endif /* __fw_api_d3_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
new file mode 100644 (file)
index 0000000..ae39b7d
--- /dev/null
@@ -0,0 +1,369 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_mac_h__
+#define __fw_api_mac_h__
+
+/*
+ * The first MAC indices (starting from 0)
+ * are available to the driver, AUX follows
+ */
+#define MAC_INDEX_AUX          4
+#define MAC_INDEX_MIN_DRIVER   0
+#define NUM_MAC_INDEX_DRIVER   MAC_INDEX_AUX
+
+#define AC_NUM 4 /* Number of access categories */
+
+/**
+ * enum iwl_mac_protection_flags - MAC context flags
+ * @MAC_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames,
+ *     this will require CCK RTS/CTS2self.
+ *     RTS/CTS will protect full burst time.
+ * @MAC_PROT_FLG_HT_PROT: enable HT protection
+ * @MAC_PROT_FLG_FAT_PROT: protect 40 MHz transmissions
+ * @MAC_PROT_FLG_SELF_CTS_EN: allow CTS2self
+ */
+enum iwl_mac_protection_flags {
+       MAC_PROT_FLG_TGG_PROTECT        = BIT(3),
+       MAC_PROT_FLG_HT_PROT            = BIT(23),
+       MAC_PROT_FLG_FAT_PROT           = BIT(24),
+       MAC_PROT_FLG_SELF_CTS_EN        = BIT(30),
+};
+
+#define MAC_FLG_SHORT_SLOT             BIT(4)
+#define MAC_FLG_SHORT_PREAMBLE         BIT(5)
+
+/**
+ * enum iwl_mac_types - Supported MAC types
+ * @FW_MAC_TYPE_FIRST: lowest supported MAC type
+ * @FW_MAC_TYPE_AUX: Auxiliary MAC (internal)
+ * @FW_MAC_TYPE_LISTENER: monitor MAC type (?)
+ * @FW_MAC_TYPE_PIBSS: Pseudo-IBSS
+ * @FW_MAC_TYPE_IBSS: IBSS
+ * @FW_MAC_TYPE_BSS_STA: BSS (managed) station
+ * @FW_MAC_TYPE_P2P_DEVICE: P2P Device
+ * @FW_MAC_TYPE_P2P_STA: P2P client
+ * @FW_MAC_TYPE_GO: P2P GO
+ * @FW_MAC_TYPE_TEST: ?
+ * @FW_MAC_TYPE_MAX: highest support MAC type
+ */
+enum iwl_mac_types {
+       FW_MAC_TYPE_FIRST = 1,
+       FW_MAC_TYPE_AUX = FW_MAC_TYPE_FIRST,
+       FW_MAC_TYPE_LISTENER,
+       FW_MAC_TYPE_PIBSS,
+       FW_MAC_TYPE_IBSS,
+       FW_MAC_TYPE_BSS_STA,
+       FW_MAC_TYPE_P2P_DEVICE,
+       FW_MAC_TYPE_P2P_STA,
+       FW_MAC_TYPE_GO,
+       FW_MAC_TYPE_TEST,
+       FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST
+}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */
+
+/**
+ * enum iwl_tsf_id - TSF hw timer ID
+ * @TSF_ID_A: use TSF A
+ * @TSF_ID_B: use TSF B
+ * @TSF_ID_C: use TSF C
+ * @TSF_ID_D: use TSF D
+ * @NUM_TSF_IDS: number of TSF timers available
+ */
+enum iwl_tsf_id {
+       TSF_ID_A = 0,
+       TSF_ID_B = 1,
+       TSF_ID_C = 2,
+       TSF_ID_D = 3,
+       NUM_TSF_IDS = 4,
+}; /* TSF_ID_API_E_VER_1 */
+
+/**
+ * struct iwl_mac_data_ap - configuration data for AP MAC context
+ * @beacon_time: beacon transmit time in system time
+ * @beacon_tsf: beacon transmit time in TSF
+ * @bi: beacon interval in TU
+ * @bi_reciprocal: 2^32 / bi
+ * @dtim_interval: dtim transmit time in TU
+ * @dtim_reciprocal: 2^32 / dtim_interval
+ * @mcast_qid: queue ID for multicast traffic
+ * @beacon_template: beacon template ID
+ */
+struct iwl_mac_data_ap {
+       __le32 beacon_time;
+       __le64 beacon_tsf;
+       __le32 bi;
+       __le32 bi_reciprocal;
+       __le32 dtim_interval;
+       __le32 dtim_reciprocal;
+       __le32 mcast_qid;
+       __le32 beacon_template;
+} __packed; /* AP_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_ibss - configuration data for IBSS MAC context
+ * @beacon_time: beacon transmit time in system time
+ * @beacon_tsf: beacon transmit time in TSF
+ * @bi: beacon interval in TU
+ * @bi_reciprocal: 2^32 / bi
+ */
+struct iwl_mac_data_ibss {
+       __le32 beacon_time;
+       __le64 beacon_tsf;
+       __le32 bi;
+       __le32 bi_reciprocal;
+} __packed; /* IBSS_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_sta - configuration data for station MAC context
+ * @is_assoc: 1 for associated state, 0 otherwise
+ * @dtim_time: DTIM arrival time in system time
+ * @dtim_tsf: DTIM arrival time in TSF
+ * @bi: beacon interval in TU, applicable only when associated
+ * @bi_reciprocal: 2^32 / bi , applicable only when associated
+ * @dtim_interval: DTIM interval in TU, applicable only when associated
+ * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated
+ * @listen_interval: in beacon intervals, applicable only when associated
+ * @assoc_id: unique ID assigned by the AP during association
+ */
+struct iwl_mac_data_sta {
+       __le32 is_assoc;
+       __le32 dtim_time;
+       __le64 dtim_tsf;
+       __le32 bi;
+       __le32 bi_reciprocal;
+       __le32 dtim_interval;
+       __le32 dtim_reciprocal;
+       __le32 listen_interval;
+       __le32 assoc_id;
+       __le32 assoc_beacon_arrive_time;
+} __packed; /* STA_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_go - configuration data for P2P GO MAC context
+ * @ap: iwl_mac_data_ap struct with most config data
+ * @ctwin: client traffic window in TU (period after TBTT when GO is present).
+ *     0 indicates that there is no CT window.
+ * @opp_ps_enabled: indicate that opportunistic PS allowed
+ */
+struct iwl_mac_data_go {
+       struct iwl_mac_data_ap ap;
+       __le32 ctwin;
+       __le32 opp_ps_enabled;
+} __packed; /* GO_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_p2p_sta - configuration data for P2P client MAC context
+ * @sta: iwl_mac_data_sta struct with most config data
+ * @ctwin: client traffic window in TU (period after TBTT when GO is present).
+ *     0 indicates that there is no CT window.
+ */
+struct iwl_mac_data_p2p_sta {
+       struct iwl_mac_data_sta sta;
+       __le32 ctwin;
+} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_pibss - Pseudo IBSS config data
+ * @stats_interval: interval in TU between statistics notifications to host.
+ */
+struct iwl_mac_data_pibss {
+       __le32 stats_interval;
+} __packed; /* PIBSS_MAC_DATA_API_S_VER_1 */
+
+/*
+ * struct iwl_mac_data_p2p_dev - configuration data for the P2P Device MAC
+ * context.
+ * @is_disc_extended: if set to true, P2P Device discoverability is enabled on
+ *     other channels as well. This should be to true only in case that the
+ *     device is discoverable and there is an active GO. Note that setting this
+ *     field when not needed, will increase the number of interrupts and have
+ *     effect on the platform power, as this setting opens the Rx filters on
+ *     all macs.
+ */
+struct iwl_mac_data_p2p_dev {
+       __le32 is_disc_extended;
+} __packed; /* _P2P_DEV_MAC_DATA_API_S_VER_1 */
+
+/**
+ * enum iwl_mac_filter_flags - MAC context filter flags
+ * @MAC_FILTER_IN_PROMISC: accept all data frames
+ * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all mangement and
+ *     control frames to the host
+ * @MAC_FILTER_ACCEPT_GRP: accept multicast frames
+ * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames
+ * @MAC_FILTER_DIS_GRP_DECRYPT: don't decrypt multicast frames
+ * @MAC_FILTER_IN_BEACON: transfer foreign BSS's beacons to host
+ *     (in station mode when associated)
+ * @MAC_FILTER_OUT_BCAST: filter out all broadcast frames
+ * @MAC_FILTER_IN_CRC32: extract FCS and append it to frames
+ * @MAC_FILTER_IN_PROBE_REQUEST: pass probe requests to host
+ */
+enum iwl_mac_filter_flags {
+       MAC_FILTER_IN_PROMISC           = BIT(0),
+       MAC_FILTER_IN_CONTROL_AND_MGMT  = BIT(1),
+       MAC_FILTER_ACCEPT_GRP           = BIT(2),
+       MAC_FILTER_DIS_DECRYPT          = BIT(3),
+       MAC_FILTER_DIS_GRP_DECRYPT      = BIT(4),
+       MAC_FILTER_IN_BEACON            = BIT(6),
+       MAC_FILTER_OUT_BCAST            = BIT(8),
+       MAC_FILTER_IN_CRC32             = BIT(11),
+       MAC_FILTER_IN_PROBE_REQUEST     = BIT(12),
+};
+
+/**
+ * enum iwl_mac_qos_flags - QoS flags
+ * @MAC_QOS_FLG_UPDATE_EDCA: ?
+ * @MAC_QOS_FLG_TGN: HT is enabled
+ * @MAC_QOS_FLG_TXOP_TYPE: ?
+ *
+ */
+enum iwl_mac_qos_flags {
+       MAC_QOS_FLG_UPDATE_EDCA = BIT(0),
+       MAC_QOS_FLG_TGN         = BIT(1),
+       MAC_QOS_FLG_TXOP_TYPE   = BIT(4),
+};
+
+/**
+ * struct iwl_ac_qos - QOS timing params for MAC_CONTEXT_CMD
+ * @cw_min: Contention window, start value in numbers of slots.
+ *     Should be a power-of-2, minus 1.  Device's default is 0x0f.
+ * @cw_max: Contention window, max value in numbers of slots.
+ *     Should be a power-of-2, minus 1.  Device's default is 0x3f.
+ * @aifsn:  Number of slots in Arbitration Interframe Space (before
+ *     performing random backoff timing prior to Tx).  Device default 1.
+ * @fifos_mask: FIFOs used by this MAC for this AC
+ * @edca_txop:  Length of Tx opportunity, in uSecs.  Device default is 0.
+ *
+ * One instance of this config struct for each of 4 EDCA access categories
+ * in struct iwl_qosparam_cmd.
+ *
+ * Device will automatically increase contention window by (2*CW) + 1 for each
+ * transmission retry.  Device uses cw_max as a bit mask, ANDed with new CW
+ * value, to cap the CW value.
+ */
+struct iwl_ac_qos {
+       __le16 cw_min;
+       __le16 cw_max;
+       u8 aifsn;
+       u8 fifos_mask;
+       __le16 edca_txop;
+} __packed; /* AC_QOS_API_S_VER_2 */
+
+/**
+ * struct iwl_mac_ctx_cmd - command structure to configure MAC contexts
+ * ( MAC_CONTEXT_CMD = 0x28 )
+ * @id_and_color: ID and color of the MAC
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @mac_type: one of FW_MAC_TYPE_*
+ * @tsd_id: TSF HW timer, one of TSF_ID_*
+ * @node_addr: MAC address
+ * @bssid_addr: BSSID
+ * @cck_rates: basic rates available for CCK
+ * @ofdm_rates: basic rates available for OFDM
+ * @protection_flags: combination of MAC_PROT_FLG_FLAG_*
+ * @cck_short_preamble: 0x20 for enabling short preamble, 0 otherwise
+ * @short_slot: 0x10 for enabling short slots, 0 otherwise
+ * @filter_flags: combination of MAC_FILTER_*
+ * @qos_flags: from MAC_QOS_FLG_*
+ * @ac: one iwl_mac_qos configuration for each AC
+ * @mac_specific: one of struct iwl_mac_data_*, according to mac_type
+ */
+struct iwl_mac_ctx_cmd {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       /* MAC_CONTEXT_COMMON_DATA_API_S_VER_1 */
+       __le32 mac_type;
+       __le32 tsf_id;
+       u8 node_addr[6];
+       __le16 reserved_for_node_addr;
+       u8 bssid_addr[6];
+       __le16 reserved_for_bssid_addr;
+       __le32 cck_rates;
+       __le32 ofdm_rates;
+       __le32 protection_flags;
+       __le32 cck_short_preamble;
+       __le32 short_slot;
+       __le32 filter_flags;
+       /* MAC_QOS_PARAM_API_S_VER_1 */
+       __le32 qos_flags;
+       struct iwl_ac_qos ac[AC_NUM+1];
+       /* MAC_CONTEXT_COMMON_DATA_API_S */
+       union {
+               struct iwl_mac_data_ap ap;
+               struct iwl_mac_data_go go;
+               struct iwl_mac_data_sta sta;
+               struct iwl_mac_data_p2p_sta p2p_sta;
+               struct iwl_mac_data_p2p_dev p2p_dev;
+               struct iwl_mac_data_pibss pibss;
+               struct iwl_mac_data_ibss ibss;
+       };
+} __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */
+
+static inline u32 iwl_mvm_reciprocal(u32 v)
+{
+       if (!v)
+               return 0;
+       return 0xFFFFFFFF / v;
+}
+
+#endif /* __fw_api_mac_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
new file mode 100644 (file)
index 0000000..be36b76
--- /dev/null
@@ -0,0 +1,140 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_power_h__
+#define __fw_api_power_h__
+
+/* Power Management Commands, Responses, Notifications */
+
+/**
+ * enum iwl_scan_flags - masks for power table command flags
+ * @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management,
+ *             '1' Driver enables PM (use rest of parameters)
+ * @POWER_FLAGS_SLEEP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM,
+ *             '1' PM could sleep over DTIM till listen Interval.
+ * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
+ * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
+ *             access categories are both delivery and trigger enabled.
+ * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
+ *             PBW Snoozing enabled
+ * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
+*/
+enum iwl_power_flags {
+       POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK    = BIT(0),
+       POWER_FLAGS_SLEEP_OVER_DTIM_MSK         = BIT(1),
+       POWER_FLAGS_LPRX_ENA_MSK                = BIT(2),
+       POWER_FLAGS_SNOOZE_ENA_MSK              = BIT(3),
+       POWER_FLAGS_BT_SCO_ENA                  = BIT(4),
+       POWER_FLAGS_ADVANCE_PM_ENA_MSK          = BIT(5)
+};
+
+/**
+ * struct iwl_powertable_cmd - Power Table Command
+ * POWER_TABLE_CMD = 0x77 (command, has simple generic response)
+ *
+ * @id_and_color:      MAC contex identifier
+ * @action:            Action on context - no action, add new,
+ *                     modify existent, remove
+ * @flags:             Power table command flags from POWER_FLAGS_*
+ * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
+ *                     Minimum allowed:- 3 * DTIM
+ * @rx_data_timeout:    Minimum time (usec) from last Rx packet for AM to
+ *                     PSM transition - legacy PM
+ * @tx_data_timeout:    Minimum time (usec) from last Tx packet for AM to
+ *                     PSM transition - legacy PM
+ * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to
+ *                     PSM transition - uAPSD
+ * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to
+ *                     PSM transition - uAPSD
+ * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
+ *                     Default: 80dbm
+ * @num_skip_dtim:      Number of DTIMs to skip if Skip over DTIM flag is set
+ * @snooze_interval:    TBD
+ * @snooze_window:      TBD
+ * @snooze_step:        TBD
+ * @qndp_tid:           TBD
+ * @uapsd_ac_flags:     TBD
+ * @uapsd_max_sp:       TBD
+ */
+struct iwl_powertable_cmd {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       __le16 flags;
+       u8 reserved;
+       __le16 keep_alive_seconds;
+       __le32 rx_data_timeout;
+       __le32 tx_data_timeout;
+       __le32 rx_data_timeout_uapsd;
+       __le32 tx_data_timeout_uapsd;
+       u8 lprx_rssi_threshold;
+       u8 num_skip_dtim;
+       __le16 snooze_interval;
+       __le16 snooze_window;
+       u8 snooze_step;
+       u8 qndp_tid;
+       u8 uapsd_ac_flags;
+       u8 uapsd_max_sp;
+} __packed;
+
+#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
new file mode 100644 (file)
index 0000000..aa3474d
--- /dev/null
@@ -0,0 +1,312 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_rs_h__
+#define __fw_api_rs_h__
+
+#include "fw-api-mac.h"
+
+/*
+ * These serve as indexes into
+ * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT];
+ */
+enum {
+       IWL_RATE_1M_INDEX = 0,
+       IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX,
+       IWL_RATE_2M_INDEX,
+       IWL_RATE_5M_INDEX,
+       IWL_RATE_11M_INDEX,
+       IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
+       IWL_RATE_6M_INDEX,
+       IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
+       IWL_RATE_9M_INDEX,
+       IWL_RATE_12M_INDEX,
+       IWL_RATE_18M_INDEX,
+       IWL_RATE_24M_INDEX,
+       IWL_RATE_36M_INDEX,
+       IWL_RATE_48M_INDEX,
+       IWL_RATE_54M_INDEX,
+       IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX,
+       IWL_RATE_60M_INDEX,
+       IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX,
+       IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1,
+       IWL_RATE_COUNT,
+};
+
+#define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX)
+
+/* fw API values for legacy bit rates, both OFDM and CCK */
+enum {
+       IWL_RATE_6M_PLCP  = 13,
+       IWL_RATE_9M_PLCP  = 15,
+       IWL_RATE_12M_PLCP = 5,
+       IWL_RATE_18M_PLCP = 7,
+       IWL_RATE_24M_PLCP = 9,
+       IWL_RATE_36M_PLCP = 11,
+       IWL_RATE_48M_PLCP = 1,
+       IWL_RATE_54M_PLCP = 3,
+       IWL_RATE_1M_PLCP  = 10,
+       IWL_RATE_2M_PLCP  = 20,
+       IWL_RATE_5M_PLCP  = 55,
+       IWL_RATE_11M_PLCP = 110,
+};
+
+/*
+ * rate_n_flags bit fields
+ *
+ * The 32-bit value has different layouts in the low 8 bites depending on the
+ * format. There are three formats, HT, VHT and legacy (11abg, with subformats
+ * for CCK and OFDM).
+ *
+ * High-throughput (HT) rate format
+ *     bit 8 is 1, bit 26 is 0, bit 9 is 0 (OFDM)
+ * Very High-throughput (VHT) rate format
+ *     bit 8 is 0, bit 26 is 1, bit 9 is 0 (OFDM)
+ * Legacy OFDM rate format for bits 7:0
+ *     bit 8 is 0, bit 26 is 0, bit 9 is 0 (OFDM)
+ * Legacy CCK rate format for bits 7:0:
+ *     bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK)
+ */
+
+/* Bit 8: (1) HT format, (0) legacy or VHT format */
+#define RATE_MCS_HT_POS 8
+#define RATE_MCS_HT_MSK (1 << RATE_MCS_HT_POS)
+
+/* Bit 9: (1) CCK, (0) OFDM.  HT (bit 8) must be "0" for this bit to be valid */
+#define RATE_MCS_CCK_POS 9
+#define RATE_MCS_CCK_MSK (1 << RATE_MCS_CCK_POS)
+
+/* Bit 26: (1) VHT format, (0) legacy format in bits 8:0 */
+#define RATE_MCS_VHT_POS 26
+#define RATE_MCS_VHT_MSK (1 << RATE_MCS_VHT_POS)
+
+
+/*
+ * High-throughput (HT) rate format for bits 7:0
+ *
+ *  2-0:  MCS rate base
+ *        0)   6 Mbps
+ *        1)  12 Mbps
+ *        2)  18 Mbps
+ *        3)  24 Mbps
+ *        4)  36 Mbps
+ *        5)  48 Mbps
+ *        6)  54 Mbps
+ *        7)  60 Mbps
+ *  4-3:  0)  Single stream (SISO)
+ *        1)  Dual stream (MIMO)
+ *        2)  Triple stream (MIMO)
+ *    5:  Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data
+ *  (bits 7-6 are zero)
+ *
+ * Together the low 5 bits work out to the MCS index because we don't
+ * support MCSes above 15/23, and 0-7 have one stream, 8-15 have two
+ * streams and 16-23 have three streams. We could also support MCS 32
+ * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.)
+ */
+#define RATE_HT_MCS_RATE_CODE_MSK      0x7
+
+/* Bit 10: (1) Use Green Field preamble */
+#define RATE_HT_MCS_GF_POS             10
+#define RATE_HT_MCS_GF_MSK             (1 << RATE_HT_MCS_GF_POS)
+
+#define RATE_HT_MCS_INDEX_MSK          0x3f
+
+/*
+ * Very High-throughput (VHT) rate format for bits 7:0
+ *
+ *  3-0:  VHT MCS (0-9)
+ *  5-4:  number of streams - 1:
+ *        0)  Single stream (SISO)
+ *        1)  Dual stream (MIMO)
+ *        2)  Triple stream (MIMO)
+ */
+
+/* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */
+#define RATE_VHT_MCS_RATE_CODE_MSK     0xf
+#define RATE_VHT_MCS_NSS_POS           4
+#define RATE_VHT_MCS_NSS_MSK           (3 << RATE_VHT_MCS_NSS_POS)
+
+/*
+ * Legacy OFDM rate format for bits 7:0
+ *
+ *  3-0:  0xD)   6 Mbps
+ *        0xF)   9 Mbps
+ *        0x5)  12 Mbps
+ *        0x7)  18 Mbps
+ *        0x9)  24 Mbps
+ *        0xB)  36 Mbps
+ *        0x1)  48 Mbps
+ *        0x3)  54 Mbps
+ * (bits 7-4 are 0)
+ *
+ * Legacy CCK rate format for bits 7:0:
+ * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK):
+ *
+ *  6-0:   10)  1 Mbps
+ *         20)  2 Mbps
+ *         55)  5.5 Mbps
+ *        110)  11 Mbps
+ * (bit 7 is 0)
+ */
+#define RATE_LEGACY_RATE_MSK 0xff
+
+
+/*
+ * Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz
+ * 0 and 1 are valid for HT and VHT, 2 and 3 only for VHT
+ */
+#define RATE_MCS_CHAN_WIDTH_POS                11
+#define RATE_MCS_CHAN_WIDTH_MSK                (3 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_20         (0 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_40         (1 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_80         (2 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_160                (3 << RATE_MCS_CHAN_WIDTH_POS)
+
+/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */
+#define RATE_MCS_SGI_POS               13
+#define RATE_MCS_SGI_MSK               (1 << RATE_MCS_SGI_POS)
+
+/* Bit 14-16: Antenna selection (1) Ant A, (2) Ant B, (4) Ant C */
+#define RATE_MCS_ANT_POS               14
+#define RATE_MCS_ANT_A_MSK             (1 << RATE_MCS_ANT_POS)
+#define RATE_MCS_ANT_B_MSK             (2 << RATE_MCS_ANT_POS)
+#define RATE_MCS_ANT_C_MSK             (4 << RATE_MCS_ANT_POS)
+#define RATE_MCS_ANT_AB_MSK            (RATE_MCS_ANT_A_MSK | \
+                                        RATE_MCS_ANT_B_MSK)
+#define RATE_MCS_ANT_ABC_MSK           (RATE_MCS_ANT_AB_MSK | \
+                                        RATE_MCS_ANT_C_MSK)
+#define RATE_MCS_ANT_MSK               RATE_MCS_ANT_ABC_MSK
+#define RATE_MCS_ANT_NUM 3
+
+/* Bit 17-18: (0) SS, (1) SS*2 */
+#define RATE_MCS_STBC_POS              17
+#define RATE_MCS_STBC_MSK              (1 << RATE_MCS_STBC_POS)
+
+/* Bit 19: (0) Beamforming is off, (1) Beamforming is on */
+#define RATE_MCS_BF_POS                        19
+#define RATE_MCS_BF_MSK                        (1 << RATE_MCS_BF_POS)
+
+/* Bit 20: (0) ZLF is off, (1) ZLF is on */
+#define RATE_MCS_ZLF_POS               20
+#define RATE_MCS_ZLF_MSK               (1 << RATE_MCS_ZLF_POS)
+
+/* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */
+#define RATE_MCS_DUP_POS               24
+#define RATE_MCS_DUP_MSK               (3 << RATE_MCS_DUP_POS)
+
+/* Bit 27: (1) LDPC enabled, (0) LDPC disabled */
+#define RATE_MCS_LDPC_POS              27
+#define RATE_MCS_LDPC_MSK              (1 << RATE_MCS_LDPC_POS)
+
+
+/* Link Quality definitions */
+
+/* # entries in rate scale table to support Tx retries */
+#define  LQ_MAX_RETRY_NUM 16
+
+/* Link quality command flags, only this one is available */
+#define  LQ_FLAG_SET_STA_TLC_RTS_MSK   BIT(0)
+
+/**
+ * struct iwl_lq_cmd - link quality command
+ * @sta_id: station to update
+ * @control: not used
+ * @flags: combination of LQ_FLAG_*
+ * @mimo_delim: the first SISO index in rs_table, which separates MIMO
+ *     and SISO rates
+ * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD).
+ *     Should be ANT_[ABC]
+ * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC]
+ * @initial_rate_index: first index from rs_table per AC category
+ * @agg_time_limit: aggregation max time threshold in usec/100, meaning
+ *     value of 100 is one usec. Range is 100 to 8000
+ * @agg_disable_start_th: try-count threshold for starting aggregation.
+ *     If a frame has higher try-count, it should not be selected for
+ *     starting an aggregation sequence.
+ * @agg_frame_cnt_limit: max frame count in an aggregation.
+ *     0: no limit
+ *     1: no aggregation (one frame per aggregation)
+ *     2 - 0x3f: maximal number of frames (up to 3f == 63)
+ * @rs_table: array of rates for each TX try, each is rate_n_flags,
+ *     meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP
+ * @bf_params: beam forming params, currently not used
+ */
+struct iwl_lq_cmd {
+       u8 sta_id;
+       u8 reserved1;
+       u16 control;
+       /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */
+       u8 flags;
+       u8 mimo_delim;
+       u8 single_stream_ant_msk;
+       u8 dual_stream_ant_msk;
+       u8 initial_rate_index[AC_NUM];
+       /* LINK_QUAL_AGG_PARAMS_API_S_VER_1 */
+       __le16 agg_time_limit;
+       u8 agg_disable_start_th;
+       u8 agg_frame_cnt_limit;
+       __le32 reserved2;
+       __le32 rs_table[LQ_MAX_RETRY_NUM];
+       __le32 bf_params;
+}; /* LINK_QUALITY_CMD_API_S_VER_1 */
+#endif /* __fw_api_rs_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
new file mode 100644 (file)
index 0000000..670ac8f
--- /dev/null
@@ -0,0 +1,561 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_scan_h__
+#define __fw_api_scan_h__
+
+#include "fw-api.h"
+
+/* Scan Commands, Responses, Notifications */
+
+/* Masks for iwl_scan_channel.type flags */
+#define SCAN_CHANNEL_TYPE_PASSIVE      0
+#define SCAN_CHANNEL_TYPE_ACTIVE       BIT(0)
+#define SCAN_CHANNEL_NARROW_BAND       BIT(22)
+
+/* Max number of IEs for direct SSID scans in a command */
+#define PROBE_OPTION_MAX               20
+
+/**
+ * struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table
+ * @channel: band is selected by iwl_scan_cmd "flags" field
+ * @tx_gain: gain for analog radio
+ * @dsp_atten: gain for DSP
+ * @active_dwell: dwell time for active scan in TU, typically 5-50
+ * @passive_dwell: dwell time for passive scan in TU, typically 20-500
+ * @type: type is broken down to these bits:
+ *     bit 0: 0 = passive, 1 = active
+ *     bits 1-20: SSID direct bit map. If any of these bits is set then
+ *             the corresponding SSID IE is transmitted in probe request
+ *             (bit i adds IE in position i to the probe request)
+ *     bit 22: channel width, 0 = regular, 1 = TGj narrow channel
+ *
+ * @iteration_count:
+ * @iteration_interval:
+ * This struct is used once for each channel in the scan list.
+ * Each channel can independently select:
+ * 1)  SSID for directed active scans
+ * 2)  Txpower setting (for rate specified within Tx command)
+ * 3)  How long to stay on-channel (behavior may be modified by quiet_time,
+ *     quiet_plcp_th, good_CRC_th)
+ *
+ * To avoid uCode errors, make sure the following are true (see comments
+ * under struct iwl_scan_cmd about max_out_time and quiet_time):
+ * 1)  If using passive_dwell (i.e. passive_dwell != 0):
+ *     active_dwell <= passive_dwell (< max_out_time if max_out_time != 0)
+ * 2)  quiet_time <= active_dwell
+ * 3)  If restricting off-channel time (i.e. max_out_time !=0):
+ *     passive_dwell < max_out_time
+ *     active_dwell < max_out_time
+ */
+struct iwl_scan_channel {
+       __le32 type;
+       __le16 channel;
+       __le16 iteration_count;
+       __le32 iteration_interval;
+       __le16 active_dwell;
+       __le16 passive_dwell;
+} __packed; /* SCAN_CHANNEL_CONTROL_API_S_VER_1 */
+
+/**
+ * struct iwl_ssid_ie - directed scan network information element
+ *
+ * Up to 20 of these may appear in REPLY_SCAN_CMD,
+ * selected by "type" bit field in struct iwl_scan_channel;
+ * each channel may select different ssids from among the 20 entries.
+ * SSID IEs get transmitted in reverse order of entry.
+ */
+struct iwl_ssid_ie {
+       u8 id;
+       u8 len;
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+} __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */
+
+/**
+ * iwl_scan_flags - masks for scan command flags
+ *@SCAN_FLAGS_PERIODIC_SCAN:
+ *@SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX:
+ *@SCAN_FLAGS_DELAYED_SCAN_LOWBAND:
+ *@SCAN_FLAGS_DELAYED_SCAN_HIGHBAND:
+ *@SCAN_FLAGS_FRAGMENTED_SCAN:
+ */
+enum iwl_scan_flags {
+       SCAN_FLAGS_PERIODIC_SCAN                = BIT(0),
+       SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX   = BIT(1),
+       SCAN_FLAGS_DELAYED_SCAN_LOWBAND         = BIT(2),
+       SCAN_FLAGS_DELAYED_SCAN_HIGHBAND        = BIT(3),
+       SCAN_FLAGS_FRAGMENTED_SCAN              = BIT(4),
+};
+
+/**
+ * enum iwl_scan_type - Scan types for scan command
+ * @SCAN_TYPE_FORCED:
+ * @SCAN_TYPE_BACKGROUND:
+ * @SCAN_TYPE_OS:
+ * @SCAN_TYPE_ROAMING:
+ * @SCAN_TYPE_ACTION:
+ * @SCAN_TYPE_DISCOVERY:
+ * @SCAN_TYPE_DISCOVERY_FORCED:
+ */
+enum iwl_scan_type {
+       SCAN_TYPE_FORCED                = 0,
+       SCAN_TYPE_BACKGROUND            = 1,
+       SCAN_TYPE_OS                    = 2,
+       SCAN_TYPE_ROAMING               = 3,
+       SCAN_TYPE_ACTION                = 4,
+       SCAN_TYPE_DISCOVERY             = 5,
+       SCAN_TYPE_DISCOVERY_FORCED      = 6,
+}; /* SCAN_ACTIVITY_TYPE_E_VER_1 */
+
+/* Maximal number of channels to scan */
+#define MAX_NUM_SCAN_CHANNELS 0x24
+
+/**
+ * struct iwl_scan_cmd - scan request command
+ * ( SCAN_REQUEST_CMD = 0x80 )
+ * @len: command length in bytes
+ * @scan_flags: scan flags from SCAN_FLAGS_*
+ * @channel_count: num of channels in channel list (1 - MAX_NUM_SCAN_CHANNELS)
+ * @quiet_time: in msecs, dwell this time for active scan on quiet channels
+ * @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than
+ *     this number of packets were received (typically 1)
+ * @passive2active: is auto switching from passive to active allowed (0 or 1)
+ * @rxchain_sel_flags: RXON_RX_CHAIN_*
+ * @max_out_time: in usecs, max out of serving channel time
+ * @suspend_time: how long to pause scan when returning to service channel:
+ *     bits 0-19: beacon interal in usecs (suspend before executing)
+ *     bits 20-23: reserved
+ *     bits 24-31: number of beacons (suspend between channels)
+ * @rxon_flags: RXON_FLG_*
+ * @filter_flags: RXON_FILTER_*
+ * @tx_cmd: for active scans (zero for passive), w/o payload,
+ *     no RS so specify TX rate
+ * @direct_scan: direct scan SSIDs
+ * @type: one of SCAN_TYPE_*
+ * @repeats: how many time to repeat the scan
+ */
+struct iwl_scan_cmd {
+       __le16 len;
+       u8 scan_flags;
+       u8 channel_count;
+       __le16 quiet_time;
+       __le16 quiet_plcp_th;
+       __le16 passive2active;
+       __le16 rxchain_sel_flags;
+       __le32 max_out_time;
+       __le32 suspend_time;
+       /* RX_ON_FLAGS_API_S_VER_1 */
+       __le32 rxon_flags;
+       __le32 filter_flags;
+       struct iwl_tx_cmd tx_cmd;
+       struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+       __le32 type;
+       __le32 repeats;
+
+       /*
+        * Probe request frame, followed by channel list.
+        *
+        * Size of probe request frame is specified by byte count in tx_cmd.
+        * Channel list follows immediately after probe request frame.
+        * Number of channels in list is specified by channel_count.
+        * Each channel in list is of type:
+        *
+        * struct iwl_scan_channel channels[0];
+        *
+        * NOTE:  Only one band of channels can be scanned per pass.  You
+        * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait
+        * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION)
+        * before requesting another scan.
+        */
+       u8 data[0];
+} __packed; /* SCAN_REQUEST_FIXED_PART_API_S_VER_5 */
+
+/* Response to scan request contains only status with one of these values */
+#define SCAN_RESPONSE_OK       0x1
+#define SCAN_RESPONSE_ERROR    0x2
+
+/*
+ * SCAN_ABORT_CMD = 0x81
+ * When scan abort is requested, the command has no fields except the common
+ * header. The response contains only a status with one of these values.
+ */
+#define SCAN_ABORT_POSSIBLE    0x1
+#define SCAN_ABORT_IGNORED     0x2 /* no pending scans */
+
+/* TODO: complete documentation */
+#define  SCAN_OWNER_STATUS 0x1
+#define  MEASURE_OWNER_STATUS 0x2
+
+/**
+ * struct iwl_scan_start_notif - notifies start of scan in the device
+ * ( SCAN_START_NOTIFICATION = 0x82 )
+ * @tsf_low: TSF timer (lower half) in usecs
+ * @tsf_high: TSF timer (higher half) in usecs
+ * @beacon_timer: structured as follows:
+ *     bits 0:19 - beacon interval in usecs
+ *     bits 20:23 - reserved (0)
+ *     bits 24:31 - number of beacons
+ * @channel: which channel is scanned
+ * @band: 0 for 5.2 GHz, 1 for 2.4 GHz
+ * @status: one of *_OWNER_STATUS
+ */
+struct iwl_scan_start_notif {
+       __le32 tsf_low;
+       __le32 tsf_high;
+       __le32 beacon_timer;
+       u8 channel;
+       u8 band;
+       u8 reserved[2];
+       __le32 status;
+} __packed; /* SCAN_START_NTF_API_S_VER_1 */
+
+/* scan results probe_status first bit indicates success */
+#define SCAN_PROBE_STATUS_OK           0
+#define SCAN_PROBE_STATUS_TX_FAILED    BIT(0)
+/* error statuses combined with TX_FAILED */
+#define SCAN_PROBE_STATUS_FAIL_TTL     BIT(1)
+#define SCAN_PROBE_STATUS_FAIL_BT      BIT(2)
+
+/* How many statistics are gathered for each channel */
+#define SCAN_RESULTS_STATISTICS 1
+
+/**
+ * enum iwl_scan_complete_status - status codes for scan complete notifications
+ * @SCAN_COMP_STATUS_OK:  scan completed successfully
+ * @SCAN_COMP_STATUS_ABORT: scan was aborted by user
+ * @SCAN_COMP_STATUS_ERR_SLEEP: sending null sleep packet failed
+ * @SCAN_COMP_STATUS_ERR_CHAN_TIMEOUT: timeout before channel is ready
+ * @SCAN_COMP_STATUS_ERR_PROBE: sending probe request failed
+ * @SCAN_COMP_STATUS_ERR_WAKEUP: sending null wakeup packet failed
+ * @SCAN_COMP_STATUS_ERR_ANTENNAS: invalid antennas chosen at scan command
+ * @SCAN_COMP_STATUS_ERR_INTERNAL: internal error caused scan abort
+ * @SCAN_COMP_STATUS_ERR_COEX: medium was lost ot WiMax
+ * @SCAN_COMP_STATUS_P2P_ACTION_OK: P2P public action frame TX was successful
+ *     (not an error!)
+ * @SCAN_COMP_STATUS_ITERATION_END: indicates end of one repeatition the driver
+ *     asked for
+ * @SCAN_COMP_STATUS_ERR_ALLOC_TE: scan could not allocate time events
+*/
+enum iwl_scan_complete_status {
+       SCAN_COMP_STATUS_OK = 0x1,
+       SCAN_COMP_STATUS_ABORT = 0x2,
+       SCAN_COMP_STATUS_ERR_SLEEP = 0x3,
+       SCAN_COMP_STATUS_ERR_CHAN_TIMEOUT = 0x4,
+       SCAN_COMP_STATUS_ERR_PROBE = 0x5,
+       SCAN_COMP_STATUS_ERR_WAKEUP = 0x6,
+       SCAN_COMP_STATUS_ERR_ANTENNAS = 0x7,
+       SCAN_COMP_STATUS_ERR_INTERNAL = 0x8,
+       SCAN_COMP_STATUS_ERR_COEX = 0x9,
+       SCAN_COMP_STATUS_P2P_ACTION_OK = 0xA,
+       SCAN_COMP_STATUS_ITERATION_END = 0x0B,
+       SCAN_COMP_STATUS_ERR_ALLOC_TE = 0x0C,
+};
+
+/**
+ * struct iwl_scan_results_notif - scan results for one channel
+ * ( SCAN_RESULTS_NOTIFICATION = 0x83 )
+ * @channel: which channel the results are from
+ * @band: 0 for 5.2 GHz, 1 for 2.4 GHz
+ * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request
+ * @num_probe_not_sent: # of request that weren't sent due to not enough time
+ * @duration: duration spent in channel, in usecs
+ * @statistics: statistics gathered for this channel
+ */
+struct iwl_scan_results_notif {
+       u8 channel;
+       u8 band;
+       u8 probe_status;
+       u8 num_probe_not_sent;
+       __le32 duration;
+       __le32 statistics[SCAN_RESULTS_STATISTICS];
+} __packed; /* SCAN_RESULT_NTF_API_S_VER_2 */
+
+/**
+ * struct iwl_scan_complete_notif - notifies end of scanning (all channels)
+ * ( SCAN_COMPLETE_NOTIFICATION = 0x84 )
+ * @scanned_channels: number of channels scanned (and number of valid results)
+ * @status: one of SCAN_COMP_STATUS_*
+ * @bt_status: BT on/off status
+ * @last_channel: last channel that was scanned
+ * @tsf_low: TSF timer (lower half) in usecs
+ * @tsf_high: TSF timer (higher half) in usecs
+ * @results: all scan results, only "scanned_channels" of them are valid
+ */
+struct iwl_scan_complete_notif {
+       u8 scanned_channels;
+       u8 status;
+       u8 bt_status;
+       u8 last_channel;
+       __le32 tsf_low;
+       __le32 tsf_high;
+       struct iwl_scan_results_notif results[MAX_NUM_SCAN_CHANNELS];
+} __packed; /* SCAN_COMPLETE_NTF_API_S_VER_2 */
+
+/* scan offload */
+#define IWL_MAX_SCAN_CHANNELS          40
+#define IWL_SCAN_MAX_BLACKLIST_LEN     64
+#define IWL_SCAN_MAX_PROFILES          11
+#define SCAN_OFFLOAD_PROBE_REQ_SIZE    512
+
+/* Default watchdog (in MS) for scheduled scan iteration */
+#define IWL_SCHED_SCAN_WATCHDOG cpu_to_le16(15000)
+
+#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1)
+#define CAN_ABORT_STATUS 1
+
+#define IWL_FULL_SCAN_MULTIPLIER 5
+#define IWL_FAST_SCHED_SCAN_ITERATIONS 3
+
+/**
+ * struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6
+ * @scan_flags:                see enum iwl_scan_flags
+ * @channel_count:     channels in channel list
+ * @quiet_time:                dwell time, in milisiconds, on quiet channel
+ * @quiet_plcp_th:     quiet channel num of packets threshold
+ * @good_CRC_th:       passive to active promotion threshold
+ * @rx_chain:          RXON rx chain.
+ * @max_out_time:      max uSec to be out of assoceated channel
+ * @suspend_time:      pause scan this long when returning to service channel
+ * @flags:             RXON flags
+ * @filter_flags:      RXONfilter
+ * @tx_cmd:            tx command for active scan; for 2GHz and for 5GHz.
+ * @direct_scan:       list of SSIDs for directed active scan
+ * @scan_type:         see enum iwl_scan_type.
+ * @rep_count:         repetition count for each scheduled scan iteration.
+ */
+struct iwl_scan_offload_cmd {
+       __le16 len;
+       u8 scan_flags;
+       u8 channel_count;
+       __le16 quiet_time;
+       __le16 quiet_plcp_th;
+       __le16 good_CRC_th;
+       __le16 rx_chain;
+       __le32 max_out_time;
+       __le32 suspend_time;
+       /* RX_ON_FLAGS_API_S_VER_1 */
+       __le32 flags;
+       __le32 filter_flags;
+       struct iwl_tx_cmd tx_cmd[2];
+       /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */
+       struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+       __le32 scan_type;
+       __le32 rep_count;
+} __packed;
+
+enum iwl_scan_offload_channel_flags {
+       IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE         = BIT(0),
+       IWL_SCAN_OFFLOAD_CHANNEL_NARROW         = BIT(22),
+       IWL_SCAN_OFFLOAD_CHANNEL_FULL           = BIT(24),
+       IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL        = BIT(25),
+};
+
+/**
+ * iwl_scan_channel_cfg - SCAN_CHANNEL_CFG_S
+ * @type:              bitmap - see enum iwl_scan_offload_channel_flags.
+ *                     0:      passive (0) or active (1) scan.
+ *                     1-20:   directed scan to i'th ssid.
+ *                     22:     channel width configuation - 1 for narrow.
+ *                     24:     full scan.
+ *                     25:     partial scan.
+ * @channel_number:    channel number 1-13 etc.
+ * @iter_count:                repetition count for the channel.
+ * @iter_interval:     interval between two innteration on one channel.
+ * @dwell_time:        entry 0 - active scan, entry 1 - passive scan.
+ */
+struct iwl_scan_channel_cfg {
+       __le32 type[IWL_MAX_SCAN_CHANNELS];
+       __le16 channel_number[IWL_MAX_SCAN_CHANNELS];
+       __le16 iter_count[IWL_MAX_SCAN_CHANNELS];
+       __le32 iter_interval[IWL_MAX_SCAN_CHANNELS];
+       u8 dwell_time[IWL_MAX_SCAN_CHANNELS][2];
+} __packed;
+
+/**
+ * iwl_scan_offload_cfg - SCAN_OFFLOAD_CONFIG_API_S
+ * @scan_cmd:          scan command fixed part
+ * @channel_cfg:       scan channel configuration
+ * @data:              probe request frames (one per band)
+ */
+struct iwl_scan_offload_cfg {
+       struct iwl_scan_offload_cmd scan_cmd;
+       struct iwl_scan_channel_cfg channel_cfg;
+       u8 data[0];
+} __packed;
+
+/**
+ * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S
+ * @ssid:              MAC address to filter out
+ * @reported_rssi:     AP rssi reported to the host
+ */
+struct iwl_scan_offload_blacklist {
+       u8 ssid[ETH_ALEN];
+       u8 reported_rssi;
+       u8 reserved;
+} __packed;
+
+enum iwl_scan_offload_network_type {
+       IWL_NETWORK_TYPE_BSS    = 1,
+       IWL_NETWORK_TYPE_IBSS   = 2,
+       IWL_NETWORK_TYPE_ANY    = 3,
+};
+
+enum iwl_scan_offload_band_selection {
+       IWL_SCAN_OFFLOAD_SELECT_2_4     = 0x4,
+       IWL_SCAN_OFFLOAD_SELECT_5_2     = 0x8,
+       IWL_SCAN_OFFLOAD_SELECT_ANY     = 0xc,
+};
+
+/**
+ * iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S
+ * @ssid_index:                index to ssid list in fixed part
+ * @unicast_cipher:    encryption olgorithm to match - bitmap
+ * @aut_alg:           authentication olgorithm to match - bitmap
+ * @network_type:      enum iwl_scan_offload_network_type
+ * @band_selection:    enum iwl_scan_offload_band_selection
+ */
+struct iwl_scan_offload_profile {
+       u8 ssid_index;
+       u8 unicast_cipher;
+       u8 auth_alg;
+       u8 network_type;
+       u8 band_selection;
+       u8 reserved[3];
+} __packed;
+
+/**
+ * iwl_scan_offload_profile_cfg - SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1
+ * @blaclist:          AP list to filter off from scan results
+ * @profiles:          profiles to search for match
+ * @blacklist_len:     length of blacklist
+ * @num_profiles:      num of profiles in the list
+ */
+struct iwl_scan_offload_profile_cfg {
+       struct iwl_scan_offload_blacklist blacklist[IWL_SCAN_MAX_BLACKLIST_LEN];
+       struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES];
+       u8 blacklist_len;
+       u8 num_profiles;
+       u8 reserved[2];
+} __packed;
+
+/**
+ * iwl_scan_offload_schedule - schedule of scan offload
+ * @delay:             delay between iterations, in seconds.
+ * @iterations:                num of scan iterations
+ * @full_scan_mul:     number of partial scans before each full scan
+ */
+struct iwl_scan_offload_schedule {
+       u16 delay;
+       u8 iterations;
+       u8 full_scan_mul;
+} __packed;
+
+/*
+ * iwl_scan_offload_flags
+ *
+ * IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID: filter mode - upload every beacon or match
+ *     ssid list.
+ * IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL: add cached channels to partial scan.
+ * IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN: use energy based scan before partial scan
+ *     on A band.
+ */
+enum iwl_scan_offload_flags {
+       IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID       = BIT(0),
+       IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL    = BIT(2),
+       IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN       = BIT(3),
+};
+
+/**
+ * iwl_scan_offload_req - scan offload request command
+ * @flags:             bitmap - enum iwl_scan_offload_flags.
+ * @watchdog:          maximum scan duration in TU.
+ * @delay:             delay in seconds before first iteration.
+ * @schedule_line:     scan offload schedule, for fast and regular scan.
+ */
+struct iwl_scan_offload_req {
+       __le16 flags;
+       __le16 watchdog;
+       __le16 delay;
+       __le16 reserved;
+       struct iwl_scan_offload_schedule schedule_line[2];
+} __packed;
+
+enum iwl_scan_offload_compleate_status {
+       IWL_SCAN_OFFLOAD_COMPLETED      = 1,
+       IWL_SCAN_OFFLOAD_ABORTED        = 2,
+};
+
+/**
+ * iwl_scan_offload_complete - SCAN_OFFLOAD_COMPLETE_NTF_API_S_VER_1
+ * @last_schedule_line:                last schedule line executed (fast or regular)
+ * @last_schedule_iteration:   last scan iteration executed before scan abort
+ * @status:                    enum iwl_scan_offload_compleate_status
+ */
+struct iwl_scan_offload_complete {
+       u8 last_schedule_line;
+       u8 last_schedule_iteration;
+       u8 status;
+       u8 reserved;
+} __packed;
+
+#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
new file mode 100644 (file)
index 0000000..0acb53d
--- /dev/null
@@ -0,0 +1,380 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_sta_h__
+#define __fw_api_sta_h__
+
+/**
+ * enum iwl_sta_flags - flags for the ADD_STA host command
+ * @STA_FLG_REDUCED_TX_PWR_CTRL:
+ * @STA_FLG_REDUCED_TX_PWR_DATA:
+ * @STA_FLG_FLG_ANT_MSK: Antenna selection
+ * @STA_FLG_PS: set if STA is in Power Save
+ * @STA_FLG_INVALID: set if STA is invalid
+ * @STA_FLG_DLP_EN: Direct Link Protocol is enabled
+ * @STA_FLG_SET_ALL_KEYS: the current key applies to all key IDs
+ * @STA_FLG_DRAIN_FLOW: drain flow
+ * @STA_FLG_PAN: STA is for PAN interface
+ * @STA_FLG_CLASS_AUTH:
+ * @STA_FLG_CLASS_ASSOC:
+ * @STA_FLG_CLASS_MIMO_PROT:
+ * @STA_FLG_MAX_AGG_SIZE_MSK: maximal size for A-MPDU
+ * @STA_FLG_AGG_MPDU_DENS_MSK: maximal MPDU density for Tx aggregation
+ * @STA_FLG_FAT_EN_MSK: support for channel width (for Tx). This flag is
+ *     initialised by driver and can be updated by fw upon reception of
+ *     action frames that can change the channel width. When cleared the fw
+ *     will send all the frames in 20MHz even when FAT channel is requested.
+ * @STA_FLG_MIMO_EN_MSK: support for MIMO. This flag is initialised by the
+ *     driver and can be updated by fw upon reception of action frames.
+ * @STA_FLG_MFP_EN: Management Frame Protection
+ */
+enum iwl_sta_flags {
+       STA_FLG_REDUCED_TX_PWR_CTRL     = BIT(3),
+       STA_FLG_REDUCED_TX_PWR_DATA     = BIT(6),
+
+       STA_FLG_FLG_ANT_A               = (1 << 4),
+       STA_FLG_FLG_ANT_B               = (2 << 4),
+       STA_FLG_FLG_ANT_MSK             = (STA_FLG_FLG_ANT_A |
+                                          STA_FLG_FLG_ANT_B),
+
+       STA_FLG_PS                      = BIT(8),
+       STA_FLG_INVALID                 = BIT(9),
+       STA_FLG_DLP_EN                  = BIT(10),
+       STA_FLG_SET_ALL_KEYS            = BIT(11),
+       STA_FLG_DRAIN_FLOW              = BIT(12),
+       STA_FLG_PAN                     = BIT(13),
+       STA_FLG_CLASS_AUTH              = BIT(14),
+       STA_FLG_CLASS_ASSOC             = BIT(15),
+       STA_FLG_RTS_MIMO_PROT           = BIT(17),
+
+       STA_FLG_MAX_AGG_SIZE_SHIFT      = 19,
+       STA_FLG_MAX_AGG_SIZE_8K         = (0 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_16K        = (1 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_32K        = (2 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_64K        = (3 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_128K       = (4 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_256K       = (5 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_512K       = (6 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_1024K      = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_MSK        = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+
+       STA_FLG_AGG_MPDU_DENS_SHIFT     = 23,
+       STA_FLG_AGG_MPDU_DENS_2US       = (4 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+       STA_FLG_AGG_MPDU_DENS_4US       = (5 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+       STA_FLG_AGG_MPDU_DENS_8US       = (6 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+       STA_FLG_AGG_MPDU_DENS_16US      = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+       STA_FLG_AGG_MPDU_DENS_MSK       = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+
+       STA_FLG_FAT_EN_20MHZ            = (0 << 26),
+       STA_FLG_FAT_EN_40MHZ            = (1 << 26),
+       STA_FLG_FAT_EN_80MHZ            = (2 << 26),
+       STA_FLG_FAT_EN_160MHZ           = (3 << 26),
+       STA_FLG_FAT_EN_MSK              = (3 << 26),
+
+       STA_FLG_MIMO_EN_SISO            = (0 << 28),
+       STA_FLG_MIMO_EN_MIMO2           = (1 << 28),
+       STA_FLG_MIMO_EN_MIMO3           = (2 << 28),
+       STA_FLG_MIMO_EN_MSK             = (3 << 28),
+};
+
+/**
+ * enum iwl_sta_key_flag - key flags for the ADD_STA host command
+ * @STA_KEY_FLG_EN_MSK: mask for encryption algorithm
+ * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from
+ *     station info array (1 - n 1X mode)
+ * @STA_KEY_FLG_KEYID_MSK: the index of the key
+ * @STA_KEY_NOT_VALID: key is invalid
+ * @STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key
+ * @STA_KEY_MULTICAST: set for multical key
+ * @STA_KEY_MFP: key is used for Management Frame Protection
+ */
+enum iwl_sta_key_flag {
+       STA_KEY_FLG_NO_ENC              = (0 << 0),
+       STA_KEY_FLG_WEP                 = (1 << 0),
+       STA_KEY_FLG_CCM                 = (2 << 0),
+       STA_KEY_FLG_TKIP                = (3 << 0),
+       STA_KEY_FLG_CMAC                = (6 << 0),
+       STA_KEY_FLG_ENC_UNKNOWN         = (7 << 0),
+       STA_KEY_FLG_EN_MSK              = (7 << 0),
+
+       STA_KEY_FLG_WEP_KEY_MAP         = BIT(3),
+       STA_KEY_FLG_KEYID_POS            = 8,
+       STA_KEY_FLG_KEYID_MSK           = (3 << STA_KEY_FLG_KEYID_POS),
+       STA_KEY_NOT_VALID               = BIT(11),
+       STA_KEY_FLG_WEP_13BYTES         = BIT(12),
+       STA_KEY_MULTICAST               = BIT(14),
+       STA_KEY_MFP                     = BIT(15),
+};
+
+/**
+ * enum iwl_sta_modify_flag - indicate to the fw what flag are being changed
+ * @STA_MODIFY_KEY: this command modifies %key
+ * @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx
+ * @STA_MODIFY_TX_RATE: unused
+ * @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid
+ * @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid
+ * @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count
+ * @STA_MODIFY_PROT_TH:
+ * @STA_MODIFY_QUEUES: modify the queues used by this station
+ */
+enum iwl_sta_modify_flag {
+       STA_MODIFY_KEY                          = BIT(0),
+       STA_MODIFY_TID_DISABLE_TX               = BIT(1),
+       STA_MODIFY_TX_RATE                      = BIT(2),
+       STA_MODIFY_ADD_BA_TID                   = BIT(3),
+       STA_MODIFY_REMOVE_BA_TID                = BIT(4),
+       STA_MODIFY_SLEEPING_STA_TX_COUNT        = BIT(5),
+       STA_MODIFY_PROT_TH                      = BIT(6),
+       STA_MODIFY_QUEUES                       = BIT(7),
+};
+
+#define STA_MODE_MODIFY        1
+
+/**
+ * enum iwl_sta_sleep_flag - type of sleep of the station
+ * @STA_SLEEP_STATE_AWAKE:
+ * @STA_SLEEP_STATE_PS_POLL:
+ * @STA_SLEEP_STATE_UAPSD:
+ */
+enum iwl_sta_sleep_flag {
+       STA_SLEEP_STATE_AWAKE   = 0,
+       STA_SLEEP_STATE_PS_POLL = BIT(0),
+       STA_SLEEP_STATE_UAPSD   = BIT(1),
+};
+
+/* STA ID and color bits definitions */
+#define STA_ID_SEED            (0x0f)
+#define STA_ID_POS             (0)
+#define STA_ID_MSK             (STA_ID_SEED << STA_ID_POS)
+
+#define STA_COLOR_SEED         (0x7)
+#define STA_COLOR_POS          (4)
+#define STA_COLOR_MSK          (STA_COLOR_SEED << STA_COLOR_POS)
+
+#define STA_ID_N_COLOR_GET_COLOR(id_n_color) \
+       (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS)
+#define STA_ID_N_COLOR_GET_ID(id_n_color)    \
+       (((id_n_color) & STA_ID_MSK) >> STA_ID_POS)
+
+#define STA_KEY_MAX_NUM (16)
+#define STA_KEY_IDX_INVALID (0xff)
+#define STA_KEY_MAX_DATA_KEY_NUM (4)
+#define IWL_MAX_GLOBAL_KEYS (4)
+#define STA_KEY_LEN_WEP40 (5)
+#define STA_KEY_LEN_WEP104 (13)
+
+/**
+ * struct iwl_mvm_keyinfo - key information
+ * @key_flags: type %iwl_sta_key_flag
+ * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection
+ * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx
+ * @key_offset: key offset in the fw's key table
+ * @key: 16-byte unicast decryption key
+ * @tx_secur_seq_cnt: initial RSC / PN needed for replay check
+ * @hw_tkip_mic_rx_key: byte: MIC Rx Key - used for TKIP only
+ * @hw_tkip_mic_tx_key: byte: MIC Tx Key - used for TKIP only
+ */
+struct iwl_mvm_keyinfo {
+       __le16 key_flags;
+       u8 tkip_rx_tsc_byte2;
+       u8 reserved1;
+       __le16 tkip_rx_ttak[5];
+       u8 key_offset;
+       u8 reserved2;
+       u8 key[16];
+       __le64 tx_secur_seq_cnt;
+       __le64 hw_tkip_mic_rx_key;
+       __le64 hw_tkip_mic_tx_key;
+} __packed;
+
+/**
+ * struct iwl_mvm_add_sta_cmd - Add / modify a station in the fw's station table
+ * ( REPLY_ADD_STA = 0x18 )
+ * @add_modify: 1: modify existing, 0: add new station
+ * @unicast_tx_key_id: unicast tx key id. Relevant only when unicast key sent
+ * @multicast_tx_key_id: multicast tx key id. Relevant only when multicast key
+ *     sent
+ * @mac_id_n_color: the Mac context this station belongs to
+ * @addr[ETH_ALEN]: station's MAC address
+ * @sta_id: index of station in uCode's station table
+ * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave
+ *     alone. 1 - modify, 0 - don't change.
+ * @key: look at %iwl_mvm_keyinfo
+ * @station_flags: look at %iwl_sta_flags
+ * @station_flags_msk: what of %station_flags have changed
+ * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable
+ *     AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field.
+ * @add_immediate_ba_tid: tid for which to add block-ack support (Rx)
+ *     Set %STA_MODIFY_ADD_BA_TID to use this field, and also set
+ *     add_immediate_ba_ssn.
+ * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx)
+ *     Set %STA_MODIFY_REMOVE_BA_TID to use this field
+ * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with
+ *     add_immediate_ba_tid.
+ * @sleep_tx_count: number of packets to transmit to station even though it is
+ *     asleep. Used to synchronise PS-poll and u-APSD responses while ucode
+ *     keeps track of STA sleep state.
+ * @sleep_state_flags: Look at %iwl_sta_sleep_flag.
+ * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP
+ *     mac-addr.
+ * @beamform_flags: beam forming controls
+ * @tfd_queue_msk: tfd queues used by this station
+ *
+ * The device contains an internal table of per-station information, with info
+ * on security keys, aggregation parameters, and Tx rates for initial Tx
+ * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD).
+ *
+ * ADD_STA sets up the table entry for one station, either creating a new
+ * entry, or modifying a pre-existing one.
+ */
+struct iwl_mvm_add_sta_cmd {
+       u8 add_modify;
+       u8 unicast_tx_key_id;
+       u8 multicast_tx_key_id;
+       u8 reserved1;
+       __le32 mac_id_n_color;
+       u8 addr[ETH_ALEN];
+       __le16 reserved2;
+       u8 sta_id;
+       u8 modify_mask;
+       __le16 reserved3;
+       struct iwl_mvm_keyinfo key;
+       __le32 station_flags;
+       __le32 station_flags_msk;
+       __le16 tid_disable_tx;
+       __le16 reserved4;
+       u8 add_immediate_ba_tid;
+       u8 remove_immediate_ba_tid;
+       __le16 add_immediate_ba_ssn;
+       __le16 sleep_tx_count;
+       __le16 sleep_state_flags;
+       __le16 assoc_id;
+       __le16 beamform_flags;
+       __le32 tfd_queue_msk;
+} __packed; /* ADD_STA_CMD_API_S_VER_5 */
+
+/**
+ * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command
+ * @ADD_STA_SUCCESS: operation was executed successfully
+ * @ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table
+ * @ADD_STA_IMMEDIATE_BA_FAILURE: can't add Rx block ack session
+ * @ADD_STA_MODIFY_NON_EXISTING_STA: driver requested to modify a station that
+ *     doesn't exist.
+ */
+enum iwl_mvm_add_sta_rsp_status {
+       ADD_STA_SUCCESS                 = 0x1,
+       ADD_STA_STATIONS_OVERLOAD       = 0x2,
+       ADD_STA_IMMEDIATE_BA_FAILURE    = 0x4,
+       ADD_STA_MODIFY_NON_EXISTING_STA = 0x8,
+};
+
+/**
+ * struct iwl_mvm_rm_sta_cmd - Add / modify a station in the fw's station table
+ * ( REMOVE_STA = 0x19 )
+ * @sta_id: the station id of the station to be removed
+ */
+struct iwl_mvm_rm_sta_cmd {
+       u8 sta_id;
+       u8 reserved[3];
+} __packed; /* REMOVE_STA_CMD_API_S_VER_2 */
+
+/**
+ * struct iwl_mvm_mgmt_mcast_key_cmd
+ * ( MGMT_MCAST_KEY = 0x1f )
+ * @ctrl_flags: %iwl_sta_key_flag
+ * @IGTK:
+ * @K1: IGTK master key
+ * @K2: IGTK sub key
+ * @sta_id: station ID that support IGTK
+ * @key_id:
+ * @receive_seq_cnt: initial RSC/PN needed for replay check
+ */
+struct iwl_mvm_mgmt_mcast_key_cmd {
+       __le32 ctrl_flags;
+       u8 IGTK[16];
+       u8 K1[16];
+       u8 K2[16];
+       __le32 key_id;
+       __le32 sta_id;
+       __le64 receive_seq_cnt;
+} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */
+
+struct iwl_mvm_wep_key {
+       u8 key_index;
+       u8 key_offset;
+       __le16 reserved1;
+       u8 key_size;
+       u8 reserved2[3];
+       u8 key[16];
+} __packed;
+
+struct iwl_mvm_wep_key_cmd {
+       __le32 mac_id_n_color;
+       u8 num_keys;
+       u8 decryption_type;
+       u8 flags;
+       u8 reserved;
+       struct iwl_mvm_wep_key wep_key[0];
+} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */
+
+
+#endif /* __fw_api_sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
new file mode 100644 (file)
index 0000000..2677914
--- /dev/null
@@ -0,0 +1,580 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_tx_h__
+#define __fw_api_tx_h__
+
+/**
+ * enum iwl_tx_flags - bitmasks for tx_flags in TX command
+ * @TX_CMD_FLG_PROT_REQUIRE: use RTS or CTS-to-self to protect the frame
+ * @TX_CMD_FLG_ACK: expect ACK from receiving station
+ * @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command.
+ *     Otherwise, use rate_n_flags from the TX command
+ * @TX_CMD_FLG_BA: this frame is a block ack
+ * @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected
+ *     Must set TX_CMD_FLG_ACK with this flag.
+ * @TX_CMD_FLG_TXOP_PROT: protect frame with full TXOP protection
+ * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence
+ * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence
+ * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC)
+ * @TX_CMD_FLG_BT_DIS: disable BT priority for this frame
+ * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control.
+ *     Should be set for mgmt, non-QOS data, mcast, bcast and in scan command
+ * @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU
+ * @TX_CMD_FLG_NEXT_FRAME: this frame includes information of the next frame
+ * @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame
+ *     Should be set for beacons and probe responses
+ * @TX_CMD_FLG_CALIB: activate PA TX power calibrations
+ * @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count
+ * @TX_CMD_FLG_AGG_START: allow this frame to start aggregation
+ * @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header.
+ *     Should be set for 26/30 length MAC headers
+ * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW
+ * @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration
+ * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation
+ * @TX_CMD_FLG_CTS_ONLY: send CTS only, no data after that
+ * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id
+ * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped
+ * @TX_CMD_FLG_EXEC_PAPD: execute PAPD
+ * @TX_CMD_FLG_PAPD_TYPE: 0 for reference power, 1 for nominal power
+ * @TX_CMD_FLG_HCCA_CHUNK: mark start of TSPEC chunk
+ */
+enum iwl_tx_flags {
+       TX_CMD_FLG_PROT_REQUIRE         = BIT(0),
+       TX_CMD_FLG_ACK                  = BIT(3),
+       TX_CMD_FLG_STA_RATE             = BIT(4),
+       TX_CMD_FLG_BA                   = BIT(5),
+       TX_CMD_FLG_BAR                  = BIT(6),
+       TX_CMD_FLG_TXOP_PROT            = BIT(7),
+       TX_CMD_FLG_VHT_NDPA             = BIT(8),
+       TX_CMD_FLG_HT_NDPA              = BIT(9),
+       TX_CMD_FLG_CSI_FDBK2HOST        = BIT(10),
+       TX_CMD_FLG_BT_DIS               = BIT(12),
+       TX_CMD_FLG_SEQ_CTL              = BIT(13),
+       TX_CMD_FLG_MORE_FRAG            = BIT(14),
+       TX_CMD_FLG_NEXT_FRAME           = BIT(15),
+       TX_CMD_FLG_TSF                  = BIT(16),
+       TX_CMD_FLG_CALIB                = BIT(17),
+       TX_CMD_FLG_KEEP_SEQ_CTL         = BIT(18),
+       TX_CMD_FLG_AGG_START            = BIT(19),
+       TX_CMD_FLG_MH_PAD               = BIT(20),
+       TX_CMD_FLG_RESP_TO_DRV          = BIT(21),
+       TX_CMD_FLG_CCMP_AGG             = BIT(22),
+       TX_CMD_FLG_TKIP_MIC_DONE        = BIT(23),
+       TX_CMD_FLG_CTS_ONLY             = BIT(24),
+       TX_CMD_FLG_DUR                  = BIT(25),
+       TX_CMD_FLG_FW_DROP              = BIT(26),
+       TX_CMD_FLG_EXEC_PAPD            = BIT(27),
+       TX_CMD_FLG_PAPD_TYPE            = BIT(28),
+       TX_CMD_FLG_HCCA_CHUNK           = BIT(31)
+}; /* TX_FLAGS_BITS_API_S_VER_1 */
+
+/*
+ * TX command security control
+ */
+#define TX_CMD_SEC_WEP                 0x01
+#define TX_CMD_SEC_CCM                 0x02
+#define TX_CMD_SEC_TKIP                        0x03
+#define TX_CMD_SEC_WEP_KEY_IDX_POS     6
+#define TX_CMD_SEC_WEP_KEY_IDX_MSK     0xc0
+#define TX_CMD_SEC_KEY128              0x08
+
+/* TODO: how does these values are OK with only 16 bit variable??? */
+/*
+ * TX command next frame info
+ *
+ * bits 0:2 - security control (TX_CMD_SEC_*)
+ * bit 3 - immediate ACK required
+ * bit 4 - rate is taken from STA table
+ * bit 5 - frame belongs to BA stream
+ * bit 6 - immediate BA response expected
+ * bit 7 - unused
+ * bits 8:15 - Station ID
+ * bits 16:31 - rate
+ */
+#define TX_CMD_NEXT_FRAME_ACK_MSK              (0x8)
+#define TX_CMD_NEXT_FRAME_STA_RATE_MSK         (0x10)
+#define TX_CMD_NEXT_FRAME_BA_MSK               (0x20)
+#define TX_CMD_NEXT_FRAME_IMM_BA_RSP_MSK       (0x40)
+#define TX_CMD_NEXT_FRAME_FLAGS_MSK            (0xf8)
+#define TX_CMD_NEXT_FRAME_STA_ID_MSK           (0xff00)
+#define TX_CMD_NEXT_FRAME_STA_ID_POS           (8)
+#define TX_CMD_NEXT_FRAME_RATE_MSK             (0xffff0000)
+#define TX_CMD_NEXT_FRAME_RATE_POS             (16)
+
+/*
+ * TX command Frame life time in us - to be written in pm_frame_timeout
+ */
+#define TX_CMD_LIFE_TIME_INFINITE      0xFFFFFFFF
+#define TX_CMD_LIFE_TIME_DEFAULT       2000000 /* 2000 ms*/
+#define TX_CMD_LIFE_TIME_PROBE_RESP    40000 /* 40 ms */
+#define TX_CMD_LIFE_TIME_EXPIRED_FRAME 0
+
+/*
+ * TID for non QoS frames - to be written in tid_tspec
+ */
+#define IWL_TID_NON_QOS        IWL_MAX_TID_COUNT
+
+/*
+ * Limits on the retransmissions - to be written in {data,rts}_retry_limit
+ */
+#define IWL_DEFAULT_TX_RETRY                   15
+#define IWL_MGMT_DFAULT_RETRY_LIMIT            3
+#define IWL_RTS_DFAULT_RETRY_LIMIT             60
+#define IWL_BAR_DFAULT_RETRY_LIMIT             60
+#define IWL_LOW_RETRY_LIMIT                    7
+
+/* TODO: complete documentation for try_cnt and btkill_cnt */
+/**
+ * struct iwl_tx_cmd - TX command struct to FW
+ * ( TX_CMD = 0x1c )
+ * @len: in bytes of the payload, see below for details
+ * @next_frame_len: same as len, but for next frame (0 if not applicable)
+ *     Used for fragmentation and bursting, but not in 11n aggregation.
+ * @tx_flags: combination of TX_CMD_FLG_*
+ * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
+ *     cleared. Combination of RATE_MCS_*
+ * @sta_id: index of destination station in FW station table
+ * @sec_ctl: security control, TX_CMD_SEC_*
+ * @initial_rate_index: index into the the rate table for initial TX attempt.
+ *     Applied if TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames.
+ * @key: security key
+ * @next_frame_flags: TX_CMD_SEC_* and TX_CMD_NEXT_FRAME_*
+ * @life_time: frame life time (usecs??)
+ * @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt +
+ *     btkill_cnd + reserved), first 32 bits. "0" disables usage.
+ * @dram_msb_ptr: upper bits of the scratch physical address
+ * @rts_retry_limit: max attempts for RTS
+ * @data_retry_limit: max attempts to send the data packet
+ * @tid_spec: TID/tspec
+ * @pm_frame_timeout: PM TX frame timeout
+ * @driver_txop: duration od EDCA TXOP, in 32-usec units. Set this if not
+ *     specified by HCCA protocol
+ *
+ * The byte count (both len and next_frame_len) includes MAC header
+ * (24/26/30/32 bytes)
+ * + 2 bytes pad if 26/30 header size
+ * + 8 byte IV for CCM or TKIP (not used for WEP)
+ * + Data payload
+ * + 8-byte MIC (not used for CCM/WEP)
+ * It does not include post-MAC padding, i.e.,
+ * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.
+ * Range of len: 14-2342 bytes.
+ *
+ * After the struct fields the MAC header is placed, plus any padding,
+ * and then the actial payload.
+ */
+struct iwl_tx_cmd {
+       __le16 len;
+       __le16 next_frame_len;
+       __le32 tx_flags;
+       /* DRAM_SCRATCH_API_U_VER_1 */
+       u8 try_cnt;
+       u8 btkill_cnt;
+       __le16 reserved;
+       __le32 rate_n_flags;
+       u8 sta_id;
+       u8 sec_ctl;
+       u8 initial_rate_index;
+       u8 reserved2;
+       u8 key[16];
+       __le16 next_frame_flags;
+       __le16 reserved3;
+       __le32 life_time;
+       __le32 dram_lsb_ptr;
+       u8 dram_msb_ptr;
+       u8 rts_retry_limit;
+       u8 data_retry_limit;
+       u8 tid_tspec;
+       __le16 pm_frame_timeout;
+       __le16 driver_txop;
+       u8 payload[0];
+       struct ieee80211_hdr hdr[0];
+} __packed; /* TX_CMD_API_S_VER_3 */
+
+/*
+ * TX response related data
+ */
+
+/*
+ * enum iwl_tx_status - status that is returned by the fw after attempts to Tx
+ * @TX_STATUS_SUCCESS:
+ * @TX_STATUS_DIRECT_DONE:
+ * @TX_STATUS_POSTPONE_DELAY:
+ * @TX_STATUS_POSTPONE_FEW_BYTES:
+ * @TX_STATUS_POSTPONE_BT_PRIO:
+ * @TX_STATUS_POSTPONE_QUIET_PERIOD:
+ * @TX_STATUS_POSTPONE_CALC_TTAK:
+ * @TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
+ * @TX_STATUS_FAIL_SHORT_LIMIT:
+ * @TX_STATUS_FAIL_LONG_LIMIT:
+ * @TX_STATUS_FAIL_UNDERRUN:
+ * @TX_STATUS_FAIL_DRAIN_FLOW:
+ * @TX_STATUS_FAIL_RFKILL_FLUSH:
+ * @TX_STATUS_FAIL_LIFE_EXPIRE:
+ * @TX_STATUS_FAIL_DEST_PS:
+ * @TX_STATUS_FAIL_HOST_ABORTED:
+ * @TX_STATUS_FAIL_BT_RETRY:
+ * @TX_STATUS_FAIL_STA_INVALID:
+ * @TX_TATUS_FAIL_FRAG_DROPPED:
+ * @TX_STATUS_FAIL_TID_DISABLE:
+ * @TX_STATUS_FAIL_FIFO_FLUSHED:
+ * @TX_STATUS_FAIL_SMALL_CF_POLL:
+ * @TX_STATUS_FAIL_FW_DROP:
+ * @TX_STATUS_FAIL_STA_COLOR_MISMATCH: mismatch between color of Tx cmd and
+ *     STA table
+ * @TX_FRAME_STATUS_INTERNAL_ABORT:
+ * @TX_MODE_MSK:
+ * @TX_MODE_NO_BURST:
+ * @TX_MODE_IN_BURST_SEQ:
+ * @TX_MODE_FIRST_IN_BURST:
+ * @TX_QUEUE_NUM_MSK:
+ *
+ * Valid only if frame_count =1
+ * TODO: complete documentation
+ */
+enum iwl_tx_status {
+       TX_STATUS_MSK = 0x000000ff,
+       TX_STATUS_SUCCESS = 0x01,
+       TX_STATUS_DIRECT_DONE = 0x02,
+       /* postpone TX */
+       TX_STATUS_POSTPONE_DELAY = 0x40,
+       TX_STATUS_POSTPONE_FEW_BYTES = 0x41,
+       TX_STATUS_POSTPONE_BT_PRIO = 0x42,
+       TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43,
+       TX_STATUS_POSTPONE_CALC_TTAK = 0x44,
+       /* abort TX */
+       TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81,
+       TX_STATUS_FAIL_SHORT_LIMIT = 0x82,
+       TX_STATUS_FAIL_LONG_LIMIT = 0x83,
+       TX_STATUS_FAIL_UNDERRUN = 0x84,
+       TX_STATUS_FAIL_DRAIN_FLOW = 0x85,
+       TX_STATUS_FAIL_RFKILL_FLUSH = 0x86,
+       TX_STATUS_FAIL_LIFE_EXPIRE = 0x87,
+       TX_STATUS_FAIL_DEST_PS = 0x88,
+       TX_STATUS_FAIL_HOST_ABORTED = 0x89,
+       TX_STATUS_FAIL_BT_RETRY = 0x8a,
+       TX_STATUS_FAIL_STA_INVALID = 0x8b,
+       TX_STATUS_FAIL_FRAG_DROPPED = 0x8c,
+       TX_STATUS_FAIL_TID_DISABLE = 0x8d,
+       TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e,
+       TX_STATUS_FAIL_SMALL_CF_POLL = 0x8f,
+       TX_STATUS_FAIL_FW_DROP = 0x90,
+       TX_STATUS_FAIL_STA_COLOR_MISMATCH = 0x91,
+       TX_STATUS_INTERNAL_ABORT = 0x92,
+       TX_MODE_MSK = 0x00000f00,
+       TX_MODE_NO_BURST = 0x00000000,
+       TX_MODE_IN_BURST_SEQ = 0x00000100,
+       TX_MODE_FIRST_IN_BURST = 0x00000200,
+       TX_QUEUE_NUM_MSK = 0x0001f000,
+       TX_NARROW_BW_MSK = 0x00060000,
+       TX_NARROW_BW_1DIV2 = 0x00020000,
+       TX_NARROW_BW_1DIV4 = 0x00040000,
+       TX_NARROW_BW_1DIV8 = 0x00060000,
+};
+
+/*
+ * enum iwl_tx_agg_status - TX aggregation status
+ * @AGG_TX_STATE_STATUS_MSK:
+ * @AGG_TX_STATE_TRANSMITTED:
+ * @AGG_TX_STATE_UNDERRUN:
+ * @AGG_TX_STATE_BT_PRIO:
+ * @AGG_TX_STATE_FEW_BYTES:
+ * @AGG_TX_STATE_ABORT:
+ * @AGG_TX_STATE_LAST_SENT_TTL:
+ * @AGG_TX_STATE_LAST_SENT_TRY_CNT:
+ * @AGG_TX_STATE_LAST_SENT_BT_KILL:
+ * @AGG_TX_STATE_SCD_QUERY:
+ * @AGG_TX_STATE_TEST_BAD_CRC32:
+ * @AGG_TX_STATE_RESPONSE:
+ * @AGG_TX_STATE_DUMP_TX:
+ * @AGG_TX_STATE_DELAY_TX:
+ * @AGG_TX_STATE_TRY_CNT_MSK: Retry count for 1st frame in aggregation (retries
+ *     occur if tx failed for this frame when it was a member of a previous
+ *     aggregation block). If rate scaling is used, retry count indicates the
+ *     rate table entry used for all frames in the new agg.
+ *@ AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for
+ *     this frame
+ *
+ * TODO: complete documentation
+ */
+enum iwl_tx_agg_status {
+       AGG_TX_STATE_STATUS_MSK = 0x00fff,
+       AGG_TX_STATE_TRANSMITTED = 0x000,
+       AGG_TX_STATE_UNDERRUN = 0x001,
+       AGG_TX_STATE_BT_PRIO = 0x002,
+       AGG_TX_STATE_FEW_BYTES = 0x004,
+       AGG_TX_STATE_ABORT = 0x008,
+       AGG_TX_STATE_LAST_SENT_TTL = 0x010,
+       AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020,
+       AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040,
+       AGG_TX_STATE_SCD_QUERY = 0x080,
+       AGG_TX_STATE_TEST_BAD_CRC32 = 0x0100,
+       AGG_TX_STATE_RESPONSE = 0x1ff,
+       AGG_TX_STATE_DUMP_TX = 0x200,
+       AGG_TX_STATE_DELAY_TX = 0x400,
+       AGG_TX_STATE_TRY_CNT_POS = 12,
+       AGG_TX_STATE_TRY_CNT_MSK = 0xf << AGG_TX_STATE_TRY_CNT_POS,
+};
+
+#define AGG_TX_STATE_LAST_SENT_MSK  (AGG_TX_STATE_LAST_SENT_TTL| \
+                                    AGG_TX_STATE_LAST_SENT_TRY_CNT| \
+                                    AGG_TX_STATE_LAST_SENT_BT_KILL)
+
+/*
+ * The mask below describes a status where we are absolutely sure that the MPDU
+ * wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've
+ * written the bytes to the TXE, but we know nothing about what the DSP did.
+ */
+#define AGG_TX_STAT_FRAME_NOT_SENT (AGG_TX_STATE_FEW_BYTES | \
+                                   AGG_TX_STATE_ABORT | \
+                                   AGG_TX_STATE_SCD_QUERY)
+
+/*
+ * REPLY_TX = 0x1c (response)
+ *
+ * This response may be in one of two slightly different formats, indicated
+ * by the frame_count field:
+ *
+ * 1)  No aggregation (frame_count == 1).  This reports Tx results for a single
+ *     frame. Multiple attempts, at various bit rates, may have been made for
+ *     this frame.
+ *
+ * 2)  Aggregation (frame_count > 1).  This reports Tx results for two or more
+ *     frames that used block-acknowledge.  All frames were transmitted at
+ *     same rate. Rate scaling may have been used if first frame in this new
+ *     agg block failed in previous agg block(s).
+ *
+ *     Note that, for aggregation, ACK (block-ack) status is not delivered
+ *     here; block-ack has not been received by the time the device records
+ *     this status.
+ *     This status relates to reasons the tx might have been blocked or aborted
+ *     within the device, rather than whether it was received successfully by
+ *     the destination station.
+ */
+
+/**
+ * struct agg_tx_status - per packet TX aggregation status
+ * @status: enum iwl_tx_agg_status
+ * @sequence: Sequence # for this frame's Tx cmd (not SSN!)
+ */
+struct agg_tx_status {
+       __le16 status;
+       __le16 sequence;
+} __packed;
+
+/*
+ * definitions for initial rate index field
+ * bits [3:0] initial rate index
+ * bits [6:4] rate table color, used for the initial rate
+ * bit-7 invalid rate indication
+ */
+#define TX_RES_INIT_RATE_INDEX_MSK 0x0f
+#define TX_RES_RATE_TABLE_COLOR_MSK 0x70
+#define TX_RES_INV_RATE_INDEX_MSK 0x80
+
+#define IWL_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f)
+#define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4)
+
+/**
+ * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet
+ * ( REPLY_TX = 0x1c )
+ * @frame_count: 1 no aggregation, >1 aggregation
+ * @bt_kill_count: num of times blocked by bluetooth (unused for agg)
+ * @failure_rts: num of failures due to unsuccessful RTS
+ * @failure_frame: num failures due to no ACK (unused for agg)
+ * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the
+ *     Tx of all the batch. RATE_MCS_*
+ * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK.
+ *     for agg: RTS + CTS + aggregation tx time + block-ack time.
+ *     in usec.
+ * @pa_status: tx power info
+ * @pa_integ_res_a: tx power info
+ * @pa_integ_res_b: tx power info
+ * @pa_integ_res_c: tx power info
+ * @measurement_req_id: tx power info
+ * @tfd_info: TFD information set by the FH
+ * @seq_ctl: sequence control from the Tx cmd
+ * @byte_cnt: byte count from the Tx cmd
+ * @tlc_info: TLC rate info
+ * @ra_tid: bits [3:0] = ra, bits [7:4] = tid
+ * @frame_ctrl: frame control
+ * @status: for non-agg:  frame status TX_STATUS_*
+ *     for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields
+ *     follow this one, up to frame_count.
+ *
+ * After the array of statuses comes the SSN of the SCD. Look at
+ * %iwl_mvm_get_scd_ssn for more details.
+ */
+struct iwl_mvm_tx_resp {
+       u8 frame_count;
+       u8 bt_kill_count;
+       u8 failure_rts;
+       u8 failure_frame;
+       __le32 initial_rate;
+       __le16 wireless_media_time;
+
+       u8 pa_status;
+       u8 pa_integ_res_a[3];
+       u8 pa_integ_res_b[3];
+       u8 pa_integ_res_c[3];
+       __le16 measurement_req_id;
+       __le16 reserved;
+
+       __le32 tfd_info;
+       __le16 seq_ctl;
+       __le16 byte_cnt;
+       u8 tlc_info;
+       u8 ra_tid;
+       __le16 frame_ctrl;
+
+       struct agg_tx_status status;
+} __packed; /* TX_RSP_API_S_VER_3 */
+
+/**
+ * struct iwl_mvm_ba_notif - notifies about reception of BA
+ * ( BA_NOTIF = 0xc5 )
+ * @sta_addr_lo32: lower 32 bits of the MAC address
+ * @sta_addr_hi16: upper 16 bits of the MAC address
+ * @sta_id: Index of recipient (BA-sending) station in fw's station table
+ * @tid: tid of the session
+ * @seq_ctl:
+ * @bitmap: the bitmap of the BA notification as seen in the air
+ * @scd_flow: the tx queue this BA relates to
+ * @scd_ssn: the index of the last contiguously sent packet
+ * @txed: number of Txed frames in this batch
+ * @txed_2_done: number of Acked frames in this batch
+ */
+struct iwl_mvm_ba_notif {
+       __le32 sta_addr_lo32;
+       __le16 sta_addr_hi16;
+       __le16 reserved;
+
+       u8 sta_id;
+       u8 tid;
+       __le16 seq_ctl;
+       __le64 bitmap;
+       __le16 scd_flow;
+       __le16 scd_ssn;
+       u8 txed;
+       u8 txed_2_done;
+       __le16 reserved1;
+} __packed;
+
+/*
+ * struct iwl_mac_beacon_cmd - beacon template command
+ * @tx: the tx commands associated with the beacon frame
+ * @template_id: currently equal to the mac context id of the coresponding
+ *  mac.
+ * @tim_idx: the offset of the tim IE in the beacon
+ * @tim_size: the length of the tim IE
+ * @frame: the template of the beacon frame
+ */
+struct iwl_mac_beacon_cmd {
+       struct iwl_tx_cmd tx;
+       __le32 template_id;
+       __le32 tim_idx;
+       __le32 tim_size;
+       struct ieee80211_hdr frame[0];
+} __packed;
+
+/**
+ * enum iwl_dump_control - dump (flush) control flags
+ * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty
+ *     and the TFD queues are empty.
+ */
+enum iwl_dump_control {
+       DUMP_TX_FIFO_FLUSH      = BIT(1),
+};
+
+/**
+ * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command
+ * @queues_ctl: bitmap of queues to flush
+ * @flush_ctl: control flags
+ * @reserved: reserved
+ */
+struct iwl_tx_path_flush_cmd {
+       __le32 queues_ctl;
+       __le16 flush_ctl;
+       __le16 reserved;
+} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */
+
+/**
+ * iwl_mvm_get_scd_ssn - returns the SSN of the SCD
+ * @tx_resp: the Tx response from the fw (agg or non-agg)
+ *
+ * When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since
+ * it can't know that everything will go well until the end of the AMPDU, it
+ * can't know in advance the number of MPDUs that will be sent in the current
+ * batch. This is why it writes the agg Tx response while it fetches the MPDUs.
+ * Hence, it can't know in advance what the SSN of the SCD will be at the end
+ * of the batch. This is why the SSN of the SCD is written at the end of the
+ * whole struct at a variable offset. This function knows how to cope with the
+ * variable offset and returns the SSN of the SCD.
+ */
+static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm_tx_resp *tx_resp)
+{
+       return le32_to_cpup((__le32 *)&tx_resp->status +
+                           tx_resp->frame_count) & 0xfff;
+}
+
+#endif /* __fw_api_tx_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
new file mode 100644 (file)
index 0000000..9fd49db
--- /dev/null
@@ -0,0 +1,949 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_h__
+#define __fw_api_h__
+
+#include "fw-api-rs.h"
+#include "fw-api-tx.h"
+#include "fw-api-sta.h"
+#include "fw-api-mac.h"
+#include "fw-api-power.h"
+#include "fw-api-d3.h"
+
+/* queue and FIFO numbers by usage */
+enum {
+       IWL_MVM_OFFCHANNEL_QUEUE = 8,
+       IWL_MVM_CMD_QUEUE = 9,
+       IWL_MVM_AUX_QUEUE = 15,
+       IWL_MVM_FIRST_AGG_QUEUE = 16,
+       IWL_MVM_NUM_QUEUES = 20,
+       IWL_MVM_LAST_AGG_QUEUE = IWL_MVM_NUM_QUEUES - 1,
+       IWL_MVM_CMD_FIFO = 7
+};
+
+#define IWL_MVM_STATION_COUNT  16
+
+/* commands */
+enum {
+       MVM_ALIVE = 0x1,
+       REPLY_ERROR = 0x2,
+
+       INIT_COMPLETE_NOTIF = 0x4,
+
+       /* PHY context commands */
+       PHY_CONTEXT_CMD = 0x8,
+       DBG_CFG = 0x9,
+
+       /* station table */
+       ADD_STA = 0x18,
+       REMOVE_STA = 0x19,
+
+       /* TX */
+       TX_CMD = 0x1c,
+       TXPATH_FLUSH = 0x1e,
+       MGMT_MCAST_KEY = 0x1f,
+
+       /* global key */
+       WEP_KEY = 0x20,
+
+       /* MAC and Binding commands */
+       MAC_CONTEXT_CMD = 0x28,
+       TIME_EVENT_CMD = 0x29, /* both CMD and response */
+       TIME_EVENT_NOTIFICATION = 0x2a,
+       BINDING_CONTEXT_CMD = 0x2b,
+       TIME_QUOTA_CMD = 0x2c,
+
+       LQ_CMD = 0x4e,
+
+       /* Calibration */
+       TEMPERATURE_NOTIFICATION = 0x62,
+       CALIBRATION_CFG_CMD = 0x65,
+       CALIBRATION_RES_NOTIFICATION = 0x66,
+       CALIBRATION_COMPLETE_NOTIFICATION = 0x67,
+       RADIO_VERSION_NOTIFICATION = 0x68,
+
+       /* Scan offload */
+       SCAN_OFFLOAD_REQUEST_CMD = 0x51,
+       SCAN_OFFLOAD_ABORT_CMD = 0x52,
+       SCAN_OFFLOAD_COMPLETE = 0x6D,
+       SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
+       SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
+
+       /* Phy */
+       PHY_CONFIGURATION_CMD = 0x6a,
+       CALIB_RES_NOTIF_PHY_DB = 0x6b,
+       /* PHY_DB_CMD = 0x6c, */
+
+       /* Power */
+       POWER_TABLE_CMD = 0x77,
+
+       /* Scanning */
+       SCAN_REQUEST_CMD = 0x80,
+       SCAN_ABORT_CMD = 0x81,
+       SCAN_START_NOTIFICATION = 0x82,
+       SCAN_RESULTS_NOTIFICATION = 0x83,
+       SCAN_COMPLETE_NOTIFICATION = 0x84,
+
+       /* NVM */
+       NVM_ACCESS_CMD = 0x88,
+
+       SET_CALIB_DEFAULT_CMD = 0x8e,
+
+       BEACON_TEMPLATE_CMD = 0x91,
+       TX_ANT_CONFIGURATION_CMD = 0x98,
+       STATISTICS_NOTIFICATION = 0x9d,
+
+       /* RF-KILL commands and notifications */
+       CARD_STATE_CMD = 0xa0,
+       CARD_STATE_NOTIFICATION = 0xa1,
+
+       REPLY_RX_PHY_CMD = 0xc0,
+       REPLY_RX_MPDU_CMD = 0xc1,
+       BA_NOTIF = 0xc5,
+
+       REPLY_DEBUG_CMD = 0xf0,
+       DEBUG_LOG_MSG = 0xf7,
+
+       /* D3 commands/notifications */
+       D3_CONFIG_CMD = 0xd3,
+       PROT_OFFLOAD_CONFIG_CMD = 0xd4,
+       OFFLOADS_QUERY_CMD = 0xd5,
+       REMOTE_WAKE_CONFIG_CMD = 0xd6,
+
+       /* for WoWLAN in particular */
+       WOWLAN_PATTERNS = 0xe0,
+       WOWLAN_CONFIGURATION = 0xe1,
+       WOWLAN_TSC_RSC_PARAM = 0xe2,
+       WOWLAN_TKIP_PARAM = 0xe3,
+       WOWLAN_KEK_KCK_MATERIAL = 0xe4,
+       WOWLAN_GET_STATUSES = 0xe5,
+       WOWLAN_TX_POWER_PER_DB = 0xe6,
+
+       /* and for NetDetect */
+       NET_DETECT_CONFIG_CMD = 0x54,
+       NET_DETECT_PROFILES_QUERY_CMD = 0x56,
+       NET_DETECT_PROFILES_CMD = 0x57,
+       NET_DETECT_HOTSPOTS_CMD = 0x58,
+       NET_DETECT_HOTSPOTS_QUERY_CMD = 0x59,
+
+       REPLY_MAX = 0xff,
+};
+
+/**
+ * struct iwl_cmd_response - generic response struct for most commands
+ * @status: status of the command asked, changes for each one
+ */
+struct iwl_cmd_response {
+       __le32 status;
+};
+
+/*
+ * struct iwl_tx_ant_cfg_cmd
+ * @valid: valid antenna configuration
+ */
+struct iwl_tx_ant_cfg_cmd {
+       __le32 valid;
+} __packed;
+
+/*
+ * Calibration control struct.
+ * Sent as part of the phy configuration command.
+ * @flow_trigger: bitmap for which calibrations to perform according to
+ *             flow triggers.
+ * @event_trigger: bitmap for which calibrations to perform according to
+ *             event triggers.
+ */
+struct iwl_calib_ctrl {
+       __le32 flow_trigger;
+       __le32 event_trigger;
+} __packed;
+
+/* This enum defines the bitmap of various calibrations to enable in both
+ * init ucode and runtime ucode through CALIBRATION_CFG_CMD.
+ */
+enum iwl_calib_cfg {
+       IWL_CALIB_CFG_XTAL_IDX                  = BIT(0),
+       IWL_CALIB_CFG_TEMPERATURE_IDX           = BIT(1),
+       IWL_CALIB_CFG_VOLTAGE_READ_IDX          = BIT(2),
+       IWL_CALIB_CFG_PAPD_IDX                  = BIT(3),
+       IWL_CALIB_CFG_TX_PWR_IDX                = BIT(4),
+       IWL_CALIB_CFG_DC_IDX                    = BIT(5),
+       IWL_CALIB_CFG_BB_FILTER_IDX             = BIT(6),
+       IWL_CALIB_CFG_LO_LEAKAGE_IDX            = BIT(7),
+       IWL_CALIB_CFG_TX_IQ_IDX                 = BIT(8),
+       IWL_CALIB_CFG_TX_IQ_SKEW_IDX            = BIT(9),
+       IWL_CALIB_CFG_RX_IQ_IDX                 = BIT(10),
+       IWL_CALIB_CFG_RX_IQ_SKEW_IDX            = BIT(11),
+       IWL_CALIB_CFG_SENSITIVITY_IDX           = BIT(12),
+       IWL_CALIB_CFG_CHAIN_NOISE_IDX           = BIT(13),
+       IWL_CALIB_CFG_DISCONNECTED_ANT_IDX      = BIT(14),
+       IWL_CALIB_CFG_ANT_COUPLING_IDX          = BIT(15),
+       IWL_CALIB_CFG_DAC_IDX                   = BIT(16),
+       IWL_CALIB_CFG_ABS_IDX                   = BIT(17),
+       IWL_CALIB_CFG_AGC_IDX                   = BIT(18),
+};
+
+/*
+ * Phy configuration command.
+ */
+struct iwl_phy_cfg_cmd {
+       __le32  phy_cfg;
+       struct iwl_calib_ctrl calib_control;
+} __packed;
+
+#define PHY_CFG_RADIO_TYPE     (BIT(0) | BIT(1))
+#define PHY_CFG_RADIO_STEP     (BIT(2) | BIT(3))
+#define PHY_CFG_RADIO_DASH     (BIT(4) | BIT(5))
+#define PHY_CFG_PRODUCT_NUMBER (BIT(6) | BIT(7))
+#define PHY_CFG_TX_CHAIN_A     BIT(8)
+#define PHY_CFG_TX_CHAIN_B     BIT(9)
+#define PHY_CFG_TX_CHAIN_C     BIT(10)
+#define PHY_CFG_RX_CHAIN_A     BIT(12)
+#define PHY_CFG_RX_CHAIN_B     BIT(13)
+#define PHY_CFG_RX_CHAIN_C     BIT(14)
+
+
+/* Target of the NVM_ACCESS_CMD */
+enum {
+       NVM_ACCESS_TARGET_CACHE = 0,
+       NVM_ACCESS_TARGET_OTP = 1,
+       NVM_ACCESS_TARGET_EEPROM = 2,
+};
+
+/**
+ * struct iwl_nvm_access_cmd_ver1 - Request the device to send the NVM.
+ * @op_code: 0 - read, 1 - write.
+ * @target: NVM_ACCESS_TARGET_*. should be 0 for read.
+ * @cache_refresh: 0 - None, 1- NVM.
+ * @offset: offset in the nvm data.
+ * @length: of the chunk.
+ * @data: empty on read, the NVM chunk on write
+ */
+struct iwl_nvm_access_cmd_ver1 {
+       u8 op_code;
+       u8 target;
+       u8 cache_refresh;
+       u8 reserved;
+       __le16 offset;
+       __le16 length;
+       u8 data[];
+} __packed; /* NVM_ACCESS_CMD_API_S_VER_1 */
+
+/**
+ * struct iwl_nvm_access_resp_ver1 - response to NVM_ACCESS_CMD
+ * @offset: the offset in the nvm data
+ * @length: of the chunk
+ * @data: the nvm chunk on when NVM_ACCESS_CMD was read, nothing on write
+ */
+struct iwl_nvm_access_resp_ver1 {
+       __le16 offset;
+       __le16 length;
+       u8 data[];
+} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_1 */
+
+/* Section types for NVM_ACCESS_CMD version 2 */
+enum {
+       NVM_SECTION_TYPE_HW = 0,
+       NVM_SECTION_TYPE_SW,
+       NVM_SECTION_TYPE_PAPD,
+       NVM_SECTION_TYPE_BT,
+       NVM_SECTION_TYPE_CALIBRATION,
+       NVM_SECTION_TYPE_PRODUCTION,
+       NVM_SECTION_TYPE_POST_FCS_CALIB,
+       NVM_NUM_OF_SECTIONS,
+};
+
+/**
+ * struct iwl_nvm_access_cmd_ver2 - Request the device to send an NVM section
+ * @op_code: 0 - read, 1 - write
+ * @target: NVM_ACCESS_TARGET_*
+ * @type: NVM_SECTION_TYPE_*
+ * @offset: offset in bytes into the section
+ * @length: in bytes, to read/write
+ * @data: if write operation, the data to write. On read its empty
+ */
+struct iwl_nvm_access_cmd_ver2 {
+       u8 op_code;
+       u8 target;
+       __le16 type;
+       __le16 offset;
+       __le16 length;
+       u8 data[];
+} __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */
+
+/**
+ * struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD
+ * @offset: offset in bytes into the section
+ * @length: in bytes, either how much was written or read
+ * @type: NVM_SECTION_TYPE_*
+ * @status: 0 for success, fail otherwise
+ * @data: if read operation, the data returned. Empty on write.
+ */
+struct iwl_nvm_access_resp_ver2 {
+       __le16 offset;
+       __le16 length;
+       __le16 type;
+       __le16 status;
+       u8 data[];
+} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_2 */
+
+/* MVM_ALIVE 0x1 */
+
+/* alive response is_valid values */
+#define ALIVE_RESP_UCODE_OK    BIT(0)
+#define ALIVE_RESP_RFKILL      BIT(1)
+
+/* alive response ver_type values */
+enum {
+       FW_TYPE_HW = 0,
+       FW_TYPE_PROT = 1,
+       FW_TYPE_AP = 2,
+       FW_TYPE_WOWLAN = 3,
+       FW_TYPE_TIMING = 4,
+       FW_TYPE_WIPAN = 5
+};
+
+/* alive response ver_subtype values */
+enum {
+       FW_SUBTYPE_FULL_FEATURE = 0,
+       FW_SUBTYPE_BOOTSRAP = 1, /* Not valid */
+       FW_SUBTYPE_REDUCED = 2,
+       FW_SUBTYPE_ALIVE_ONLY = 3,
+       FW_SUBTYPE_WOWLAN = 4,
+       FW_SUBTYPE_AP_SUBTYPE = 5,
+       FW_SUBTYPE_WIPAN = 6,
+       FW_SUBTYPE_INITIALIZE = 9
+};
+
+#define IWL_ALIVE_STATUS_ERR 0xDEAD
+#define IWL_ALIVE_STATUS_OK 0xCAFE
+
+#define IWL_ALIVE_FLG_RFKILL   BIT(0)
+
+struct mvm_alive_resp {
+       __le16 status;
+       __le16 flags;
+       u8 ucode_minor;
+       u8 ucode_major;
+       __le16 id;
+       u8 api_minor;
+       u8 api_major;
+       u8 ver_subtype;
+       u8 ver_type;
+       u8 mac;
+       u8 opt;
+       __le16 reserved2;
+       __le32 timestamp;
+       __le32 error_event_table_ptr;   /* SRAM address for error log */
+       __le32 log_event_table_ptr;     /* SRAM address for event log */
+       __le32 cpu_register_ptr;
+       __le32 dbgm_config_ptr;
+       __le32 alive_counter_ptr;
+       __le32 scd_base_ptr;            /* SRAM address for SCD */
+} __packed; /* ALIVE_RES_API_S_VER_1 */
+
+/* Error response/notification */
+enum {
+       FW_ERR_UNKNOWN_CMD = 0x0,
+       FW_ERR_INVALID_CMD_PARAM = 0x1,
+       FW_ERR_SERVICE = 0x2,
+       FW_ERR_ARC_MEMORY = 0x3,
+       FW_ERR_ARC_CODE = 0x4,
+       FW_ERR_WATCH_DOG = 0x5,
+       FW_ERR_WEP_GRP_KEY_INDX = 0x10,
+       FW_ERR_WEP_KEY_SIZE = 0x11,
+       FW_ERR_OBSOLETE_FUNC = 0x12,
+       FW_ERR_UNEXPECTED = 0xFE,
+       FW_ERR_FATAL = 0xFF
+};
+
+/**
+ * struct iwl_error_resp - FW error indication
+ * ( REPLY_ERROR = 0x2 )
+ * @error_type: one of FW_ERR_*
+ * @cmd_id: the command ID for which the error occured
+ * @bad_cmd_seq_num: sequence number of the erroneous command
+ * @error_service: which service created the error, applicable only if
+ *     error_type = 2, otherwise 0
+ * @timestamp: TSF in usecs.
+ */
+struct iwl_error_resp {
+       __le32 error_type;
+       u8 cmd_id;
+       u8 reserved1;
+       __le16 bad_cmd_seq_num;
+       __le32 error_service;
+       __le64 timestamp;
+} __packed;
+
+
+/* Common PHY, MAC and Bindings definitions */
+
+#define MAX_MACS_IN_BINDING    (3)
+#define MAX_BINDINGS           (4)
+#define AUX_BINDING_INDEX      (3)
+#define MAX_PHYS               (4)
+
+/* Used to extract ID and color from the context dword */
+#define FW_CTXT_ID_POS   (0)
+#define FW_CTXT_ID_MSK   (0xff << FW_CTXT_ID_POS)
+#define FW_CTXT_COLOR_POS (8)
+#define FW_CTXT_COLOR_MSK (0xff << FW_CTXT_COLOR_POS)
+#define FW_CTXT_INVALID          (0xffffffff)
+
+#define FW_CMD_ID_AND_COLOR(_id, _color) ((_id << FW_CTXT_ID_POS) |\
+                                         (_color << FW_CTXT_COLOR_POS))
+
+/* Possible actions on PHYs, MACs and Bindings */
+enum {
+       FW_CTXT_ACTION_STUB = 0,
+       FW_CTXT_ACTION_ADD,
+       FW_CTXT_ACTION_MODIFY,
+       FW_CTXT_ACTION_REMOVE,
+       FW_CTXT_ACTION_NUM
+}; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */
+
+/* Time Events */
+
+/* Time Event types, according to MAC type */
+enum iwl_time_event_type {
+       /* BSS Station Events */
+       TE_BSS_STA_AGGRESSIVE_ASSOC,
+       TE_BSS_STA_ASSOC,
+       TE_BSS_EAP_DHCP_PROT,
+       TE_BSS_QUIET_PERIOD,
+
+       /* P2P Device Events */
+       TE_P2P_DEVICE_DISCOVERABLE,
+       TE_P2P_DEVICE_LISTEN,
+       TE_P2P_DEVICE_ACTION_SCAN,
+       TE_P2P_DEVICE_FULL_SCAN,
+
+       /* P2P Client Events */
+       TE_P2P_CLIENT_AGGRESSIVE_ASSOC,
+       TE_P2P_CLIENT_ASSOC,
+       TE_P2P_CLIENT_QUIET_PERIOD,
+
+       /* P2P GO Events */
+       TE_P2P_GO_ASSOC_PROT,
+       TE_P2P_GO_REPETITIVE_NOA,
+       TE_P2P_GO_CT_WINDOW,
+
+       /* WiDi Sync Events */
+       TE_WIDI_TX_SYNC,
+
+       TE_MAX
+}; /* MAC_EVENT_TYPE_API_E_VER_1 */
+
+/* Time Event dependencies: none, on another TE, or in a specific time */
+enum {
+       TE_INDEPENDENT          = 0,
+       TE_DEP_OTHER            = 1,
+       TE_DEP_TSF              = 2,
+       TE_EVENT_SOCIOPATHIC    = 4,
+}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */
+
+/* When to send Time Event notifications and to whom (internal = FW) */
+enum {
+       TE_NOTIF_NONE = 0,
+       TE_NOTIF_HOST_START = 0x1,
+       TE_NOTIF_HOST_END = 0x2,
+       TE_NOTIF_INTERNAL_START = 0x4,
+       TE_NOTIF_INTERNAL_END = 0x8
+}; /* MAC_EVENT_ACTION_API_E_VER_1 */
+
+/*
+ * @TE_FRAG_NONE: fragmentation of the time event is NOT allowed.
+ * @TE_FRAG_SINGLE: fragmentation of the time event is allowed, but only
+ *  the first fragment is scheduled.
+ * @TE_FRAG_DUAL: fragmentation of the time event is allowed, but only
+ *  the first 2 fragments are scheduled.
+ * @TE_FRAG_ENDLESS: fragmentation of the time event is allowed, and any number
+ *  of fragments are valid.
+ *
+ * Other than the constant defined above, specifying a fragmentation value 'x'
+ * means that the event can be fragmented but only the first 'x' will be
+ * scheduled.
+ */
+enum {
+       TE_FRAG_NONE = 0,
+       TE_FRAG_SINGLE = 1,
+       TE_FRAG_DUAL = 2,
+       TE_FRAG_ENDLESS = 0xffffffff
+};
+
+/* Repeat the time event endlessly (until removed) */
+#define TE_REPEAT_ENDLESS      (0xffffffff)
+/* If a Time Event has bounded repetitions, this is the maximal value */
+#define TE_REPEAT_MAX_MSK      (0x0fffffff)
+/* If a Time Event can be fragmented, this is the max number of fragments */
+#define TE_FRAG_MAX_MSK                (0x0fffffff)
+
+/**
+ * struct iwl_time_event_cmd - configuring Time Events
+ * ( TIME_EVENT_CMD = 0x29 )
+ * @id_and_color: ID and color of the relevant MAC
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @id: this field has two meanings, depending on the action:
+ *     If the action is ADD, then it means the type of event to add.
+ *     For all other actions it is the unique event ID assigned when the
+ *     event was added by the FW.
+ * @apply_time: When to start the Time Event (in GP2)
+ * @max_delay: maximum delay to event's start (apply time), in TU
+ * @depends_on: the unique ID of the event we depend on (if any)
+ * @interval: interval between repetitions, in TU
+ * @interval_reciprocal: 2^32 / interval
+ * @duration: duration of event in TU
+ * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS
+ * @dep_policy: one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF
+ * @is_present: 0 or 1, are we present or absent during the Time Event
+ * @max_frags: maximal number of fragments the Time Event can be divided to
+ * @notify: notifications using TE_NOTIF_* (whom to notify when)
+ */
+struct iwl_time_event_cmd {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       __le32 id;
+       /* MAC_TIME_EVENT_DATA_API_S_VER_1 */
+       __le32 apply_time;
+       __le32 max_delay;
+       __le32 dep_policy;
+       __le32 depends_on;
+       __le32 is_present;
+       __le32 max_frags;
+       __le32 interval;
+       __le32 interval_reciprocal;
+       __le32 duration;
+       __le32 repeat;
+       __le32 notify;
+} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_1 */
+
+/**
+ * struct iwl_time_event_resp - response structure to iwl_time_event_cmd
+ * @status: bit 0 indicates success, all others specify errors
+ * @id: the Time Event type
+ * @unique_id: the unique ID assigned (in ADD) or given (others) to the TE
+ * @id_and_color: ID and color of the relevant MAC
+ */
+struct iwl_time_event_resp {
+       __le32 status;
+       __le32 id;
+       __le32 unique_id;
+       __le32 id_and_color;
+} __packed; /* MAC_TIME_EVENT_RSP_API_S_VER_1 */
+
+/**
+ * struct iwl_time_event_notif - notifications of time event start/stop
+ * ( TIME_EVENT_NOTIFICATION = 0x2a )
+ * @timestamp: action timestamp in GP2
+ * @session_id: session's unique id
+ * @unique_id: unique id of the Time Event itself
+ * @id_and_color: ID and color of the relevant MAC
+ * @action: one of TE_NOTIF_START or TE_NOTIF_END
+ * @status: true if scheduled, false otherwise (not executed)
+ */
+struct iwl_time_event_notif {
+       __le32 timestamp;
+       __le32 session_id;
+       __le32 unique_id;
+       __le32 id_and_color;
+       __le32 action;
+       __le32 status;
+} __packed; /* MAC_TIME_EVENT_NTFY_API_S_VER_1 */
+
+
+/* Bindings and Time Quota */
+
+/**
+ * struct iwl_binding_cmd - configuring bindings
+ * ( BINDING_CONTEXT_CMD = 0x2b )
+ * @id_and_color: ID and color of the relevant Binding
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @macs: array of MAC id and colors which belong to the binding
+ * @phy: PHY id and color which belongs to the binding
+ */
+struct iwl_binding_cmd {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       /* BINDING_DATA_API_S_VER_1 */
+       __le32 macs[MAX_MACS_IN_BINDING];
+       __le32 phy;
+} __packed; /* BINDING_CMD_API_S_VER_1 */
+
+/**
+ * struct iwl_time_quota_data - configuration of time quota per binding
+ * @id_and_color: ID and color of the relevant Binding
+ * @quota: absolute time quota in TU. The scheduler will try to divide the
+ *     remainig quota (after Time Events) according to this quota.
+ * @max_duration: max uninterrupted context duration in TU
+ */
+struct iwl_time_quota_data {
+       __le32 id_and_color;
+       __le32 quota;
+       __le32 max_duration;
+} __packed; /* TIME_QUOTA_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_time_quota_cmd - configuration of time quota between bindings
+ * ( TIME_QUOTA_CMD = 0x2c )
+ * @quotas: allocations per binding
+ */
+struct iwl_time_quota_cmd {
+       struct iwl_time_quota_data quotas[MAX_BINDINGS];
+} __packed; /* TIME_QUOTA_ALLOCATION_CMD_API_S_VER_1 */
+
+
+/* PHY context */
+
+/* Supported bands */
+#define PHY_BAND_5  (0)
+#define PHY_BAND_24 (1)
+
+/* Supported channel width, vary if there is VHT support */
+#define PHY_VHT_CHANNEL_MODE20 (0x0)
+#define PHY_VHT_CHANNEL_MODE40 (0x1)
+#define PHY_VHT_CHANNEL_MODE80 (0x2)
+#define PHY_VHT_CHANNEL_MODE160        (0x3)
+
+/*
+ * Control channel position:
+ * For legacy set bit means upper channel, otherwise lower.
+ * For VHT - bit-2 marks if the control is lower/upper relative to center-freq
+ *   bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0.
+ *                                   center_freq
+ *                                        |
+ * 40Mhz                          |_______|_______|
+ * 80Mhz                  |_______|_______|_______|_______|
+ * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______|
+ * code      011     010     001     000  |  100     101     110    111
+ */
+#define PHY_VHT_CTRL_POS_1_BELOW  (0x0)
+#define PHY_VHT_CTRL_POS_2_BELOW  (0x1)
+#define PHY_VHT_CTRL_POS_3_BELOW  (0x2)
+#define PHY_VHT_CTRL_POS_4_BELOW  (0x3)
+#define PHY_VHT_CTRL_POS_1_ABOVE  (0x4)
+#define PHY_VHT_CTRL_POS_2_ABOVE  (0x5)
+#define PHY_VHT_CTRL_POS_3_ABOVE  (0x6)
+#define PHY_VHT_CTRL_POS_4_ABOVE  (0x7)
+
+/*
+ * @band: PHY_BAND_*
+ * @channel: channel number
+ * @width: PHY_[VHT|LEGACY]_CHANNEL_*
+ * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_*
+ */
+struct iwl_fw_channel_info {
+       u8 band;
+       u8 channel;
+       u8 width;
+       u8 ctrl_pos;
+} __packed;
+
+#define PHY_RX_CHAIN_DRIVER_FORCE_POS  (0)
+#define PHY_RX_CHAIN_DRIVER_FORCE_MSK \
+       (0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS)
+#define PHY_RX_CHAIN_VALID_POS         (1)
+#define PHY_RX_CHAIN_VALID_MSK \
+       (0x7 << PHY_RX_CHAIN_VALID_POS)
+#define PHY_RX_CHAIN_FORCE_SEL_POS     (4)
+#define PHY_RX_CHAIN_FORCE_SEL_MSK \
+       (0x7 << PHY_RX_CHAIN_FORCE_SEL_POS)
+#define PHY_RX_CHAIN_FORCE_MIMO_SEL_POS        (7)
+#define PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK \
+       (0x7 << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS)
+#define PHY_RX_CHAIN_CNT_POS           (10)
+#define PHY_RX_CHAIN_CNT_MSK \
+       (0x3 << PHY_RX_CHAIN_CNT_POS)
+#define PHY_RX_CHAIN_MIMO_CNT_POS      (12)
+#define PHY_RX_CHAIN_MIMO_CNT_MSK \
+       (0x3 << PHY_RX_CHAIN_MIMO_CNT_POS)
+#define PHY_RX_CHAIN_MIMO_FORCE_POS    (14)
+#define PHY_RX_CHAIN_MIMO_FORCE_MSK \
+       (0x1 << PHY_RX_CHAIN_MIMO_FORCE_POS)
+
+/* TODO: fix the value, make it depend on firmware at runtime? */
+#define NUM_PHY_CTX    3
+
+/* TODO: complete missing documentation */
+/**
+ * struct iwl_phy_context_cmd - config of the PHY context
+ * ( PHY_CONTEXT_CMD = 0x8 )
+ * @id_and_color: ID and color of the relevant Binding
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @apply_time: 0 means immediate apply and context switch.
+ *     other value means apply new params after X usecs
+ * @tx_param_color: ???
+ * @channel_info:
+ * @txchain_info: ???
+ * @rxchain_info: ???
+ * @acquisition_data: ???
+ * @dsp_cfg_flags: set to 0
+ */
+struct iwl_phy_context_cmd {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       /* PHY_CONTEXT_DATA_API_S_VER_1 */
+       __le32 apply_time;
+       __le32 tx_param_color;
+       struct iwl_fw_channel_info ci;
+       __le32 txchain_info;
+       __le32 rxchain_info;
+       __le32 acquisition_data;
+       __le32 dsp_cfg_flags;
+} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */
+
+#define IWL_RX_INFO_PHY_CNT 8
+#define IWL_RX_INFO_AGC_IDX 1
+#define IWL_RX_INFO_RSSI_AB_IDX 2
+#define IWL_RX_INFO_RSSI_C_IDX 3
+#define IWL_OFDM_AGC_DB_MSK 0xfe00
+#define IWL_OFDM_AGC_DB_POS 9
+#define IWL_OFDM_RSSI_INBAND_A_MSK 0x00ff
+#define IWL_OFDM_RSSI_ALLBAND_A_MSK 0xff00
+#define IWL_OFDM_RSSI_A_POS 0
+#define IWL_OFDM_RSSI_INBAND_B_MSK 0xff0000
+#define IWL_OFDM_RSSI_ALLBAND_B_MSK 0xff000000
+#define IWL_OFDM_RSSI_B_POS 16
+#define IWL_OFDM_RSSI_INBAND_C_MSK 0x00ff
+#define IWL_OFDM_RSSI_ALLBAND_C_MSK 0xff00
+#define IWL_OFDM_RSSI_C_POS 0
+
+/**
+ * struct iwl_rx_phy_info - phy info
+ * (REPLY_RX_PHY_CMD = 0xc0)
+ * @non_cfg_phy_cnt: non configurable DSP phy data byte count
+ * @cfg_phy_cnt: configurable DSP phy data byte count
+ * @stat_id: configurable DSP phy data set ID
+ * @reserved1:
+ * @system_timestamp: GP2  at on air rise
+ * @timestamp: TSF at on air rise
+ * @beacon_time_stamp: beacon at on-air rise
+ * @phy_flags: general phy flags: band, modulation, ...
+ * @channel: channel number
+ * @non_cfg_phy_buf: for various implementations of non_cfg_phy
+ * @rate_n_flags: RATE_MCS_*
+ * @byte_count: frame's byte-count
+ * @frame_time: frame's time on the air, based on byte count and frame rate
+ *     calculation
+ *
+ * Before each Rx, the device sends this data. It contains PHY information
+ * about the reception of the packet.
+ */
+struct iwl_rx_phy_info {
+       u8 non_cfg_phy_cnt;
+       u8 cfg_phy_cnt;
+       u8 stat_id;
+       u8 reserved1;
+       __le32 system_timestamp;
+       __le64 timestamp;
+       __le32 beacon_time_stamp;
+       __le16 phy_flags;
+       __le16 channel;
+       __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT];
+       __le32 rate_n_flags;
+       __le32 byte_count;
+       __le16 reserved2;
+       __le16 frame_time;
+} __packed;
+
+struct iwl_rx_mpdu_res_start {
+       __le16 byte_count;
+       __le16 reserved;
+} __packed;
+
+/**
+ * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags
+ * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band
+ * @RX_RES_PHY_FLAGS_MOD_CCK:
+ * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short
+ * @RX_RES_PHY_FLAGS_NARROW_BAND:
+ * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received
+ * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU
+ * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame
+ * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble
+ * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame
+ */
+enum iwl_rx_phy_flags {
+       RX_RES_PHY_FLAGS_BAND_24        = BIT(0),
+       RX_RES_PHY_FLAGS_MOD_CCK        = BIT(1),
+       RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2),
+       RX_RES_PHY_FLAGS_NARROW_BAND    = BIT(3),
+       RX_RES_PHY_FLAGS_ANTENNA        = (0x7 << 4),
+       RX_RES_PHY_FLAGS_ANTENNA_POS    = 4,
+       RX_RES_PHY_FLAGS_AGG            = BIT(7),
+       RX_RES_PHY_FLAGS_OFDM_HT        = BIT(8),
+       RX_RES_PHY_FLAGS_OFDM_GF        = BIT(9),
+       RX_RES_PHY_FLAGS_OFDM_VHT       = BIT(10),
+};
+
+/**
+ * enum iwl_mvm_rx_status - written by fw for each Rx packet
+ * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine
+ * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow
+ * @RX_MPDU_RES_STATUS_SRC_STA_FOUND:
+ * @RX_MPDU_RES_STATUS_KEY_VALID:
+ * @RX_MPDU_RES_STATUS_KEY_PARAM_OK:
+ * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed
+ * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked
+ *     in the driver.
+ * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine
+ * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR:  valid for alg = CCM_CMAC or
+ *     alg = CCM only. Checks replay attack for 11w frames. Relevant only if
+ *     %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set.
+ * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted
+ * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP
+ * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM
+ * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP
+ * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC
+ * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted
+ * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm
+ * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted
+ * @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP:
+ * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP:
+ * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT:
+ * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame
+ * @RX_MPDU_RES_STATUS_HASH_INDEX_MSK:
+ * @RX_MPDU_RES_STATUS_STA_ID_MSK:
+ * @RX_MPDU_RES_STATUS_RRF_KILL:
+ * @RX_MPDU_RES_STATUS_FILTERING_MSK:
+ * @RX_MPDU_RES_STATUS2_FILTERING_MSK:
+ */
+enum iwl_mvm_rx_status {
+       RX_MPDU_RES_STATUS_CRC_OK                       = BIT(0),
+       RX_MPDU_RES_STATUS_OVERRUN_OK                   = BIT(1),
+       RX_MPDU_RES_STATUS_SRC_STA_FOUND                = BIT(2),
+       RX_MPDU_RES_STATUS_KEY_VALID                    = BIT(3),
+       RX_MPDU_RES_STATUS_KEY_PARAM_OK                 = BIT(4),
+       RX_MPDU_RES_STATUS_ICV_OK                       = BIT(5),
+       RX_MPDU_RES_STATUS_MIC_OK                       = BIT(6),
+       RX_MPDU_RES_STATUS_TTAK_OK                      = BIT(7),
+       RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR         = BIT(7),
+       RX_MPDU_RES_STATUS_SEC_NO_ENC                   = (0 << 8),
+       RX_MPDU_RES_STATUS_SEC_WEP_ENC                  = (1 << 8),
+       RX_MPDU_RES_STATUS_SEC_CCM_ENC                  = (2 << 8),
+       RX_MPDU_RES_STATUS_SEC_TKIP_ENC                 = (3 << 8),
+       RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC             = (6 << 8),
+       RX_MPDU_RES_STATUS_SEC_ENC_ERR                  = (7 << 8),
+       RX_MPDU_RES_STATUS_SEC_ENC_MSK                  = (7 << 8),
+       RX_MPDU_RES_STATUS_DEC_DONE                     = BIT(11),
+       RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP        = BIT(12),
+       RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP               = BIT(13),
+       RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT               = BIT(14),
+       RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME             = BIT(15),
+       RX_MPDU_RES_STATUS_HASH_INDEX_MSK               = (0x3F0000),
+       RX_MPDU_RES_STATUS_STA_ID_MSK                   = (0x1f000000),
+       RX_MPDU_RES_STATUS_RRF_KILL                     = BIT(29),
+       RX_MPDU_RES_STATUS_FILTERING_MSK                = (0xc00000),
+       RX_MPDU_RES_STATUS2_FILTERING_MSK               = (0xc0000000),
+};
+
+/**
+ * struct iwl_radio_version_notif - information on the radio version
+ * ( RADIO_VERSION_NOTIFICATION = 0x68 )
+ * @radio_flavor:
+ * @radio_step:
+ * @radio_dash:
+ */
+struct iwl_radio_version_notif {
+       __le32 radio_flavor;
+       __le32 radio_step;
+       __le32 radio_dash;
+} __packed; /* RADIO_VERSION_NOTOFICATION_S_VER_1 */
+
+enum iwl_card_state_flags {
+       CARD_ENABLED            = 0x00,
+       HW_CARD_DISABLED        = 0x01,
+       SW_CARD_DISABLED        = 0x02,
+       CT_KILL_CARD_DISABLED   = 0x04,
+       HALT_CARD_DISABLED      = 0x08,
+       CARD_DISABLED_MSK       = 0x0f,
+       CARD_IS_RX_ON           = 0x10,
+};
+
+/**
+ * struct iwl_radio_version_notif - information on the radio version
+ * ( CARD_STATE_NOTIFICATION = 0xa1 )
+ * @flags: %iwl_card_state_flags
+ */
+struct iwl_card_state_notif {
+       __le32 flags;
+} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */
+
+/**
+ * struct iwl_set_calib_default_cmd - set default value for calibration.
+ * ( SET_CALIB_DEFAULT_CMD = 0x8e )
+ * @calib_index: the calibration to set value for
+ * @length: of data
+ * @data: the value to set for the calibration result
+ */
+struct iwl_set_calib_default_cmd {
+       __le16 calib_index;
+       __le16 length;
+       u8 data[0];
+} __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */
+
+#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
new file mode 100644 (file)
index 0000000..90473c2
--- /dev/null
@@ -0,0 +1,644 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <net/mac80211.h>
+
+#include "iwl-trans.h"
+#include "iwl-op-mode.h"
+#include "iwl-fw.h"
+#include "iwl-debug.h"
+#include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */
+#include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */
+#include "iwl-eeprom-parse.h"
+
+#include "mvm.h"
+#include "iwl-phy-db.h"
+
+#define MVM_UCODE_ALIVE_TIMEOUT        HZ
+#define MVM_UCODE_CALIB_TIMEOUT        (2*HZ)
+
+#define UCODE_VALID_OK cpu_to_le32(0x1)
+
+/* Default calibration values for WkP - set to INIT image w/o running */
+static const u8 wkp_calib_values_bb_filter[] = { 0xbf, 0x00, 0x5f, 0x00, 0x2f,
+                                                0x00, 0x18, 0x00 };
+static const u8 wkp_calib_values_rx_dc[] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+                                            0x7f, 0x7f, 0x7f };
+static const u8 wkp_calib_values_tx_lo[] = { 0x00, 0x00, 0x00, 0x00 };
+static const u8 wkp_calib_values_tx_iq[] = { 0xff, 0x00, 0xff, 0x00, 0x00,
+                                            0x00 };
+static const u8 wkp_calib_values_rx_iq[] = { 0xff, 0x00, 0x00, 0x00 };
+static const u8 wkp_calib_values_rx_iq_skew[] = { 0x00, 0x00, 0x01, 0x00 };
+static const u8 wkp_calib_values_tx_iq_skew[] = { 0x01, 0x00, 0x00, 0x00 };
+static const u8 wkp_calib_values_xtal[] = { 0xd2, 0xd2 };
+
+struct iwl_calib_default_data {
+       u16 size;
+       void *data;
+};
+
+#define CALIB_SIZE_N_DATA(_buf) {.size = sizeof(_buf), .data = &_buf}
+
+static const struct iwl_calib_default_data wkp_calib_default_data[12] = {
+       [5] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_dc),
+       [6] = CALIB_SIZE_N_DATA(wkp_calib_values_bb_filter),
+       [7] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_lo),
+       [8] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq),
+       [9] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq_skew),
+       [10] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq),
+       [11] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq_skew),
+};
+
+struct iwl_mvm_alive_data {
+       bool valid;
+       u32 scd_base_addr;
+};
+
+static inline const struct fw_img *
+iwl_get_ucode_image(struct iwl_mvm *mvm, enum iwl_ucode_type ucode_type)
+{
+       if (ucode_type >= IWL_UCODE_TYPE_MAX)
+               return NULL;
+
+       return &mvm->fw->img[ucode_type];
+}
+
+static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
+{
+       struct iwl_tx_ant_cfg_cmd tx_ant_cmd = {
+               .valid = cpu_to_le32(valid_tx_ant),
+       };
+
+       IWL_DEBUG_HC(mvm, "select valid tx ant: %u\n", valid_tx_ant);
+       return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, CMD_SYNC,
+                                   sizeof(tx_ant_cmd), &tx_ant_cmd);
+}
+
+static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
+                        struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_mvm *mvm =
+               container_of(notif_wait, struct iwl_mvm, notif_wait);
+       struct iwl_mvm_alive_data *alive_data = data;
+       struct mvm_alive_resp *palive;
+
+       palive = (void *)pkt->data;
+
+       mvm->error_event_table = le32_to_cpu(palive->error_event_table_ptr);
+       mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr);
+       alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
+
+       alive_data->valid = le16_to_cpu(palive->status) == IWL_ALIVE_STATUS_OK;
+       IWL_DEBUG_FW(mvm, "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n",
+                    le16_to_cpu(palive->status), palive->ver_type,
+                    palive->ver_subtype);
+
+       return true;
+}
+
+static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait,
+                                 struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_phy_db *phy_db = data;
+
+       if (pkt->hdr.cmd != CALIB_RES_NOTIF_PHY_DB) {
+               WARN_ON(pkt->hdr.cmd != INIT_COMPLETE_NOTIF);
+               return true;
+       }
+
+       WARN_ON(iwl_phy_db_set_section(phy_db, pkt, GFP_ATOMIC));
+
+       return false;
+}
+
+static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
+                                        enum iwl_ucode_type ucode_type)
+{
+       struct iwl_notification_wait alive_wait;
+       struct iwl_mvm_alive_data alive_data;
+       const struct fw_img *fw;
+       int ret, i;
+       enum iwl_ucode_type old_type = mvm->cur_ucode;
+       static const u8 alive_cmd[] = { MVM_ALIVE };
+
+       mvm->cur_ucode = ucode_type;
+       fw = iwl_get_ucode_image(mvm, ucode_type);
+
+       mvm->ucode_loaded = false;
+
+       if (!fw)
+               return -EINVAL;
+
+       iwl_init_notification_wait(&mvm->notif_wait, &alive_wait,
+                                  alive_cmd, ARRAY_SIZE(alive_cmd),
+                                  iwl_alive_fn, &alive_data);
+
+       ret = iwl_trans_start_fw(mvm->trans, fw, ucode_type == IWL_UCODE_INIT);
+       if (ret) {
+               mvm->cur_ucode = old_type;
+               iwl_remove_notification(&mvm->notif_wait, &alive_wait);
+               return ret;
+       }
+
+       /*
+        * Some things may run in the background now, but we
+        * just wait for the ALIVE notification here.
+        */
+       ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait,
+                                   MVM_UCODE_ALIVE_TIMEOUT);
+       if (ret) {
+               mvm->cur_ucode = old_type;
+               return ret;
+       }
+
+       if (!alive_data.valid) {
+               IWL_ERR(mvm, "Loaded ucode is not valid!\n");
+               mvm->cur_ucode = old_type;
+               return -EIO;
+       }
+
+       iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
+
+       /*
+        * Note: all the queues are enabled as part of the interface
+        * initialization, but in firmware restart scenarios they
+        * could be stopped, so wake them up. In firmware restart,
+        * mac80211 will have the queues stopped as well until the
+        * reconfiguration completes. During normal startup, they
+        * will be empty.
+        */
+
+       for (i = 0; i < IWL_MAX_HW_QUEUES; i++) {
+               if (i < IWL_MVM_FIRST_AGG_QUEUE && i != IWL_MVM_CMD_QUEUE)
+                       mvm->queue_to_mac80211[i] = i;
+               else
+                       mvm->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
+               atomic_set(&mvm->queue_stop_count[i], 0);
+       }
+
+       mvm->transport_queue_stop = 0;
+
+       mvm->ucode_loaded = true;
+
+       return 0;
+}
+#define IWL_HW_REV_ID_RAINBOW  0x2
+#define IWL_PROJ_TYPE_LHP      0x5
+
+static u32 iwl_mvm_build_phy_cfg(struct iwl_mvm *mvm)
+{
+       struct iwl_nvm_data *data = mvm->nvm_data;
+       /* Temp calls to static definitions, will be changed to CSR calls */
+       u8 hw_rev_id = IWL_HW_REV_ID_RAINBOW;
+       u8 project_type = IWL_PROJ_TYPE_LHP;
+
+       return data->radio_cfg_dash | (data->radio_cfg_step << 2) |
+               (hw_rev_id << 4) | ((project_type & 0x7f) << 6) |
+               (data->valid_tx_ant << 16) | (data->valid_rx_ant << 20);
+}
+
+static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
+{
+       struct iwl_phy_cfg_cmd phy_cfg_cmd;
+       enum iwl_ucode_type ucode_type = mvm->cur_ucode;
+
+       /* Set parameters */
+       phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_build_phy_cfg(mvm));
+       phy_cfg_cmd.calib_control.event_trigger =
+               mvm->fw->default_calib[ucode_type].event_trigger;
+       phy_cfg_cmd.calib_control.flow_trigger =
+               mvm->fw->default_calib[ucode_type].flow_trigger;
+
+       IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n",
+                      phy_cfg_cmd.phy_cfg);
+
+       return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, CMD_SYNC,
+                                   sizeof(phy_cfg_cmd), &phy_cfg_cmd);
+}
+
+/* Starting with the new PHY DB implementation - New calibs are enabled */
+/* Value - 0x405e7 */
+#define IWL_CALIB_DEFAULT_FLOW_INIT    (IWL_CALIB_CFG_XTAL_IDX         |\
+                                        IWL_CALIB_CFG_TEMPERATURE_IDX  |\
+                                        IWL_CALIB_CFG_VOLTAGE_READ_IDX |\
+                                        IWL_CALIB_CFG_DC_IDX           |\
+                                        IWL_CALIB_CFG_BB_FILTER_IDX    |\
+                                        IWL_CALIB_CFG_LO_LEAKAGE_IDX   |\
+                                        IWL_CALIB_CFG_TX_IQ_IDX        |\
+                                        IWL_CALIB_CFG_RX_IQ_IDX        |\
+                                        IWL_CALIB_CFG_AGC_IDX)
+
+#define IWL_CALIB_DEFAULT_EVENT_INIT   0x0
+
+/* Value 0x41567 */
+#define IWL_CALIB_DEFAULT_FLOW_RUN     (IWL_CALIB_CFG_XTAL_IDX         |\
+                                        IWL_CALIB_CFG_TEMPERATURE_IDX  |\
+                                        IWL_CALIB_CFG_VOLTAGE_READ_IDX |\
+                                        IWL_CALIB_CFG_BB_FILTER_IDX    |\
+                                        IWL_CALIB_CFG_DC_IDX           |\
+                                        IWL_CALIB_CFG_TX_IQ_IDX        |\
+                                        IWL_CALIB_CFG_RX_IQ_IDX        |\
+                                        IWL_CALIB_CFG_SENSITIVITY_IDX  |\
+                                        IWL_CALIB_CFG_AGC_IDX)
+
+#define IWL_CALIB_DEFAULT_EVENT_RUN    (IWL_CALIB_CFG_XTAL_IDX         |\
+                                        IWL_CALIB_CFG_TEMPERATURE_IDX  |\
+                                        IWL_CALIB_CFG_VOLTAGE_READ_IDX |\
+                                        IWL_CALIB_CFG_TX_PWR_IDX       |\
+                                        IWL_CALIB_CFG_DC_IDX           |\
+                                        IWL_CALIB_CFG_TX_IQ_IDX        |\
+                                        IWL_CALIB_CFG_SENSITIVITY_IDX)
+
+/*
+ * Sets the calibrations trigger values that will be sent to the FW for runtime
+ * and init calibrations.
+ * The ones given in the FW TLV are not correct.
+ */
+static void iwl_set_default_calib_trigger(struct iwl_mvm *mvm)
+{
+       struct iwl_tlv_calib_ctrl default_calib;
+
+       /*
+        * WkP FW TLV calib bits are wrong, overwrite them.
+        * This defines the dynamic calibrations which are implemented in the
+        * uCode both for init(flow) calculation and event driven calibs.
+        */
+
+       /* Init Image */
+       default_calib.event_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_EVENT_INIT);
+       default_calib.flow_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_FLOW_INIT);
+
+       if (default_calib.event_trigger !=
+           mvm->fw->default_calib[IWL_UCODE_INIT].event_trigger)
+               IWL_ERR(mvm,
+                       "Updating the event calib for INIT image: 0x%x -> 0x%x\n",
+                       mvm->fw->default_calib[IWL_UCODE_INIT].event_trigger,
+                       default_calib.event_trigger);
+       if (default_calib.flow_trigger !=
+           mvm->fw->default_calib[IWL_UCODE_INIT].flow_trigger)
+               IWL_ERR(mvm,
+                       "Updating the flow calib for INIT image: 0x%x -> 0x%x\n",
+                       mvm->fw->default_calib[IWL_UCODE_INIT].flow_trigger,
+                       default_calib.flow_trigger);
+
+       memcpy((void *)&mvm->fw->default_calib[IWL_UCODE_INIT],
+              &default_calib, sizeof(struct iwl_tlv_calib_ctrl));
+       IWL_ERR(mvm,
+               "Setting uCode init calibrations event 0x%x, trigger 0x%x\n",
+               default_calib.event_trigger,
+               default_calib.flow_trigger);
+
+       /* Run time image */
+       default_calib.event_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_EVENT_RUN);
+       default_calib.flow_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_FLOW_RUN);
+
+       if (default_calib.event_trigger !=
+           mvm->fw->default_calib[IWL_UCODE_REGULAR].event_trigger)
+               IWL_ERR(mvm,
+                       "Updating the event calib for RT image: 0x%x -> 0x%x\n",
+                       mvm->fw->default_calib[IWL_UCODE_REGULAR].event_trigger,
+                       default_calib.event_trigger);
+       if (default_calib.flow_trigger !=
+           mvm->fw->default_calib[IWL_UCODE_REGULAR].flow_trigger)
+               IWL_ERR(mvm,
+                       "Updating the flow calib for RT image: 0x%x -> 0x%x\n",
+                       mvm->fw->default_calib[IWL_UCODE_REGULAR].flow_trigger,
+                       default_calib.flow_trigger);
+
+       memcpy((void *)&mvm->fw->default_calib[IWL_UCODE_REGULAR],
+              &default_calib, sizeof(struct iwl_tlv_calib_ctrl));
+       IWL_ERR(mvm,
+               "Setting uCode runtime calibs event 0x%x, trigger 0x%x\n",
+               default_calib.event_trigger,
+               default_calib.flow_trigger);
+}
+
+static int iwl_set_default_calibrations(struct iwl_mvm *mvm)
+{
+       u8 cmd_raw[16]; /* holds the variable size commands */
+       struct iwl_set_calib_default_cmd *cmd =
+               (struct iwl_set_calib_default_cmd *)cmd_raw;
+       int ret, i;
+
+       /* Setting default values for calibrations we don't run */
+       for (i = 0; i < ARRAY_SIZE(wkp_calib_default_data); i++) {
+               u16 cmd_len;
+
+               if (wkp_calib_default_data[i].size == 0)
+                       continue;
+
+               memset(cmd_raw, 0, sizeof(cmd_raw));
+               cmd_len = wkp_calib_default_data[i].size + sizeof(cmd);
+               cmd->calib_index = cpu_to_le16(i);
+               cmd->length = cpu_to_le16(wkp_calib_default_data[i].size);
+               if (WARN_ONCE(cmd_len > sizeof(cmd_raw),
+                             "Need to enlarge cmd_raw to %d\n", cmd_len))
+                       break;
+               memcpy(cmd->data, wkp_calib_default_data[i].data,
+                      wkp_calib_default_data[i].size);
+               ret = iwl_mvm_send_cmd_pdu(mvm, SET_CALIB_DEFAULT_CMD, 0,
+                                          sizeof(*cmd) +
+                                          wkp_calib_default_data[i].size,
+                                          cmd);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
+{
+       struct iwl_notification_wait calib_wait;
+       static const u8 init_complete[] = {
+               INIT_COMPLETE_NOTIF,
+               CALIB_RES_NOTIF_PHY_DB
+       };
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (mvm->init_ucode_run)
+               return 0;
+
+       iwl_init_notification_wait(&mvm->notif_wait,
+                                  &calib_wait,
+                                  init_complete,
+                                  ARRAY_SIZE(init_complete),
+                                  iwl_wait_phy_db_entry,
+                                  mvm->phy_db);
+
+       /* Will also start the device */
+       ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_INIT);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to start INIT ucode: %d\n", ret);
+               goto error;
+       }
+
+       if (read_nvm) {
+               /* Read nvm */
+               ret = iwl_nvm_init(mvm);
+               if (ret) {
+                       IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
+                       goto error;
+               }
+       }
+
+       ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
+       WARN_ON(ret);
+
+       /* Override the calibrations from TLV and the const of fw */
+       iwl_set_default_calib_trigger(mvm);
+
+       /* WkP doesn't have all calibrations, need to set default values */
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+               ret = iwl_set_default_calibrations(mvm);
+               if (ret)
+                       goto error;
+       }
+
+       /*
+        * Send phy configurations command to init uCode
+        * to start the 16.0 uCode init image internal calibrations.
+        */
+       ret = iwl_send_phy_cfg_cmd(mvm);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n",
+                       ret);
+               goto error;
+       }
+
+       /*
+        * Some things may run in the background now, but we
+        * just wait for the calibration complete notification.
+        */
+       ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait,
+                       MVM_UCODE_CALIB_TIMEOUT);
+       if (!ret)
+               mvm->init_ucode_run = true;
+       goto out;
+
+error:
+       iwl_remove_notification(&mvm->notif_wait, &calib_wait);
+out:
+       if (!iwlmvm_mod_params.init_dbg) {
+               iwl_trans_stop_device(mvm->trans);
+       } else if (!mvm->nvm_data) {
+               /* we want to debug INIT and we have no NVM - fake */
+               mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
+                                       sizeof(struct ieee80211_channel) +
+                                       sizeof(struct ieee80211_rate),
+                                       GFP_KERNEL);
+               if (!mvm->nvm_data)
+                       return -ENOMEM;
+               mvm->nvm_data->valid_rx_ant = 1;
+               mvm->nvm_data->valid_tx_ant = 1;
+               mvm->nvm_data->bands[0].channels = mvm->nvm_data->channels;
+               mvm->nvm_data->bands[0].n_channels = 1;
+               mvm->nvm_data->bands[0].n_bitrates = 1;
+               mvm->nvm_data->bands[0].bitrates =
+                       (void *)mvm->nvm_data->channels + 1;
+               mvm->nvm_data->bands[0].bitrates->hw_value = 10;
+       }
+
+       return ret;
+}
+
+#define UCODE_CALIB_TIMEOUT    (2*HZ)
+
+int iwl_mvm_up(struct iwl_mvm *mvm)
+{
+       int ret, i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_trans_start_hw(mvm->trans);
+       if (ret)
+               return ret;
+
+       /* If we were in RFKILL during module loading, load init ucode now */
+       if (!mvm->init_ucode_run) {
+               ret = iwl_run_init_mvm_ucode(mvm, false);
+               if (ret && !iwlmvm_mod_params.init_dbg) {
+                       IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret);
+                       goto error;
+               }
+       }
+
+       if (iwlmvm_mod_params.init_dbg)
+               return 0;
+
+       ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
+               goto error;
+       }
+
+       ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant);
+       if (ret)
+               goto error;
+
+       /* Send phy db control command and then phy db calibration*/
+       ret = iwl_send_phy_db_data(mvm->phy_db);
+       if (ret)
+               goto error;
+
+       ret = iwl_send_phy_cfg_cmd(mvm);
+       if (ret)
+               goto error;
+
+       /* init the fw <-> mac80211 STA mapping */
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
+               RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
+
+       /* Add auxiliary station for scanning */
+       ret = iwl_mvm_add_aux_sta(mvm);
+       if (ret)
+               goto error;
+
+       IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
+
+       return 0;
+ error:
+       iwl_trans_stop_device(mvm->trans);
+       return ret;
+}
+
+int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm)
+{
+       int ret, i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_trans_start_hw(mvm->trans);
+       if (ret)
+               return ret;
+
+       ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_WOWLAN);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to start WoWLAN firmware: %d\n", ret);
+               goto error;
+       }
+
+       ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant);
+       if (ret)
+               goto error;
+
+       /* Send phy db control command and then phy db calibration*/
+       ret = iwl_send_phy_db_data(mvm->phy_db);
+       if (ret)
+               goto error;
+
+       ret = iwl_send_phy_cfg_cmd(mvm);
+       if (ret)
+               goto error;
+
+       /* init the fw <-> mac80211 STA mapping */
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
+               RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
+
+       /* Add auxiliary station for scanning */
+       ret = iwl_mvm_add_aux_sta(mvm);
+       if (ret)
+               goto error;
+
+       return 0;
+ error:
+       iwl_trans_stop_device(mvm->trans);
+       return ret;
+}
+
+int iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
+                                   struct iwl_rx_cmd_buffer *rxb,
+                                   struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_card_state_notif *card_state_notif = (void *)pkt->data;
+       u32 flags = le32_to_cpu(card_state_notif->flags);
+
+       IWL_DEBUG_RF_KILL(mvm, "Card state received: HW:%s SW:%s CT:%s\n",
+                         (flags & HW_CARD_DISABLED) ? "Kill" : "On",
+                         (flags & SW_CARD_DISABLED) ? "Kill" : "On",
+                         (flags & CT_KILL_CARD_DISABLED) ?
+                         "Reached" : "Not reached");
+
+       if (flags & CARD_DISABLED_MSK)
+               iwl_write32(mvm->trans, CSR_UCODE_DRV_GP1_SET,
+                           CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+
+       return 0;
+}
+
+int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                        struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_radio_version_notif *radio_version = (void *)pkt->data;
+
+       /* TODO: what to do with that? */
+       IWL_DEBUG_INFO(mvm,
+                      "Radio version: flavor: 0x%08x, step 0x%08x, dash 0x%08x\n",
+                      le32_to_cpu(radio_version->radio_flavor),
+                      le32_to_cpu(radio_version->radio_step),
+                      le32_to_cpu(radio_version->radio_dash));
+       return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/led.c b/drivers/net/wireless/iwlwifi/mvm/led.c
new file mode 100644 (file)
index 0000000..011906e
--- /dev/null
@@ -0,0 +1,134 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/leds.h>
+#include "iwl-io.h"
+#include "iwl-csr.h"
+#include "mvm.h"
+
+/* Set led register on */
+static void iwl_mvm_led_enable(struct iwl_mvm *mvm)
+{
+       iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON);
+}
+
+/* Set led register off */
+static void iwl_mvm_led_disable(struct iwl_mvm *mvm)
+{
+       iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_OFF);
+}
+
+static void iwl_led_brightness_set(struct led_classdev *led_cdev,
+                                  enum led_brightness brightness)
+{
+       struct iwl_mvm *mvm = container_of(led_cdev, struct iwl_mvm, led);
+       if (brightness > 0)
+               iwl_mvm_led_enable(mvm);
+       else
+               iwl_mvm_led_disable(mvm);
+}
+
+int iwl_mvm_leds_init(struct iwl_mvm *mvm)
+{
+       int mode = iwlwifi_mod_params.led_mode;
+       int ret;
+
+       switch (mode) {
+       case IWL_LED_DEFAULT:
+       case IWL_LED_RF_STATE:
+               mode = IWL_LED_RF_STATE;
+               break;
+       case IWL_LED_DISABLE:
+               IWL_INFO(mvm, "Led disabled\n");
+               return 0;
+       default:
+               return -EINVAL;
+       };
+
+       mvm->led.name = kasprintf(GFP_KERNEL, "%s-led",
+                                  wiphy_name(mvm->hw->wiphy));
+       mvm->led.brightness_set = iwl_led_brightness_set;
+       mvm->led.max_brightness = 1;
+
+       if (mode == IWL_LED_RF_STATE)
+               mvm->led.default_trigger =
+                       ieee80211_get_radio_led_name(mvm->hw);
+
+       ret = led_classdev_register(mvm->trans->dev, &mvm->led);
+       if (ret) {
+               kfree(mvm->led.name);
+               IWL_INFO(mvm, "Failed to enable led\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
+{
+       if (iwlwifi_mod_params.led_mode == IWL_LED_DISABLE)
+               return;
+
+       led_classdev_unregister(&mvm->led);
+       kfree(mvm->led.name);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
new file mode 100644 (file)
index 0000000..c08a17a
--- /dev/null
@@ -0,0 +1,951 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include "iwl-io.h"
+#include "iwl-prph.h"
+#include "fw-api.h"
+#include "mvm.h"
+
+const u8 iwl_mvm_ac_to_tx_fifo[] = {
+       IWL_MVM_TX_FIFO_BK,
+       IWL_MVM_TX_FIFO_BE,
+       IWL_MVM_TX_FIFO_VI,
+       IWL_MVM_TX_FIFO_VO,
+};
+
+struct iwl_mvm_mac_iface_iterator_data {
+       struct iwl_mvm *mvm;
+       struct ieee80211_vif *vif;
+       unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)];
+       unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)];
+       unsigned long used_hw_queues[BITS_TO_LONGS(IWL_MVM_FIRST_AGG_QUEUE)];
+       enum iwl_tsf_id preferred_tsf;
+       bool found_vif;
+};
+
+static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
+                                      struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_mac_iface_iterator_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u32 ac;
+
+       /* Iterator may already find the interface being added -- skip it */
+       if (vif == data->vif) {
+               data->found_vif = true;
+               return;
+       }
+
+       /* Mark the queues used by the vif */
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+               if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
+                       __set_bit(vif->hw_queue[ac], data->used_hw_queues);
+
+       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
+               __set_bit(vif->cab_queue, data->used_hw_queues);
+
+       /*
+        * Mark MAC IDs as used by clearing the available bit, and
+        * (below) mark TSFs as used if their existing use is not
+        * compatible with the new interface type.
+        * No locking or atomic bit operations are needed since the
+        * data is on the stack of the caller function.
+        */
+       __clear_bit(mvmvif->id, data->available_mac_ids);
+
+       /*
+        * The TSF is a hardware/firmware resource, there are 4 and
+        * the driver should assign and free them as needed. However,
+        * there are cases where 2 MACs should share the same TSF ID
+        * for the purpose of clock sync, an optimization to avoid
+        * clock drift causing overlapping TBTTs/DTIMs for a GO and
+        * client in the system.
+        *
+        * The firmware will decide according to the MAC type which
+        * will be the master and slave. Clients that need to sync
+        * with a remote station will be the master, and an AP or GO
+        * will be the slave.
+        *
+        * Depending on the new interface type it can be slaved to
+        * or become the master of an existing interface.
+        */
+       switch (data->vif->type) {
+       case NL80211_IFTYPE_STATION:
+               /*
+                * The new interface is client, so if the existing one
+                * we're iterating is an AP, the TSF should be used to
+                * avoid drift between the new client and existing AP,
+                * the existing AP will get drift updates from the new
+                * client context in this case
+                */
+               if (vif->type == NL80211_IFTYPE_AP) {
+                       if (data->preferred_tsf == NUM_TSF_IDS &&
+                           test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+                               data->preferred_tsf = mvmvif->tsf_id;
+                       return;
+               }
+               break;
+       case NL80211_IFTYPE_AP:
+               /*
+                * The new interface is AP/GO, so should get drift
+                * updates from an existing client or use the same
+                * TSF as an existing GO. There's no drift between
+                * TSFs internally but if they used different TSFs
+                * then a new client MAC could update one of them
+                * and cause drift that way.
+                */
+               if (vif->type == NL80211_IFTYPE_STATION ||
+                   vif->type == NL80211_IFTYPE_AP) {
+                       if (data->preferred_tsf == NUM_TSF_IDS &&
+                           test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+                               data->preferred_tsf = mvmvif->tsf_id;
+                       return;
+               }
+               break;
+       default:
+               /*
+                * For all other interface types there's no need to
+                * take drift into account. Either they're exclusive
+                * like IBSS and monitor, or we don't care much about
+                * their TSF (like P2P Device), but we won't be able
+                * to share the TSF resource.
+                */
+               break;
+       }
+
+       /*
+        * Unless we exited above, we can't share the TSF resource
+        * that the virtual interface we're iterating over is using
+        * with the new one, so clear the available bit and if this
+        * was the preferred one, reset that as well.
+        */
+       __clear_bit(mvmvif->tsf_id, data->available_tsf_ids);
+
+       if (data->preferred_tsf == mvmvif->tsf_id)
+               data->preferred_tsf = NUM_TSF_IDS;
+}
+
+/*
+ * Get the mask of the queus used by the vif
+ */
+u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
+                               struct ieee80211_vif *vif)
+{
+       u32 qmask, ac;
+
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
+               return BIT(IWL_OFFCHANNEL_QUEUE);
+
+       qmask = (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) ?
+               BIT(vif->cab_queue) : 0;
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+               if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
+                       qmask |= BIT(vif->hw_queue[ac]);
+
+       return qmask;
+}
+
+static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
+                                              struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_mac_iface_iterator_data data = {
+               .mvm = mvm,
+               .vif = vif,
+               .available_mac_ids = { (1 << NUM_MAC_INDEX_DRIVER) - 1 },
+               .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 },
+               /* no preference yet */
+               .preferred_tsf = NUM_TSF_IDS,
+               .used_hw_queues = {
+                       BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
+                       BIT(IWL_MVM_AUX_QUEUE) |
+                       BIT(IWL_MVM_CMD_QUEUE)
+               },
+               .found_vif = false,
+       };
+       u32 ac;
+       int ret;
+
+       /*
+        * Allocate a MAC ID and a TSF for this MAC, along with the queues
+        * and other resources.
+        */
+
+       /*
+        * Before the iterator, we start with all MAC IDs and TSFs available.
+        *
+        * During iteration, all MAC IDs are cleared that are in use by other
+        * virtual interfaces, and all TSF IDs are cleared that can't be used
+        * by this new virtual interface because they're used by an interface
+        * that can't share it with the new one.
+        * At the same time, we check if there's a preferred TSF in the case
+        * that we should share it with another interface.
+        */
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+               iwl_mvm_mac_iface_iterator, &data);
+
+       /*
+        * In the case we're getting here during resume, it's similar to
+        * firmware restart, and with RESUME_ALL the iterator will find
+        * the vif being added already.
+        * We don't want to reassign any IDs in either case since doing
+        * so would probably assign different IDs (as interfaces aren't
+        * necessarily added in the same order), but the old IDs were
+        * preserved anyway, so skip ID assignment for both resume and
+        * recovery.
+        */
+       if (data.found_vif)
+               return 0;
+
+       /* Therefore, in recovery, we can't get here */
+       WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
+
+       mvmvif->id = find_first_bit(data.available_mac_ids,
+                                   NUM_MAC_INDEX_DRIVER);
+       if (mvmvif->id == NUM_MAC_INDEX_DRIVER) {
+               IWL_ERR(mvm, "Failed to init MAC context - no free ID!\n");
+               ret = -EIO;
+               goto exit_fail;
+       }
+
+       if (data.preferred_tsf != NUM_TSF_IDS)
+               mvmvif->tsf_id = data.preferred_tsf;
+       else
+               mvmvif->tsf_id = find_first_bit(data.available_tsf_ids,
+                                               NUM_TSF_IDS);
+       if (mvmvif->tsf_id == NUM_TSF_IDS) {
+               IWL_ERR(mvm, "Failed to init MAC context - no free TSF!\n");
+               ret = -EIO;
+               goto exit_fail;
+       }
+
+       mvmvif->color = 0;
+
+       /* No need to allocate data queues to P2P Device MAC.*/
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE;
+
+               return 0;
+       }
+
+       /* Find available queues, and allocate them to the ACs */
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               u8 queue = find_first_zero_bit(data.used_hw_queues,
+                                              IWL_MVM_FIRST_AGG_QUEUE);
+
+               if (queue >= IWL_MVM_FIRST_AGG_QUEUE) {
+                       IWL_ERR(mvm, "Failed to allocate queue\n");
+                       ret = -EIO;
+                       goto exit_fail;
+               }
+
+               __set_bit(queue, data.used_hw_queues);
+               vif->hw_queue[ac] = queue;
+       }
+
+       /* Allocate the CAB queue for softAP and GO interfaces */
+       if (vif->type == NL80211_IFTYPE_AP) {
+               u8 queue = find_first_zero_bit(data.used_hw_queues,
+                                              IWL_MVM_FIRST_AGG_QUEUE);
+
+               if (queue >= IWL_MVM_FIRST_AGG_QUEUE) {
+                       IWL_ERR(mvm, "Failed to allocate cab queue\n");
+                       ret = -EIO;
+                       goto exit_fail;
+               }
+
+               vif->cab_queue = queue;
+       } else {
+               vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+       }
+
+       mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT;
+       mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+
+       INIT_LIST_HEAD(&mvmvif->time_event_data.list);
+       mvmvif->time_event_data.id = TE_MAX;
+
+       return 0;
+
+exit_fail:
+       memset(mvmvif, 0, sizeof(struct iwl_mvm_vif));
+       memset(vif->hw_queue, IEEE80211_INVAL_HW_QUEUE, sizeof(vif->hw_queue));
+       vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+       return ret;
+}
+
+int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       u32 ac;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_mvm_mac_ctxt_allocate_resources(mvm, vif);
+       if (ret)
+               return ret;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_P2P_DEVICE:
+               iwl_trans_ac_txq_enable(mvm->trans, IWL_MVM_OFFCHANNEL_QUEUE,
+                                       IWL_MVM_TX_FIFO_VO);
+               break;
+       case NL80211_IFTYPE_AP:
+               iwl_trans_ac_txq_enable(mvm->trans, vif->cab_queue,
+                                       IWL_MVM_TX_FIFO_VO);
+               /* fall through */
+       default:
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       iwl_trans_ac_txq_enable(mvm->trans, vif->hw_queue[ac],
+                                               iwl_mvm_ac_to_tx_fifo[ac]);
+               break;
+       }
+
+       return 0;
+}
+
+void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       int ac;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_P2P_DEVICE:
+               iwl_trans_txq_disable(mvm->trans, IWL_MVM_OFFCHANNEL_QUEUE);
+               break;
+       case NL80211_IFTYPE_AP:
+               iwl_trans_txq_disable(mvm->trans, vif->cab_queue);
+               /* fall through */
+       default:
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       iwl_trans_txq_disable(mvm->trans, vif->hw_queue[ac]);
+       }
+}
+
+static void iwl_mvm_ack_rates(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             enum ieee80211_band band,
+                             u8 *cck_rates, u8 *ofdm_rates)
+{
+       struct ieee80211_supported_band *sband;
+       unsigned long basic = vif->bss_conf.basic_rates;
+       int lowest_present_ofdm = 100;
+       int lowest_present_cck = 100;
+       u8 cck = 0;
+       u8 ofdm = 0;
+       int i;
+
+       sband = mvm->hw->wiphy->bands[band];
+
+       for_each_set_bit(i, &basic, BITS_PER_LONG) {
+               int hw = sband->bitrates[i].hw_value;
+               if (hw >= IWL_FIRST_OFDM_RATE) {
+                       ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE);
+                       if (lowest_present_ofdm > hw)
+                               lowest_present_ofdm = hw;
+               } else {
+                       BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
+
+                       cck |= BIT(hw);
+                       if (lowest_present_cck > hw)
+                               lowest_present_cck = hw;
+               }
+       }
+
+       /*
+        * Now we've got the basic rates as bitmaps in the ofdm and cck
+        * variables. This isn't sufficient though, as there might not
+        * be all the right rates in the bitmap. E.g. if the only basic
+        * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
+        * and 6 Mbps because the 802.11-2007 standard says in 9.6:
+        *
+        *    [...] a STA responding to a received frame shall transmit
+        *    its Control Response frame [...] at the highest rate in the
+        *    BSSBasicRateSet parameter that is less than or equal to the
+        *    rate of the immediately previous frame in the frame exchange
+        *    sequence ([...]) and that is of the same modulation class
+        *    ([...]) as the received frame. If no rate contained in the
+        *    BSSBasicRateSet parameter meets these conditions, then the
+        *    control frame sent in response to a received frame shall be
+        *    transmitted at the highest mandatory rate of the PHY that is
+        *    less than or equal to the rate of the received frame, and
+        *    that is of the same modulation class as the received frame.
+        *
+        * As a consequence, we need to add all mandatory rates that are
+        * lower than all of the basic rates to these bitmaps.
+        */
+
+       if (IWL_RATE_24M_INDEX < lowest_present_ofdm)
+               ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE;
+       if (IWL_RATE_12M_INDEX < lowest_present_ofdm)
+               ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE;
+       /* 6M already there or needed so always add */
+       ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE;
+
+       /*
+        * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP.
+        * Note, however:
+        *  - if no CCK rates are basic, it must be ERP since there must
+        *    be some basic rates at all, so they're OFDM => ERP PHY
+        *    (or we're in 5 GHz, and the cck bitmap will never be used)
+        *  - if 11M is a basic rate, it must be ERP as well, so add 5.5M
+        *  - if 5.5M is basic, 1M and 2M are mandatory
+        *  - if 2M is basic, 1M is mandatory
+        *  - if 1M is basic, that's the only valid ACK rate.
+        * As a consequence, it's not as complicated as it sounds, just add
+        * any lower rates to the ACK rate bitmap.
+        */
+       if (IWL_RATE_11M_INDEX < lowest_present_cck)
+               cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE;
+       if (IWL_RATE_5M_INDEX < lowest_present_cck)
+               cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE;
+       if (IWL_RATE_2M_INDEX < lowest_present_cck)
+               cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE;
+       /* 1M already there or needed so always add */
+       cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE;
+
+       *cck_rates = cck;
+       *ofdm_rates = ofdm;
+}
+
+static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       struct iwl_mac_ctx_cmd *cmd,
+                                       u32 action)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct ieee80211_chanctx_conf *chanctx;
+       u8 cck_ack_rates, ofdm_ack_rates;
+       int i;
+
+       cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                           mvmvif->color));
+       cmd->action = cpu_to_le32(action);
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               if (vif->p2p)
+                       cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_STA);
+               else
+                       cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_BSS_STA);
+               break;
+       case NL80211_IFTYPE_AP:
+               cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_GO);
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_LISTENER);
+               break;
+       case NL80211_IFTYPE_P2P_DEVICE:
+               cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_DEVICE);
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_IBSS);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+       }
+
+       cmd->tsf_id = cpu_to_le32(mvmvif->tsf_id);
+
+       memcpy(cmd->node_addr, vif->addr, ETH_ALEN);
+       if (vif->bss_conf.bssid)
+               memcpy(cmd->bssid_addr, vif->bss_conf.bssid, ETH_ALEN);
+       else
+               eth_broadcast_addr(cmd->bssid_addr);
+
+       rcu_read_lock();
+       chanctx = rcu_dereference(vif->chanctx_conf);
+       iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band
+                                           : IEEE80211_BAND_2GHZ,
+                         &cck_ack_rates, &ofdm_ack_rates);
+       rcu_read_unlock();
+
+       cmd->cck_rates = cpu_to_le32((u32)cck_ack_rates);
+       cmd->ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates);
+
+       cmd->cck_short_preamble =
+               cpu_to_le32(vif->bss_conf.use_short_preamble ?
+                           MAC_FLG_SHORT_PREAMBLE : 0);
+       cmd->short_slot =
+               cpu_to_le32(vif->bss_conf.use_short_slot ?
+                           MAC_FLG_SHORT_SLOT : 0);
+
+       for (i = 0; i < AC_NUM; i++) {
+               cmd->ac[i].cw_min = cpu_to_le16(mvmvif->queue_params[i].cw_min);
+               cmd->ac[i].cw_max = cpu_to_le16(mvmvif->queue_params[i].cw_max);
+               cmd->ac[i].aifsn = mvmvif->queue_params[i].aifs;
+               cmd->ac[i].edca_txop =
+                       cpu_to_le16(mvmvif->queue_params[i].txop * 32);
+               cmd->ac[i].fifos_mask = BIT(iwl_mvm_ac_to_tx_fifo[i]);
+       }
+
+       if (vif->bss_conf.qos)
+               cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
+
+       if (vif->bss_conf.use_cts_prot)
+               cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT |
+                                                    MAC_PROT_FLG_SELF_CTS_EN);
+
+       /*
+        * I think that we should enable these 2 flags regardless the HT PROT
+        * fields in the HT IE, but I am not sure. Someone knows whom to ask?...
+        */
+       if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
+               cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN);
+               cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_HT_PROT |
+                                                    MAC_PROT_FLG_FAT_PROT);
+       }
+
+       cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP);
+}
+
+static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
+                                    struct iwl_mac_ctx_cmd *cmd)
+{
+       int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC,
+                                      sizeof(*cmd), cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n",
+                       le32_to_cpu(cmd->action), ret);
+       return ret;
+}
+
+/*
+ * Fill the specific data for mac context of type station or p2p client
+ */
+static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         struct iwl_mac_data_sta *ctxt_sta)
+{
+       ctxt_sta->is_assoc = cpu_to_le32(vif->bss_conf.assoc ? 1 : 0);
+
+       ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int);
+       ctxt_sta->bi_reciprocal =
+               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
+       ctxt_sta->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
+                                             vif->bss_conf.dtim_period);
+       ctxt_sta->dtim_reciprocal =
+               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
+                                              vif->bss_conf.dtim_period));
+
+       ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval);
+       ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_station(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_STATION || vif->p2p);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       /* Fill the data specific for station mode */
+       iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta);
+
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_p2p_client(struct iwl_mvm *mvm,
+                                          struct ieee80211_vif *vif,
+                                          u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_STATION || !vif->p2p);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       /* Fill the data specific for station mode */
+       iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.p2p_sta.sta);
+
+       cmd.p2p_sta.ctwin = cpu_to_le32(vif->bss_conf.p2p_ctwindow);
+
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif,
+                                        u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
+
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+       /* No other data to be filled */
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+struct iwl_mvm_go_iterator_data {
+       bool go_active;
+};
+
+static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_go_iterator_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif->type == NL80211_IFTYPE_AP && vif->p2p && mvmvif->ap_active)
+               data->go_active = true;
+}
+
+static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
+                                          struct ieee80211_vif *vif,
+                                          u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+       struct iwl_mvm_go_iterator_data data = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE);
+
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
+       cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROMISC);
+
+       /*
+        * This flag should be set to true when the P2P Device is
+        * discoverable and there is at least another active P2P GO. Settings
+        * this flag will allow the P2P Device to be discoverable on other
+        * channels in addition to its listen channel.
+        * Note that this flag should not be set in other cases as it opens the
+        * Rx filters on all MAC and increases the number of interrupts.
+        */
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+               iwl_mvm_go_iterator, &data);
+
+       cmd.p2p_dev.is_disc_extended = cpu_to_le32(data.go_active ? 1 : 0);
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm,
+                                    struct iwl_mac_beacon_cmd *beacon_cmd,
+                                    u8 *beacon, u32 frame_size)
+{
+       u32 tim_idx;
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
+
+       /* The index is relative to frame start but we start looking at the
+        * variable-length part of the beacon. */
+       tim_idx = mgmt->u.beacon.variable - beacon;
+
+       /* Parse variable-length elements of beacon to find WLAN_EID_TIM */
+       while ((tim_idx < (frame_size - 2)) &&
+                       (beacon[tim_idx] != WLAN_EID_TIM))
+               tim_idx += beacon[tim_idx+1] + 2;
+
+       /* If TIM field was found, set variables */
+       if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
+               beacon_cmd->tim_idx = cpu_to_le32(tim_idx);
+               beacon_cmd->tim_size = cpu_to_le32((u32)beacon[tim_idx+1]);
+       } else {
+               IWL_WARN(mvm, "Unable to find TIM Element in beacon\n");
+       }
+}
+
+static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       struct sk_buff *beacon)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_host_cmd cmd = {
+               .id = BEACON_TEMPLATE_CMD,
+               .flags = CMD_ASYNC,
+       };
+       struct iwl_mac_beacon_cmd beacon_cmd = {};
+       struct ieee80211_tx_info *info;
+       u32 beacon_skb_len;
+       u32 rate;
+
+       if (WARN_ON(!beacon))
+               return -EINVAL;
+
+       beacon_skb_len = beacon->len;
+
+       /* TODO: for now the beacon template id is set to be the mac context id.
+        * Might be better to handle it as another resource ... */
+       beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id);
+
+       /* Set up TX command fields */
+       beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len);
+       beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id;
+       beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
+       beacon_cmd.tx.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+                                            TX_CMD_FLG_BT_DIS  |
+                                            TX_CMD_FLG_TSF);
+
+       mvm->mgmt_last_antenna_idx =
+               iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant,
+                                    mvm->mgmt_last_antenna_idx);
+
+       beacon_cmd.tx.rate_n_flags =
+               cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
+                           RATE_MCS_ANT_POS);
+
+       info = IEEE80211_SKB_CB(beacon);
+
+       if (info->band == IEEE80211_BAND_5GHZ || vif->p2p) {
+               rate = IWL_FIRST_OFDM_RATE;
+       } else {
+               rate = IWL_FIRST_CCK_RATE;
+               beacon_cmd.tx.rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK);
+       }
+       beacon_cmd.tx.rate_n_flags |=
+               cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate));
+
+       /* Set up TX beacon command fields */
+       iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd,
+                                beacon->data,
+                                beacon_skb_len);
+
+       /* Submit command */
+       cmd.len[0] = sizeof(beacon_cmd);
+       cmd.data[0] = &beacon_cmd;
+       cmd.dataflags[0] = 0;
+       cmd.len[1] = beacon_skb_len;
+       cmd.data[1] = beacon->data;
+       cmd.dataflags[1] = IWL_HCMD_DFL_DUP;
+
+       return iwl_mvm_send_cmd(mvm, &cmd);
+}
+
+/* The beacon template for the AP/GO context has changed and needs update */
+int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif)
+{
+       struct sk_buff *beacon;
+       int ret;
+
+       WARN_ON(vif->type != NL80211_IFTYPE_AP);
+
+       beacon = ieee80211_beacon_get(mvm->hw, vif);
+       if (!beacon)
+               return -ENOMEM;
+
+       ret = iwl_mvm_mac_ctxt_send_beacon(mvm, vif, beacon);
+       dev_kfree_skb(beacon);
+       return ret;
+}
+
+/*
+ * Fill the specific data for mac context of type AP of P2P GO
+ */
+static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif,
+                                        struct iwl_mac_data_ap *ctxt_ap)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u32 curr_dev_time;
+
+       ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int);
+       ctxt_ap->bi_reciprocal =
+               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
+       ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
+                                            vif->bss_conf.dtim_period);
+       ctxt_ap->dtim_reciprocal =
+               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
+                                              vif->bss_conf.dtim_period));
+
+       ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue);
+       curr_dev_time = iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG);
+       ctxt_ap->beacon_time = cpu_to_le32(curr_dev_time);
+
+       ctxt_ap->beacon_tsf = cpu_to_le64(curr_dev_time);
+
+       /* TODO: Assume that the beacon id == mac context id */
+       ctxt_ap->beacon_template = cpu_to_le32(mvmvif->id);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *vif,
+                                  u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       /* Fill the data specific for ap mode */
+       iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap);
+
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *vif,
+                                  u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_AP || !vif->p2p);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       /* Fill the data specific for GO mode */
+       iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap);
+
+       cmd.go.ctwin = cpu_to_le32(vif->bss_conf.p2p_ctwindow);
+       cmd.go.opp_ps_enabled = cpu_to_le32(!!vif->bss_conf.p2p_oppps);
+
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               u32 action)
+{
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               if (!vif->p2p)
+                       return iwl_mvm_mac_ctxt_cmd_station(mvm, vif,
+                                                           action);
+               else
+                       return iwl_mvm_mac_ctxt_cmd_p2p_client(mvm, vif,
+                                                              action);
+               break;
+       case NL80211_IFTYPE_AP:
+               if (!vif->p2p)
+                       return iwl_mvm_mac_ctxt_cmd_ap(mvm, vif, action);
+               else
+                       return iwl_mvm_mac_ctxt_cmd_go(mvm, vif, action);
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               return iwl_mvm_mac_ctxt_cmd_listener(mvm, vif, action);
+       case NL80211_IFTYPE_P2P_DEVICE:
+               return iwl_mvm_mac_ctxt_cmd_p2p_device(mvm, vif, action);
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n",
+                     vif->addr, ieee80211_vif_type_p2p(vif)))
+               return -EIO;
+
+       ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD);
+       if (ret)
+               return ret;
+
+       mvmvif->uploaded = true;
+       return 0;
+}
+
+int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n",
+                     vif->addr, ieee80211_vif_type_p2p(vif)))
+               return -EIO;
+
+       return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY);
+}
+
+int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mac_ctx_cmd cmd;
+       int ret;
+
+       if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n",
+                     vif->addr, ieee80211_vif_type_p2p(vif)))
+               return -EIO;
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                          mvmvif->color));
+       cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC,
+                                  sizeof(cmd), &cmd);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret);
+               return ret;
+       }
+
+       mvmvif->uploaded = false;
+       return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
new file mode 100644 (file)
index 0000000..a6b05a0
--- /dev/null
@@ -0,0 +1,1310 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+
+#include "iwl-op-mode.h"
+#include "iwl-io.h"
+#include "mvm.h"
+#include "sta.h"
+#include "time-event.h"
+#include "iwl-eeprom-parse.h"
+#include "fw-api-scan.h"
+#include "iwl-phy-db.h"
+
+static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_STATION) |
+                       BIT(NL80211_IFTYPE_AP),
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                       BIT(NL80211_IFTYPE_P2P_GO),
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
+       },
+};
+
+static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
+       {
+               .num_different_channels = 1,
+               .max_interfaces = 3,
+               .limits = iwl_mvm_limits,
+               .n_limits = ARRAY_SIZE(iwl_mvm_limits),
+       },
+};
+
+int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
+{
+       struct ieee80211_hw *hw = mvm->hw;
+       int num_mac, ret;
+
+       /* Tell mac80211 our characteristics */
+       hw->flags = IEEE80211_HW_SIGNAL_DBM |
+                   IEEE80211_HW_SPECTRUM_MGMT |
+                   IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+                   IEEE80211_HW_QUEUE_CONTROL |
+                   IEEE80211_HW_WANT_MONITOR_VIF |
+                   IEEE80211_HW_SCAN_WHILE_IDLE |
+                   IEEE80211_HW_NEED_DTIM_PERIOD |
+                   IEEE80211_HW_SUPPORTS_PS |
+                   IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+                   IEEE80211_HW_AMPDU_AGGREGATION;
+
+       hw->queues = IWL_FIRST_AMPDU_QUEUE;
+       hw->offchannel_tx_hw_queue = IWL_OFFCHANNEL_QUEUE;
+       hw->rate_control_algorithm = "iwl-mvm-rs";
+
+       /*
+        * Enable 11w if advertised by firmware and software crypto
+        * is not enabled (as the firmware will interpret some mgmt
+        * packets, so enabling it with software crypto isn't safe)
+        */
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP &&
+           !iwlwifi_mod_params.sw_crypto)
+               hw->flags |= IEEE80211_HW_MFP_CAPABLE;
+
+       hw->sta_data_size = sizeof(struct iwl_mvm_sta);
+       hw->vif_data_size = sizeof(struct iwl_mvm_vif);
+       hw->chanctx_data_size = sizeof(struct iwl_mvm_phy_ctxt);
+
+       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_P2P_CLIENT) |
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_P2P_GO) |
+               BIT(NL80211_IFTYPE_P2P_DEVICE);
+
+       hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
+                           WIPHY_FLAG_DISABLE_BEACON_HINTS |
+                           WIPHY_FLAG_IBSS_RSN;
+
+       hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
+       hw->wiphy->n_iface_combinations =
+               ARRAY_SIZE(iwl_mvm_iface_combinations);
+
+       hw->wiphy->max_remain_on_channel_duration = 500;
+       hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
+
+       /* Extract MAC address */
+       memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
+       hw->wiphy->addresses = mvm->addresses;
+       hw->wiphy->n_addresses = 1;
+       num_mac = mvm->nvm_data->n_hw_addrs;
+       if (num_mac > 1) {
+               memcpy(mvm->addresses[1].addr, mvm->addresses[0].addr,
+                      ETH_ALEN);
+               mvm->addresses[1].addr[5]++;
+               hw->wiphy->n_addresses++;
+       }
+
+       /* we create the 802.11 header and a max-length SSID element */
+       hw->wiphy->max_scan_ie_len =
+               mvm->fw->ucode_capa.max_probe_length - 24 - 34;
+       hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
+
+       if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
+               hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+                       &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
+       if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels)
+               hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+                       &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
+
+       hw->wiphy->hw_version = mvm->trans->hw_id;
+
+       if (iwlwifi_mod_params.power_save)
+               hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+       else
+               hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+       hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
+                              NL80211_FEATURE_P2P_GO_OPPPS;
+
+       mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
+
+#ifdef CONFIG_PM_SLEEP
+       if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
+           mvm->trans->ops->d3_suspend &&
+           mvm->trans->ops->d3_resume &&
+           device_can_wakeup(mvm->trans->dev)) {
+               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
+                                         WIPHY_WOWLAN_DISCONNECT |
+                                         WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+                                         WIPHY_WOWLAN_RFKILL_RELEASE;
+               if (!iwlwifi_mod_params.sw_crypto)
+                       hw->wiphy->wowlan.flags |=
+                               WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+                               WIPHY_WOWLAN_GTK_REKEY_FAILURE |
+                               WIPHY_WOWLAN_4WAY_HANDSHAKE;
+
+               hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
+               hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
+               hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+       }
+#endif
+
+       ret = iwl_mvm_leds_init(mvm);
+       if (ret)
+               return ret;
+
+       return ieee80211_register_hw(mvm->hw);
+}
+
+static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
+                          struct ieee80211_tx_control *control,
+                          struct sk_buff *skb)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) {
+               IWL_DEBUG_DROP(mvm, "Dropping - RF KILL\n");
+               goto drop;
+       }
+
+       if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_OFFCHANNEL_QUEUE &&
+           !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
+               goto drop;
+
+       if (control->sta) {
+               if (iwl_mvm_tx_skb(mvm, skb, control->sta))
+                       goto drop;
+               return;
+       }
+
+       if (iwl_mvm_tx_skb_non_sta(mvm, skb))
+               goto drop;
+       return;
+ drop:
+       ieee80211_free_txskb(hw, skb);
+}
+
+static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   enum ieee80211_ampdu_mlme_action action,
+                                   struct ieee80211_sta *sta, u16 tid,
+                                   u16 *ssn, u8 buf_size)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n",
+                    sta->addr, tid, action);
+
+       if (!(mvm->nvm_data->sku_cap_11n_enable))
+               return -EACCES;
+
+       mutex_lock(&mvm->mutex);
+
+       switch (action) {
+       case IEEE80211_AMPDU_RX_START:
+               if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, *ssn, true);
+               break;
+       case IEEE80211_AMPDU_RX_STOP:
+               ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false);
+               break;
+       case IEEE80211_AMPDU_TX_START:
+               ret = iwl_mvm_sta_tx_agg_start(mvm, vif, sta, tid, ssn);
+               break;
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+               ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid);
+               break;
+       case IEEE80211_AMPDU_TX_OPERATIONAL:
+               ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               ret = -EINVAL;
+               break;
+       }
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mvmvif->uploaded = false;
+       mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+
+       /* does this make sense at all? */
+       mvmvif->color++;
+
+       spin_lock_bh(&mvm->time_event_lock);
+       iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
+               mvmvif->phy_ctxt = NULL;
+}
+
+static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
+{
+       iwl_trans_stop_device(mvm->trans);
+       iwl_trans_stop_hw(mvm->trans, false);
+
+       mvm->scan_status = IWL_MVM_SCAN_NONE;
+
+       /* just in case one was running */
+       ieee80211_remain_on_channel_expired(mvm->hw);
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+               iwl_mvm_cleanup_iterator, mvm);
+
+       memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
+       memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
+
+       ieee80211_wake_queues(mvm->hw);
+
+       mvm->vif_count = 0;
+}
+
+static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       /* Clean up some internal and mac80211 state on restart */
+       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+               iwl_mvm_restart_cleanup(mvm);
+
+       ret = iwl_mvm_up(mvm);
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+       ret = iwl_mvm_update_quotas(mvm, NULL);
+       if (ret)
+               IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
+                       ret);
+
+       mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       flush_work(&mvm->async_handlers_wk);
+
+       mutex_lock(&mvm->mutex);
+       /* async_handlers_wk is now blocked */
+
+       /*
+        * The work item could be running or queued if the
+        * ROC time event stops just as we get here.
+        */
+       cancel_work_sync(&mvm->roc_done_wk);
+
+       iwl_trans_stop_device(mvm->trans);
+       iwl_trans_stop_hw(mvm->trans, false);
+
+       iwl_mvm_async_handlers_purge(mvm);
+       /* async_handlers_list is empty and will stay empty: HW is stopped */
+
+       /* the fw is stopped, the aux sta is dead: clean up driver state */
+       iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta);
+
+       mutex_unlock(&mvm->mutex);
+
+       /*
+        * The worker might have been waiting for the mutex, let it run and
+        * discover that its list is now empty.
+        */
+       cancel_work_sync(&mvm->async_handlers_wk);
+}
+
+static void iwl_mvm_pm_disable_iterator(void *data, u8 *mac,
+                                       struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = data;
+       int ret;
+
+       ret = iwl_mvm_power_disable(mvm, vif);
+       if (ret)
+               IWL_ERR(mvm, "failed to disable power management\n");
+}
+
+static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
+                                         struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = data;
+
+       iwl_mvm_power_update_mode(mvm, vif);
+}
+
+static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       /*
+        * Not much to do here. The stack will not allow interface
+        * types or combinations that we didn't advertise, so we
+        * don't really have to check the types.
+        */
+
+       mutex_lock(&mvm->mutex);
+
+       /* Allocate resources for the MAC context, and add it the the fw  */
+       ret = iwl_mvm_mac_ctxt_init(mvm, vif);
+       if (ret)
+               goto out_unlock;
+
+       /*
+        * The AP binding flow can be done only after the beacon
+        * template is configured (which happens only in the mac80211
+        * start_ap() flow), and adding the broadcast station can happen
+        * only after the binding.
+        * In addition, since modifying the MAC before adding a bcast
+        * station is not allowed by the FW, delay the adding of MAC context to
+        * the point where we can also add the bcast station.
+        * In short: there's not much we can do at this point, other than
+        * allocating resources :)
+        */
+       if (vif->type == NL80211_IFTYPE_AP) {
+               u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
+               ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta,
+                                              qmask);
+               if (ret) {
+                       IWL_ERR(mvm, "Failed to allocate bcast sta\n");
+                       goto out_release;
+               }
+
+               goto out_unlock;
+       }
+
+       /*
+        * TODO: remove this temporary code.
+        * Currently MVM FW supports power management only on single MAC.
+        * Iterate and disable PM on all active interfaces.
+        * Note: the method below does not count the new interface being added
+        * at this moment.
+        */
+       mvm->vif_count++;
+       if (mvm->vif_count > 1) {
+               IWL_DEBUG_MAC80211(mvm,
+                                  "Disable power on existing interfaces\n");
+               ieee80211_iterate_active_interfaces(
+                                           mvm->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           iwl_mvm_pm_disable_iterator, mvm);
+       }
+
+       ret = iwl_mvm_mac_ctxt_add(mvm, vif);
+       if (ret)
+               goto out_release;
+
+       /*
+        * Update power state on the new interface. Admittedly, based on
+        * mac80211 logics this power update will disable power management
+        */
+       iwl_mvm_power_update_mode(mvm, vif);
+
+       /*
+        * P2P_DEVICE interface does not have a channel context assigned to it,
+        * so a dedicated PHY context is allocated to it and the corresponding
+        * MAC context is bound to it at this stage.
+        */
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+               struct ieee80211_channel *chan;
+               struct cfg80211_chan_def chandef;
+
+               mvmvif->phy_ctxt = &mvm->phy_ctxt_roc;
+
+               /*
+                * The channel used here isn't relevant as it's
+                * going to be overwritten as part of the ROC flow.
+                * For now use the first channel we have.
+                */
+               chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0];
+               cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+               ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt,
+                                          &chandef, 1, 1);
+               if (ret)
+                       goto out_remove_mac;
+
+               ret = iwl_mvm_binding_add_vif(mvm, vif);
+               if (ret)
+                       goto out_remove_phy;
+
+               ret = iwl_mvm_add_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
+               if (ret)
+                       goto out_unbind;
+
+               /* Save a pointer to p2p device vif, so it can later be used to
+                * update the p2p device MAC when a GO is started/stopped */
+               mvm->p2p_device_vif = vif;
+       }
+
+       goto out_unlock;
+
+ out_unbind:
+       iwl_mvm_binding_remove_vif(mvm, vif);
+ out_remove_phy:
+       iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt);
+ out_remove_mac:
+       mvmvif->phy_ctxt = NULL;
+       iwl_mvm_mac_ctxt_remove(mvm, vif);
+ out_release:
+       /*
+        * TODO: remove this temporary code.
+        * Currently MVM FW supports power management only on single MAC.
+        * Check if only one additional interface remains after rereasing
+        * current one. Update power mode on the remaining interface.
+        */
+       mvm->vif_count--;
+       IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
+                          mvm->vif_count);
+       if (mvm->vif_count == 1) {
+               ieee80211_iterate_active_interfaces(
+                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                                       iwl_mvm_power_update_iterator, mvm);
+       }
+       iwl_mvm_mac_ctxt_release(mvm, vif);
+ out_unlock:
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u32 tfd_msk = 0, ac;
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+               if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
+                       tfd_msk |= BIT(vif->hw_queue[ac]);
+
+       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
+               tfd_msk |= BIT(vif->cab_queue);
+
+       if (tfd_msk) {
+               mutex_lock(&mvm->mutex);
+               iwl_mvm_flush_tx_path(mvm, tfd_msk, true);
+               mutex_unlock(&mvm->mutex);
+       }
+
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+               /*
+                * Flush the ROC worker which will flush the OFFCHANNEL queue.
+                * We assume here that all the packets sent to the OFFCHANNEL
+                * queue are sent in ROC session.
+                */
+               flush_work(&mvm->roc_done_wk);
+       } else {
+               /*
+                * By now, all the AC queues are empty. The AGG queues are
+                * empty too. We already got all the Tx responses for all the
+                * packets in the queues. The drain work can have been
+                * triggered. Flush it. This work item takes the mutex, so kill
+                * it before we take it.
+                */
+               flush_work(&mvm->sta_drained_wk);
+       }
+
+       mutex_lock(&mvm->mutex);
+
+       /*
+        * For AP/GO interface, the tear down of the resources allocated to the
+        * interface should be handled as part of the bss_info_changed flow.
+        */
+       if (vif->type == NL80211_IFTYPE_AP) {
+               iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta);
+               goto out_release;
+       }
+
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+               mvm->p2p_device_vif = NULL;
+               iwl_mvm_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
+               iwl_mvm_binding_remove_vif(mvm, vif);
+               iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt);
+               mvmvif->phy_ctxt = NULL;
+       }
+
+       /*
+        * TODO: remove this temporary code.
+        * Currently MVM FW supports power management only on single MAC.
+        * Check if only one additional interface remains after removing
+        * current one. Update power mode on the remaining interface.
+        */
+       if (mvm->vif_count)
+               mvm->vif_count--;
+       IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
+                          mvm->vif_count);
+       if (mvm->vif_count == 1) {
+               ieee80211_iterate_active_interfaces(
+                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                                       iwl_mvm_power_update_iterator, mvm);
+       }
+
+       iwl_mvm_mac_ctxt_remove(mvm, vif);
+
+out_release:
+       iwl_mvm_mac_ctxt_release(mvm, vif);
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
+{
+       return 0;
+}
+
+static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
+                                    unsigned int changed_flags,
+                                    unsigned int *total_flags,
+                                    u64 multicast)
+{
+       *total_flags = 0;
+}
+
+static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
+                                            struct ieee80211_vif *vif,
+                                            struct ieee80211_bss_conf *bss_conf,
+                                            u32 changes)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+       if (ret)
+               IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
+
+       if (changes & BSS_CHANGED_ASSOC) {
+               if (bss_conf->assoc) {
+                       /* add quota for this interface */
+                       ret = iwl_mvm_update_quotas(mvm, vif);
+                       if (ret) {
+                               IWL_ERR(mvm, "failed to update quotas\n");
+                               return;
+                       }
+                       iwl_mvm_remove_time_event(mvm, mvmvif,
+                                                 &mvmvif->time_event_data);
+               } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+                       /* remove AP station now that the MAC is unassoc */
+                       ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
+                       if (ret)
+                               IWL_ERR(mvm, "failed to remove AP station\n");
+                       mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+                       /* remove quota for this interface */
+                       ret = iwl_mvm_update_quotas(mvm, NULL);
+                       if (ret)
+                               IWL_ERR(mvm, "failed to update quotas\n");
+               }
+       } else if (changes & BSS_CHANGED_PS) {
+               /*
+                * TODO: remove this temporary code.
+                * Currently MVM FW supports power management only on single
+                * MAC. Avoid power mode update if more than one interface
+                * is active.
+                */
+               IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
+                                  mvm->vif_count);
+               if (mvm->vif_count == 1) {
+                       ret = iwl_mvm_power_update_mode(mvm, vif);
+                       if (ret)
+                               IWL_ERR(mvm, "failed to update power mode\n");
+               }
+       }
+}
+
+static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       /* Send the beacon template */
+       ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif);
+       if (ret)
+               goto out_unlock;
+
+       /* Add the mac context */
+       ret = iwl_mvm_mac_ctxt_add(mvm, vif);
+       if (ret)
+               goto out_unlock;
+
+       /* Perform the binding */
+       ret = iwl_mvm_binding_add_vif(mvm, vif);
+       if (ret)
+               goto out_remove;
+
+       mvmvif->ap_active = true;
+
+       /* Send the bcast station. At this stage the TBTT and DTIM time events
+        * are added and applied to the scheduler */
+       ret = iwl_mvm_send_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
+       if (ret)
+               goto out_unbind;
+
+       ret = iwl_mvm_update_quotas(mvm, vif);
+       if (ret)
+               goto out_rm_bcast;
+
+       /* Need to update the P2P Device MAC */
+       if (vif->p2p && mvm->p2p_device_vif)
+               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
+
+       mutex_unlock(&mvm->mutex);
+       return 0;
+
+out_rm_bcast:
+       iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
+out_unbind:
+       iwl_mvm_binding_remove_vif(mvm, vif);
+out_remove:
+       iwl_mvm_mac_ctxt_remove(mvm, vif);
+out_unlock:
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
+static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mutex_lock(&mvm->mutex);
+
+       mvmvif->ap_active = false;
+
+       /* Need to update the P2P Device MAC */
+       if (vif->p2p && mvm->p2p_device_vif)
+               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
+
+       iwl_mvm_update_quotas(mvm, NULL);
+       iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
+       iwl_mvm_binding_remove_vif(mvm, vif);
+       iwl_mvm_mac_ctxt_remove(mvm, vif);
+
+       mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_bss_info_changed_ap(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_bss_conf *bss_conf,
+                                       u32 changes)
+{
+       /* Need to send a new beacon template to the FW */
+       if (changes & BSS_CHANGED_BEACON) {
+               if (iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
+                       IWL_WARN(mvm, "Failed updating beacon data\n");
+       }
+}
+
+static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
+                                    struct ieee80211_bss_conf *bss_conf,
+                                    u32 changes)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mutex_lock(&mvm->mutex);
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes);
+               break;
+       case NL80211_IFTYPE_AP:
+               iwl_mvm_bss_info_changed_ap(mvm, vif, bss_conf, changes);
+               break;
+       default:
+               /* shouldn't happen */
+               WARN_ON_ONCE(1);
+       }
+
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              struct cfg80211_scan_request *req)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS)
+               return -EINVAL;
+
+       mutex_lock(&mvm->mutex);
+
+       if (mvm->scan_status == IWL_MVM_SCAN_NONE)
+               ret = iwl_mvm_scan_request(mvm, vif, req);
+       else
+               ret = -EBUSY;
+
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mutex_lock(&mvm->mutex);
+
+       iwl_mvm_cancel_scan(mvm);
+
+       mutex_unlock(&mvm->mutex);
+}
+
+static void
+iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,
+                                 struct ieee80211_sta *sta, u16 tid,
+                                 int num_frames,
+                                 enum ieee80211_frame_release_type reason,
+                                 bool more_data)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+
+       /* TODO: how do we tell the fw to send frames for a specific TID */
+
+       /*
+        * The fw will send EOSP notification when the last frame will be
+        * transmitted.
+        */
+       iwl_mvm_sta_modify_sleep_tx_count(mvm, mvmsta->sta_id, reason,
+                                         num_frames);
+}
+
+static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  enum sta_notify_cmd cmd,
+                                  struct ieee80211_sta *sta)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+
+       switch (cmd) {
+       case STA_NOTIFY_SLEEP:
+               if (atomic_read(&mvmsta->pending_frames) > 0)
+                       ieee80211_sta_block_awake(hw, sta, true);
+               /*
+                * The fw updates the STA to be asleep. Tx packets on the Tx
+                * queues to this station will not be transmitted. The fw will
+                * send a Tx response with TX_STATUS_FAIL_DEST_PS.
+                */
+               break;
+       case STA_NOTIFY_AWAKE:
+               if (WARN_ON(mvmsta->sta_id == IWL_INVALID_STATION))
+                       break;
+               iwl_mvm_sta_modify_ps_wake(mvm, mvmsta->sta_id);
+               break;
+       default:
+               break;
+       }
+}
+
+static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_sta *sta,
+                                enum ieee80211_sta_state old_state,
+                                enum ieee80211_sta_state new_state)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n",
+                          sta->addr, old_state, new_state);
+
+       /* this would be a mac80211 bug ... but don't crash */
+       if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
+               return -EINVAL;
+
+       /* if a STA is being removed, reuse its ID */
+       flush_work(&mvm->sta_drained_wk);
+
+       mutex_lock(&mvm->mutex);
+       if (old_state == IEEE80211_STA_NOTEXIST &&
+           new_state == IEEE80211_STA_NONE) {
+               ret = iwl_mvm_add_sta(mvm, vif, sta);
+       } else if (old_state == IEEE80211_STA_NONE &&
+                  new_state == IEEE80211_STA_AUTH) {
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_AUTH &&
+                  new_state == IEEE80211_STA_ASSOC) {
+               iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band);
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTHORIZED) {
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_AUTHORIZED &&
+                  new_state == IEEE80211_STA_ASSOC) {
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTH) {
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_AUTH &&
+                  new_state == IEEE80211_STA_NONE) {
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_NONE &&
+                  new_state == IEEE80211_STA_NOTEXIST) {
+               ret = iwl_mvm_rm_sta(mvm, vif, sta);
+       } else {
+               ret = -EIO;
+       }
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mvm->rts_threshold = value;
+
+       return 0;
+}
+
+static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif, u16 ac,
+                              const struct ieee80211_tx_queue_params *params)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mvmvif->queue_params[ac] = *params;
+
+       /*
+        * No need to update right away, we'll get BSS_CHANGED_QOS
+        * The exception is P2P_DEVICE interface which needs immediate update.
+        */
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+               int ret;
+
+               mutex_lock(&mvm->mutex);
+               ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+               mutex_unlock(&mvm->mutex);
+               return ret;
+       }
+       return 0;
+}
+
+static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       u32 duration = min(IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS,
+                          200 + vif->bss_conf.beacon_int);
+       u32 min_duration = min(IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS,
+                              100 + vif->bss_conf.beacon_int);
+
+       if (WARN_ON_ONCE(vif->bss_conf.assoc))
+               return;
+
+       mutex_lock(&mvm->mutex);
+       /* Try really hard to protect the session and hear a beacon */
+       iwl_mvm_protect_session(mvm, vif, duration, min_duration);
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
+                              enum set_key_cmd cmd,
+                              struct ieee80211_vif *vif,
+                              struct ieee80211_sta *sta,
+                              struct ieee80211_key_conf *key)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       if (iwlwifi_mod_params.sw_crypto) {
+               IWL_DEBUG_MAC80211(mvm, "leave - hwcrypto disabled\n");
+               return -EOPNOTSUPP;
+       }
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+               /* fall-through */
+       case WLAN_CIPHER_SUITE_CCMP:
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+               break;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               WARN_ON_ONCE(!(hw->flags & IEEE80211_HW_MFP_CAPABLE));
+               break;
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               /*
+                * Support for TX only, at least for now, so accept
+                * the key and do nothing else. Then mac80211 will
+                * pass it for TX but we don't have to use it for RX.
+                */
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       mutex_lock(&mvm->mutex);
+
+       switch (cmd) {
+       case SET_KEY:
+               IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n");
+               ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, false);
+               if (ret) {
+                       IWL_WARN(mvm, "set key failed\n");
+                       /*
+                        * can't add key for RX, but we don't need it
+                        * in the device for TX so still return 0
+                        */
+                       ret = 0;
+               }
+
+               break;
+       case DISABLE_KEY:
+               IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n");
+               ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
+static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_key_conf *keyconf,
+                                       struct ieee80211_sta *sta,
+                                       u32 iv32, u16 *phase1key)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key);
+}
+
+
+static int iwl_mvm_roc(struct ieee80211_hw *hw,
+                      struct ieee80211_vif *vif,
+                      struct ieee80211_channel *channel,
+                      int duration)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct cfg80211_chan_def chandef;
+       int ret;
+
+       if (vif->type != NL80211_IFTYPE_P2P_DEVICE) {
+               IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type);
+               return -EINVAL;
+       }
+
+       IWL_DEBUG_MAC80211(mvm, "enter (%d, %d)\n", channel->hw_value,
+                          duration);
+
+       mutex_lock(&mvm->mutex);
+
+       cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
+       ret = iwl_mvm_phy_ctxt_changed(mvm, &mvm->phy_ctxt_roc,
+                                      &chandef, 1, 1);
+
+       /* Schedule the time events */
+       ret = iwl_mvm_start_p2p_roc(mvm, vif, duration);
+
+       mutex_unlock(&mvm->mutex);
+       IWL_DEBUG_MAC80211(mvm, "leave\n");
+
+       return ret;
+}
+
+static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       IWL_DEBUG_MAC80211(mvm, "enter\n");
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_stop_p2p_roc(mvm);
+       mutex_unlock(&mvm->mutex);
+
+       IWL_DEBUG_MAC80211(mvm, "leave\n");
+       return 0;
+}
+
+static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
+                              struct ieee80211_chanctx_conf *ctx)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       IWL_DEBUG_MAC80211(mvm, "Add PHY context\n");
+       ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, &ctx->def,
+                                  ctx->rx_chains_static,
+                                  ctx->rx_chains_dynamic);
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
+static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
+                                  struct ieee80211_chanctx_conf *ctx)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_phy_ctxt_remove(mvm, phy_ctxt);
+       mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
+                                  struct ieee80211_chanctx_conf *ctx,
+                                  u32 changed)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
+                                ctx->rx_chains_static,
+                                ctx->rx_chains_dynamic);
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_chanctx_conf *ctx)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_phy_ctxt *phyctx = (void *)ctx->drv_priv;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       mvmvif->phy_ctxt = phyctx;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_AP:
+               /*
+                * The AP binding flow is handled as part of the start_ap flow
+                * (in bss_info_changed).
+                */
+               ret = 0;
+               goto out_unlock;
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MONITOR:
+               break;
+       default:
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       ret = iwl_mvm_binding_add_vif(mvm, vif);
+       if (ret)
+               goto out_unlock;
+
+       /*
+        * Setting the quota at this stage is only required for monitor
+        * interfaces. For the other types, the bss_info changed flow
+        * will handle quota settings.
+        */
+       if (vif->type == NL80211_IFTYPE_MONITOR) {
+               ret = iwl_mvm_update_quotas(mvm, vif);
+               if (ret)
+                       goto out_remove_binding;
+       }
+
+       goto out_unlock;
+
+ out_remove_binding:
+       iwl_mvm_binding_remove_vif(mvm, vif);
+ out_unlock:
+       mutex_unlock(&mvm->mutex);
+       if (ret)
+               mvmvif->phy_ctxt = NULL;
+       return ret;
+}
+
+static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif,
+                                        struct ieee80211_chanctx_conf *ctx)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mutex_lock(&mvm->mutex);
+
+       iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
+
+       if (vif->type == NL80211_IFTYPE_AP)
+               goto out_unlock;
+
+       iwl_mvm_binding_remove_vif(mvm, vif);
+       switch (vif->type) {
+       case NL80211_IFTYPE_MONITOR:
+               iwl_mvm_update_quotas(mvm, vif);
+               break;
+       default:
+               break;
+       }
+
+out_unlock:
+       mvmvif->phy_ctxt = NULL;
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
+                          struct ieee80211_sta *sta,
+                          bool set)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+
+       if (!mvm_sta || !mvm_sta->vif) {
+               IWL_ERR(mvm, "Station is not associated to a vif\n");
+               return -EINVAL;
+       }
+
+       return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);
+}
+
+struct ieee80211_ops iwl_mvm_hw_ops = {
+       .tx = iwl_mvm_mac_tx,
+       .ampdu_action = iwl_mvm_mac_ampdu_action,
+       .start = iwl_mvm_mac_start,
+       .restart_complete = iwl_mvm_mac_restart_complete,
+       .stop = iwl_mvm_mac_stop,
+       .add_interface = iwl_mvm_mac_add_interface,
+       .remove_interface = iwl_mvm_mac_remove_interface,
+       .config = iwl_mvm_mac_config,
+       .configure_filter = iwl_mvm_configure_filter,
+       .bss_info_changed = iwl_mvm_bss_info_changed,
+       .hw_scan = iwl_mvm_mac_hw_scan,
+       .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan,
+       .sta_state = iwl_mvm_mac_sta_state,
+       .sta_notify = iwl_mvm_mac_sta_notify,
+       .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
+       .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
+       .conf_tx = iwl_mvm_mac_conf_tx,
+       .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
+       .set_key = iwl_mvm_mac_set_key,
+       .update_tkip_key = iwl_mvm_mac_update_tkip_key,
+       .remain_on_channel = iwl_mvm_roc,
+       .cancel_remain_on_channel = iwl_mvm_cancel_roc,
+
+       .add_chanctx = iwl_mvm_add_chanctx,
+       .remove_chanctx = iwl_mvm_remove_chanctx,
+       .change_chanctx = iwl_mvm_change_chanctx,
+       .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
+       .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
+
+       .start_ap = iwl_mvm_start_ap,
+       .stop_ap = iwl_mvm_stop_ap,
+
+       .set_tim = iwl_mvm_set_tim,
+
+#ifdef CONFIG_PM_SLEEP
+       /* look at d3.c */
+       .suspend = iwl_mvm_suspend,
+       .resume = iwl_mvm_resume,
+       .set_wakeup = iwl_mvm_set_wakeup,
+       .set_rekey_data = iwl_mvm_set_rekey_data,
+#if IS_ENABLED(CONFIG_IPV6)
+       .ipv6_addr_change = iwl_mvm_ipv6_addr_change,
+#endif
+       .set_default_unicast_key = iwl_mvm_set_default_unicast_key,
+#endif
+};
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
new file mode 100644 (file)
index 0000000..4e339cc
--- /dev/null
@@ -0,0 +1,500 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __IWL_MVM_H__
+#define __IWL_MVM_H__
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/leds.h>
+#include <linux/in6.h>
+
+#include "iwl-op-mode.h"
+#include "iwl-trans.h"
+#include "iwl-notif-wait.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-test.h"
+#include "iwl-trans.h"
+#include "sta.h"
+#include "fw-api.h"
+
+#define IWL_INVALID_MAC80211_QUEUE     0xff
+#define IWL_MVM_MAX_ADDRESSES          2
+#define IWL_RSSI_OFFSET 44
+
+enum iwl_mvm_tx_fifo {
+       IWL_MVM_TX_FIFO_BK = 0,
+       IWL_MVM_TX_FIFO_BE,
+       IWL_MVM_TX_FIFO_VI,
+       IWL_MVM_TX_FIFO_VO,
+};
+
+/* Placeholder */
+#define IWL_OFFCHANNEL_QUEUE 8
+#define IWL_FIRST_AMPDU_QUEUE 11
+
+extern struct ieee80211_ops iwl_mvm_hw_ops;
+/**
+ * struct iwl_mvm_mod_params - module parameters for iwlmvm
+ * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted.
+ *     We will register to mac80211 to have testmode working. The NIC must not
+ *     be up'ed after the INIT fw asserted. This is useful to be able to use
+ *     proprietary tools over testmode to debug the INIT fw.
+ * @power_scheme: CAM(Continuous Active Mode)-1, BPS(Balanced Power
+ *     Save)-2(default), LP(Low Power)-3
+ */
+struct iwl_mvm_mod_params {
+       bool init_dbg;
+       int power_scheme;
+};
+extern struct iwl_mvm_mod_params iwlmvm_mod_params;
+
+struct iwl_mvm_phy_ctxt {
+       u16 id;
+       u16 color;
+
+       /*
+        * TODO: This should probably be removed. Currently here only for rate
+        * scaling algorithm
+        */
+       struct ieee80211_channel *channel;
+};
+
+struct iwl_mvm_time_event_data {
+       struct ieee80211_vif *vif;
+       struct list_head list;
+       unsigned long end_jiffies;
+       u32 duration;
+       bool running;
+       u32 uid;
+
+       /*
+        * The access to the 'id' field must be done when the
+        * mvm->time_event_lock is held, as it value is used to indicate
+        * if the te is in the time event list or not (when id == TE_MAX)
+        */
+       u32 id;
+};
+
+ /* Power management */
+
+/**
+ * enum iwl_power_scheme
+ * @IWL_POWER_LEVEL_CAM - Continuously Active Mode
+ * @IWL_POWER_LEVEL_BPS - Balanced Power Save (default)
+ * @IWL_POWER_LEVEL_LP  - Low Power
+ */
+enum iwl_power_scheme {
+       IWL_POWER_SCHEME_CAM = 1,
+       IWL_POWER_SCHEME_BPS,
+       IWL_POWER_SCHEME_LP
+};
+
+#define IWL_CONN_MAX_LISTEN_INTERVAL   70
+
+/**
+ * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context
+ * @id: between 0 and 3
+ * @color: to solve races upon MAC addition and removal
+ * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA
+ * @uploaded: indicates the MAC context has been added to the device
+ * @ap_active: indicates that ap context is configured, and that the interface
+ *  should get quota etc.
+ * @queue_params: QoS params for this MAC
+ * @bcast_sta: station used for broadcast packets. Used by the following
+ *  vifs: P2P_DEVICE, GO and AP.
+ * @beacon_skb: the skb used to hold the AP/GO beacon template
+ */
+struct iwl_mvm_vif {
+       u16 id;
+       u16 color;
+       u8 ap_sta_id;
+
+       bool uploaded;
+       bool ap_active;
+
+       enum iwl_tsf_id tsf_id;
+
+       /*
+        * QoS data from mac80211, need to store this here
+        * as mac80211 has a separate callback but we need
+        * to have the data for the MAC context
+        */
+       struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+       struct iwl_mvm_time_event_data time_event_data;
+
+       struct iwl_mvm_int_sta bcast_sta;
+
+       /*
+        * Assigned while mac80211 has the interface in a channel context,
+        * or, for P2P Device, while it exists.
+        */
+       struct iwl_mvm_phy_ctxt *phy_ctxt;
+
+#ifdef CONFIG_PM_SLEEP
+       /* WoWLAN GTK rekey data */
+       struct {
+               u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
+               __le64 replay_ctr;
+               bool valid;
+       } rekey_data;
+
+       int tx_key_idx;
+
+#if IS_ENABLED(CONFIG_IPV6)
+       /* IPv6 addresses for WoWLAN */
+       struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS];
+       int num_target_ipv6_addrs;
+#endif
+#endif
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       struct dentry *dbgfs_dir;
+       void *dbgfs_data;
+#endif
+};
+
+static inline struct iwl_mvm_vif *
+iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)
+{
+       return (void *)vif->drv_priv;
+}
+
+enum iwl_mvm_status {
+       IWL_MVM_STATUS_HW_RFKILL,
+       IWL_MVM_STATUS_ROC_RUNNING,
+       IWL_MVM_STATUS_IN_HW_RESTART,
+};
+
+enum iwl_scan_status {
+       IWL_MVM_SCAN_NONE,
+       IWL_MVM_SCAN_OS,
+};
+
+/**
+ * struct iwl_nvm_section - describes an NVM section in memory.
+ *
+ * This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD,
+ * and saved for later use by the driver. Not all NVM sections are saved
+ * this way, only the needed ones.
+ */
+struct iwl_nvm_section {
+       u16 length;
+       const u8 *data;
+};
+
+struct iwl_mvm {
+       /* for logger access */
+       struct device *dev;
+
+       struct iwl_trans *trans;
+       const struct iwl_fw *fw;
+       const struct iwl_cfg *cfg;
+       struct iwl_phy_db *phy_db;
+       struct ieee80211_hw *hw;
+
+       /* for protecting access to iwl_mvm */
+       struct mutex mutex;
+       struct list_head async_handlers_list;
+       spinlock_t async_handlers_lock;
+       struct work_struct async_handlers_wk;
+
+       struct work_struct roc_done_wk;
+
+       unsigned long status;
+
+       enum iwl_ucode_type cur_ucode;
+       bool ucode_loaded;
+       bool init_ucode_run;
+       u32 error_event_table;
+       u32 log_event_table;
+
+       u32 ampdu_ref;
+
+       struct iwl_notif_wait_data notif_wait;
+
+       unsigned long transport_queue_stop;
+       u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
+       atomic_t queue_stop_count[IWL_MAX_HW_QUEUES];
+
+       struct iwl_nvm_data *nvm_data;
+       /* eeprom blob for debugfs/testmode */
+       u8 *eeprom_blob;
+       size_t eeprom_blob_size;
+       /* NVM sections for 7000 family */
+       struct iwl_nvm_section nvm_sections[NVM_NUM_OF_SECTIONS];
+
+       /* EEPROM MAC addresses */
+       struct mac_address addresses[IWL_MVM_MAX_ADDRESSES];
+
+       /* data related to data path */
+       struct iwl_rx_phy_info last_phy_info;
+       struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT];
+       struct work_struct sta_drained_wk;
+       unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)];
+
+       /* configured by mac80211 */
+       u32 rts_threshold;
+
+       /* Scan status, cmd (pre-allocated) and auxiliary station */
+       enum iwl_scan_status scan_status;
+       struct iwl_scan_cmd *scan_cmd;
+
+       /* Internal station */
+       struct iwl_mvm_int_sta aux_sta;
+
+       u8 scan_last_antenna_idx; /* to toggle TX between antennas */
+       u8 mgmt_last_antenna_idx;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       struct dentry *debugfs_dir;
+       u32 dbgfs_sram_offset, dbgfs_sram_len;
+       bool prevent_power_down_d3;
+#endif
+
+       struct iwl_mvm_phy_ctxt phy_ctxt_roc;
+
+       struct list_head time_event_list;
+       spinlock_t time_event_lock;
+
+       /*
+        * A bitmap indicating the index of the key in use. The firmware
+        * can hold 16 keys at most. Reflect this fact.
+        */
+       unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
+       u8 vif_count;
+
+       struct led_classdev led;
+
+       struct ieee80211_vif *p2p_device_vif;
+};
+
+/* Extract MVM priv from op_mode and _hw */
+#define IWL_OP_MODE_GET_MVM(_iwl_op_mode)              \
+       ((struct iwl_mvm *)(_iwl_op_mode)->op_mode_specific)
+
+#define IWL_MAC80211_GET_MVM(_hw)                      \
+       IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv))
+
+extern const u8 iwl_mvm_ac_to_tx_fifo[];
+
+struct iwl_rate_info {
+       u8 plcp;        /* uCode API:  IWL_RATE_6M_PLCP, etc. */
+       u8 plcp_siso;   /* uCode API:  IWL_RATE_SISO_6M_PLCP, etc. */
+       u8 plcp_mimo2;  /* uCode API:  IWL_RATE_MIMO2_6M_PLCP, etc. */
+       u8 plcp_mimo3;  /* uCode API:  IWL_RATE_MIMO3_6M_PLCP, etc. */
+       u8 ieee;        /* MAC header:  IWL_RATE_6M_IEEE, etc. */
+};
+
+/******************
+ * MVM Methods
+ ******************/
+/* uCode */
+int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm);
+
+/* Utils */
+int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
+                                       enum ieee80211_band band);
+u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
+void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
+u8 first_antenna(u8 mask);
+u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
+
+/* Tx / Host Commands */
+int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm,
+                                 struct iwl_host_cmd *cmd);
+int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u8 id,
+                                     u32 flags, u16 len, const void *data);
+int __must_check iwl_mvm_send_cmd_status(struct iwl_mvm *mvm,
+                                        struct iwl_host_cmd *cmd,
+                                        u32 *status);
+int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u8 id,
+                                            u16 len, const void *data,
+                                            u32 *status);
+int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
+                  struct ieee80211_sta *sta);
+int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb);
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_mvm_get_tx_fail_reason(u32 status);
+#else
+static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; }
+#endif
+int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync);
+void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm);
+
+/* Statistics */
+int iwl_mvm_rx_reply_statistics(struct iwl_mvm *mvm,
+                               struct iwl_rx_cmd_buffer *rxb,
+                               struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
+                         struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd);
+
+/* NVM */
+int iwl_nvm_init(struct iwl_mvm *mvm);
+
+int iwl_mvm_up(struct iwl_mvm *mvm);
+int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm);
+
+int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm);
+
+/*
+ * FW notifications / CMD responses handlers
+ * Convention: iwl_mvm_rx_<NAME OF THE CMD>
+ */
+int iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                      struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                     struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                       struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                        struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
+                               struct iwl_rx_cmd_buffer *rxb,
+                               struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                        struct iwl_device_cmd *cmd);
+
+/* MVM PHY */
+int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+                        struct cfg80211_chan_def *chandef,
+                        u8 chains_static, u8 chains_dynamic);
+int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+                            struct cfg80211_chan_def *chandef,
+                            u8 chains_static, u8 chains_dynamic);
+void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm,
+                            struct iwl_mvm_phy_ctxt *ctxt);
+
+/* MAC (virtual interface) programming */
+int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
+                               struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif);
+
+/* Bindings */
+int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+
+/* Quota management */
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif);
+
+/* Scanning */
+int iwl_mvm_scan_request(struct iwl_mvm *mvm,
+                        struct ieee80211_vif *vif,
+                        struct cfg80211_scan_request *req);
+int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                            struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                            struct iwl_device_cmd *cmd);
+void iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
+
+/* MVM debugfs */
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
+int iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                              struct dentry *dbgfs_dir);
+void iwl_power_get_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         struct iwl_powertable_cmd *cmd);
+#else
+static inline int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm,
+                                        struct dentry *dbgfs_dir)
+{
+       return 0;
+}
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
+
+/* rate scaling */
+int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
+                       u8 flags, bool init);
+
+/* power managment */
+int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+
+int iwl_mvm_leds_init(struct iwl_mvm *mvm);
+void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
+
+/* D3 (WoWLAN, NetDetect) */
+int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
+int iwl_mvm_resume(struct ieee80211_hw *hw);
+void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled);
+void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct cfg80211_gtk_rekey_data *data);
+void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             struct inet6_dev *idev);
+void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif, int idx);
+
+#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c
new file mode 100644 (file)
index 0000000..20016bc
--- /dev/null
@@ -0,0 +1,311 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include "iwl-trans.h"
+#include "mvm.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-eeprom-read.h"
+#include "iwl-nvm-parse.h"
+
+/* list of NVM sections we are allowed/need to read */
+static const int nvm_to_read[] = {
+       NVM_SECTION_TYPE_HW,
+       NVM_SECTION_TYPE_SW,
+       NVM_SECTION_TYPE_CALIBRATION,
+       NVM_SECTION_TYPE_PRODUCTION,
+};
+
+/* used to simplify the shared operations on NCM_ACCESS_CMD versions */
+union iwl_nvm_access_cmd {
+       struct iwl_nvm_access_cmd_ver1 ver1;
+       struct iwl_nvm_access_cmd_ver2 ver2;
+};
+union iwl_nvm_access_resp {
+       struct iwl_nvm_access_resp_ver1 ver1;
+       struct iwl_nvm_access_resp_ver2 ver2;
+};
+
+static inline void iwl_nvm_fill_read_ver1(struct iwl_nvm_access_cmd_ver1 *cmd,
+                                         u16 offset, u16 length)
+{
+       cmd->offset = cpu_to_le16(offset);
+       cmd->length = cpu_to_le16(length);
+       cmd->cache_refresh = 1;
+}
+
+static inline void iwl_nvm_fill_read_ver2(struct iwl_nvm_access_cmd_ver2 *cmd,
+                                         u16 offset, u16 length, u16 section)
+{
+       cmd->offset = cpu_to_le16(offset);
+       cmd->length = cpu_to_le16(length);
+       cmd->type = cpu_to_le16(section);
+}
+
+static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
+                             u16 offset, u16 length, u8 *data)
+{
+       union iwl_nvm_access_cmd nvm_access_cmd;
+       union iwl_nvm_access_resp *nvm_resp;
+       struct iwl_rx_packet *pkt;
+       struct iwl_host_cmd cmd = {
+               .id = NVM_ACCESS_CMD,
+               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .data = { &nvm_access_cmd, },
+       };
+       int ret, bytes_read, offset_read;
+       u8 *resp_data;
+
+       memset(&nvm_access_cmd, 0, sizeof(nvm_access_cmd));
+
+       /* TODO: not sure family should be the decider, maybe FW version? */
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+               iwl_nvm_fill_read_ver2(&(nvm_access_cmd.ver2),
+                                      offset, length, section);
+               cmd.len[0] = sizeof(struct iwl_nvm_access_cmd_ver2);
+       } else {
+               iwl_nvm_fill_read_ver1(&(nvm_access_cmd.ver1),
+                                      offset, length);
+               cmd.len[0] = sizeof(struct iwl_nvm_access_cmd_ver1);
+       }
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       if (ret)
+               return ret;
+
+       pkt = cmd.resp_pkt;
+       if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+               IWL_ERR(mvm, "Bad return from NVM_ACCES_COMMAND (0x%08X)\n",
+                       pkt->hdr.flags);
+               ret = -EIO;
+               goto exit;
+       }
+
+       /* Extract NVM response */
+       nvm_resp = (void *)pkt->data;
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+               ret = le16_to_cpu(nvm_resp->ver2.status);
+               bytes_read = le16_to_cpu(nvm_resp->ver2.length);
+               offset_read = le16_to_cpu(nvm_resp->ver2.offset);
+               resp_data = nvm_resp->ver2.data;
+       } else {
+               ret = le16_to_cpu(nvm_resp->ver1.length) <= 0;
+               bytes_read = le16_to_cpu(nvm_resp->ver1.length);
+               offset_read = le16_to_cpu(nvm_resp->ver1.offset);
+               resp_data = nvm_resp->ver1.data;
+       }
+       if (ret) {
+               IWL_ERR(mvm,
+                       "NVM access command failed with status %d (device: %s)\n",
+                       ret, mvm->cfg->name);
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       if (offset_read != offset) {
+               IWL_ERR(mvm, "NVM ACCESS response with invalid offset %d\n",
+                       offset_read);
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       /* Write data to NVM */
+       memcpy(data + offset, resp_data, bytes_read);
+       ret = bytes_read;
+
+exit:
+       iwl_free_resp(&cmd);
+       return ret;
+}
+
+/*
+ * Reads an NVM section completely.
+ * NICs prior to 7000 family doesn't have a real NVM, but just read
+ * section 0 which is the EEPROM. Because the EEPROM reading is unlimited
+ * by uCode, we need to manually check in this case that we don't
+ * overflow and try to read more than the EEPROM size.
+ * For 7000 family NICs, we supply the maximal size we can read, and
+ * the uCode fills the response with as much data as we can,
+ * without overflowing, so no check is needed.
+ */
+static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
+                               u8 *data)
+{
+       u16 length, offset = 0;
+       int ret;
+       bool old_eeprom = mvm->cfg->device_family != IWL_DEVICE_FAMILY_7000;
+
+       length = (iwlwifi_mod_params.amsdu_size_8K ? (8 * 1024) : (4 * 1024))
+               - sizeof(union iwl_nvm_access_cmd)
+               - sizeof(struct iwl_rx_packet);
+       /*
+        * if length is greater than EEPROM size, truncate it because uCode
+        * doesn't check it by itself, and exit the loop when reached.
+        */
+       if (old_eeprom && length > mvm->cfg->base_params->eeprom_size)
+               length = mvm->cfg->base_params->eeprom_size;
+       ret = length;
+
+       /* Read the NVM until exhausted (reading less than requested) */
+       while (ret == length) {
+               ret = iwl_nvm_read_chunk(mvm, section, offset, length, data);
+               if (ret < 0) {
+                       IWL_ERR(mvm,
+                               "Cannot read NVM from section %d offset %d, length %d\n",
+                               section, offset, length);
+                       return ret;
+               }
+               offset += ret;
+               if (old_eeprom && offset == mvm->cfg->base_params->eeprom_size)
+                       break;
+       }
+
+       IWL_INFO(mvm, "NVM section %d read completed\n", section);
+       return offset;
+}
+
+static struct iwl_nvm_data *
+iwl_parse_nvm_sections(struct iwl_mvm *mvm)
+{
+       struct iwl_nvm_section *sections = mvm->nvm_sections;
+       const __le16 *hw, *sw, *calib;
+
+       /* Checking for required sections */
+       if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
+           !mvm->nvm_sections[NVM_SECTION_TYPE_HW].data) {
+               IWL_ERR(mvm, "Can't parse empty NVM sections\n");
+               return NULL;
+       }
+
+       if (WARN_ON(!mvm->cfg))
+               return NULL;
+
+       hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data;
+       sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
+       calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
+       return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib);
+}
+
+int iwl_nvm_init(struct iwl_mvm *mvm)
+{
+       int ret, i, section;
+       u8 *nvm_buffer, *temp;
+
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+               /* TODO: find correct NVM max size for a section */
+               nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
+                                    GFP_KERNEL);
+               if (!nvm_buffer)
+                       return -ENOMEM;
+               for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
+                       section = nvm_to_read[i];
+                       /* we override the constness for initial read */
+                       ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
+                       if (ret < 0)
+                               break;
+                       temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
+                       if (!temp) {
+                               ret = -ENOMEM;
+                               break;
+                       }
+                       mvm->nvm_sections[section].data = temp;
+                       mvm->nvm_sections[section].length = ret;
+               }
+               kfree(nvm_buffer);
+               if (ret < 0)
+                       return ret;
+       } else {
+               /* allocate eeprom */
+               mvm->eeprom_blob_size = mvm->cfg->base_params->eeprom_size;
+               IWL_DEBUG_EEPROM(mvm->trans->dev, "NVM size = %zd\n",
+                                mvm->eeprom_blob_size);
+               mvm->eeprom_blob = kzalloc(mvm->eeprom_blob_size, GFP_KERNEL);
+               if (!mvm->eeprom_blob)
+                       return -ENOMEM;
+
+               ret = iwl_nvm_read_section(mvm, 0, mvm->eeprom_blob);
+               if (ret != mvm->eeprom_blob_size) {
+                       IWL_ERR(mvm, "Read partial NVM %d/%zd\n",
+                               ret, mvm->eeprom_blob_size);
+                       kfree(mvm->eeprom_blob);
+                       mvm->eeprom_blob = NULL;
+                       return -EINVAL;
+               }
+       }
+
+       ret = 0;
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
+               mvm->nvm_data = iwl_parse_nvm_sections(mvm);
+       else
+               mvm->nvm_data =
+                       iwl_parse_eeprom_data(mvm->trans->dev,
+                                             mvm->cfg,
+                                             mvm->eeprom_blob,
+                                             mvm->eeprom_blob_size);
+
+       if (!mvm->nvm_data) {
+               kfree(mvm->eeprom_blob);
+               mvm->eeprom_blob = NULL;
+               ret = -ENOMEM;
+       }
+
+       return ret;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
new file mode 100644 (file)
index 0000000..983dca3
--- /dev/null
@@ -0,0 +1,679 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/module.h>
+#include <net/mac80211.h>
+
+#include "iwl-notif-wait.h"
+#include "iwl-trans.h"
+#include "iwl-op-mode.h"
+#include "iwl-fw.h"
+#include "iwl-debug.h"
+#include "iwl-drv.h"
+#include "iwl-modparams.h"
+#include "mvm.h"
+#include "iwl-phy-db.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-csr.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
+#include "rs.h"
+#include "fw-api-scan.h"
+#include "time-event.h"
+
+/*
+ * module name, copyright, version, etc.
+ */
+#define DRV_DESCRIPTION        "The new Intel(R) wireless AGN driver for Linux"
+
+#define DRV_VERSION     IWLWIFI_VERSION
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+
+static const struct iwl_op_mode_ops iwl_mvm_ops;
+
+struct iwl_mvm_mod_params iwlmvm_mod_params = {
+       .power_scheme = IWL_POWER_SCHEME_BPS,
+       /* rest of fields are 0 by default */
+};
+
+module_param_named(init_dbg, iwlmvm_mod_params.init_dbg, bool, S_IRUGO);
+MODULE_PARM_DESC(init_dbg,
+                "set to true to debug an ASSERT in INIT fw (default: false");
+module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO);
+MODULE_PARM_DESC(power_scheme,
+                "power management scheme: 1-active, 2-balanced, 3-low power, default: 2");
+
+/*
+ * module init and exit functions
+ */
+static int __init iwl_mvm_init(void)
+{
+       int ret;
+
+       ret = iwl_mvm_rate_control_register();
+       if (ret) {
+               pr_err("Unable to register rate control algorithm: %d\n", ret);
+               return ret;
+       }
+
+       ret = iwl_opmode_register("iwlmvm", &iwl_mvm_ops);
+
+       if (ret) {
+               pr_err("Unable to register MVM op_mode: %d\n", ret);
+               iwl_mvm_rate_control_unregister();
+       }
+
+       return ret;
+}
+module_init(iwl_mvm_init);
+
+static void __exit iwl_mvm_exit(void)
+{
+       iwl_opmode_deregister("iwlmvm");
+       iwl_mvm_rate_control_unregister();
+}
+module_exit(iwl_mvm_exit);
+
+static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash;
+       u32 reg_val = 0;
+
+       /*
+        * We can't upload the correct value to the INIT image
+        * as we don't have nvm_data by that time.
+        *
+        * TODO: Figure out what we should do here
+        */
+       if (mvm->nvm_data) {
+               radio_cfg_type = mvm->nvm_data->radio_cfg_type;
+               radio_cfg_step = mvm->nvm_data->radio_cfg_step;
+               radio_cfg_dash = mvm->nvm_data->radio_cfg_dash;
+       } else {
+               radio_cfg_type = 0;
+               radio_cfg_step = 0;
+               radio_cfg_dash = 0;
+       }
+
+       /* SKU control */
+       reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) <<
+                               CSR_HW_IF_CONFIG_REG_POS_MAC_STEP;
+       reg_val |= CSR_HW_REV_DASH(mvm->trans->hw_rev) <<
+                               CSR_HW_IF_CONFIG_REG_POS_MAC_DASH;
+
+       /* radio configuration */
+       reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE;
+       reg_val |= radio_cfg_step << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP;
+       reg_val |= radio_cfg_dash << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
+
+       WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) &
+                ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE);
+
+       /* silicon bits */
+       reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
+       reg_val |= CSR_HW_IF_CONFIG_REG_BIT_MAC_SI;
+
+       iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG,
+                               CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
+                               CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP |
+                               CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
+                               CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
+                               CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH |
+                               CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
+                               CSR_HW_IF_CONFIG_REG_BIT_MAC_SI,
+                               reg_val);
+
+       IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type,
+                      radio_cfg_step, radio_cfg_dash);
+
+       /*
+        * W/A : NIC is stuck in a reset state after Early PCIe power off
+        * (PCIe power is lost before PERST# is asserted), causing ME FW
+        * to lose ownership and not being able to obtain it back.
+        */
+       iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
+                              APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
+                              ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
+}
+
+struct iwl_rx_handlers {
+       u8 cmd_id;
+       bool async;
+       int (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                 struct iwl_device_cmd *cmd);
+};
+
+#define RX_HANDLER(_cmd_id, _fn, _async)       \
+       { .cmd_id = _cmd_id , .fn = _fn , .async = _async }
+
+/*
+ * Handlers for fw notifications
+ * Convention: RX_HANDLER(CMD_NAME, iwl_mvm_rx_CMD_NAME
+ * This list should be in order of frequency for performance purposes.
+ *
+ * The handler can be SYNC - this means that it will be called in the Rx path
+ * which can't acquire mvm->mutex. If the handler needs to hold mvm->mutex (and
+ * only in this case!), it should be set as ASYNC. In that case, it will be
+ * called from a worker with mvm->mutex held.
+ */
+static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
+       RX_HANDLER(REPLY_RX_MPDU_CMD, iwl_mvm_rx_rx_mpdu, false),
+       RX_HANDLER(REPLY_RX_PHY_CMD, iwl_mvm_rx_rx_phy_cmd, false),
+       RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false),
+       RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
+       RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
+
+       RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
+       RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
+
+       RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
+       RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
+
+       RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
+};
+#undef RX_HANDLER
+#define CMD(x) [x] = #x
+
+static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
+       CMD(MVM_ALIVE),
+       CMD(REPLY_ERROR),
+       CMD(INIT_COMPLETE_NOTIF),
+       CMD(PHY_CONTEXT_CMD),
+       CMD(MGMT_MCAST_KEY),
+       CMD(TX_CMD),
+       CMD(TXPATH_FLUSH),
+       CMD(MAC_CONTEXT_CMD),
+       CMD(TIME_EVENT_CMD),
+       CMD(TIME_EVENT_NOTIFICATION),
+       CMD(BINDING_CONTEXT_CMD),
+       CMD(TIME_QUOTA_CMD),
+       CMD(RADIO_VERSION_NOTIFICATION),
+       CMD(SCAN_REQUEST_CMD),
+       CMD(SCAN_ABORT_CMD),
+       CMD(SCAN_START_NOTIFICATION),
+       CMD(SCAN_RESULTS_NOTIFICATION),
+       CMD(SCAN_COMPLETE_NOTIFICATION),
+       CMD(NVM_ACCESS_CMD),
+       CMD(PHY_CONFIGURATION_CMD),
+       CMD(CALIB_RES_NOTIF_PHY_DB),
+       CMD(SET_CALIB_DEFAULT_CMD),
+       CMD(CALIBRATION_COMPLETE_NOTIFICATION),
+       CMD(ADD_STA),
+       CMD(REMOVE_STA),
+       CMD(LQ_CMD),
+       CMD(SCAN_OFFLOAD_CONFIG_CMD),
+       CMD(SCAN_OFFLOAD_REQUEST_CMD),
+       CMD(SCAN_OFFLOAD_ABORT_CMD),
+       CMD(SCAN_OFFLOAD_COMPLETE),
+       CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
+       CMD(POWER_TABLE_CMD),
+       CMD(WEP_KEY),
+       CMD(REPLY_RX_PHY_CMD),
+       CMD(REPLY_RX_MPDU_CMD),
+       CMD(BEACON_TEMPLATE_CMD),
+       CMD(STATISTICS_NOTIFICATION),
+       CMD(TX_ANT_CONFIGURATION_CMD),
+       CMD(D3_CONFIG_CMD),
+       CMD(PROT_OFFLOAD_CONFIG_CMD),
+       CMD(OFFLOADS_QUERY_CMD),
+       CMD(REMOTE_WAKE_CONFIG_CMD),
+       CMD(WOWLAN_PATTERNS),
+       CMD(WOWLAN_CONFIGURATION),
+       CMD(WOWLAN_TSC_RSC_PARAM),
+       CMD(WOWLAN_TKIP_PARAM),
+       CMD(WOWLAN_KEK_KCK_MATERIAL),
+       CMD(WOWLAN_GET_STATUSES),
+       CMD(WOWLAN_TX_POWER_PER_DB),
+       CMD(NET_DETECT_CONFIG_CMD),
+       CMD(NET_DETECT_PROFILES_QUERY_CMD),
+       CMD(NET_DETECT_PROFILES_CMD),
+       CMD(NET_DETECT_HOTSPOTS_CMD),
+       CMD(NET_DETECT_HOTSPOTS_QUERY_CMD),
+};
+#undef CMD
+
+/* this forward declaration can avoid to export the function */
+static void iwl_mvm_async_handlers_wk(struct work_struct *wk);
+
+static struct iwl_op_mode *
+iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+                     const struct iwl_fw *fw, struct dentry *dbgfs_dir)
+{
+       struct ieee80211_hw *hw;
+       struct iwl_op_mode *op_mode;
+       struct iwl_mvm *mvm;
+       struct iwl_trans_config trans_cfg = {};
+       static const u8 no_reclaim_cmds[] = {
+               TX_CMD,
+       };
+       int err, scan_size;
+
+       switch (cfg->device_family) {
+       case IWL_DEVICE_FAMILY_6030:
+       case IWL_DEVICE_FAMILY_6005:
+       case IWL_DEVICE_FAMILY_7000:
+               break;
+       default:
+               IWL_ERR(trans, "Trying to load mvm on an unsupported device\n");
+               return NULL;
+       }
+
+       /********************************
+        * 1. Allocating and configuring HW data
+        ********************************/
+       hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) +
+                               sizeof(struct iwl_mvm),
+                               &iwl_mvm_hw_ops);
+       if (!hw)
+               return NULL;
+
+       op_mode = hw->priv;
+       op_mode->ops = &iwl_mvm_ops;
+       op_mode->trans = trans;
+
+       mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       mvm->dev = trans->dev;
+       mvm->trans = trans;
+       mvm->cfg = cfg;
+       mvm->fw = fw;
+       mvm->hw = hw;
+
+       mutex_init(&mvm->mutex);
+       spin_lock_init(&mvm->async_handlers_lock);
+       INIT_LIST_HEAD(&mvm->time_event_list);
+       INIT_LIST_HEAD(&mvm->async_handlers_list);
+       spin_lock_init(&mvm->time_event_lock);
+
+       INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
+       INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
+       INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
+
+       SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
+
+       /*
+        * Populate the state variables that the transport layer needs
+        * to know about.
+        */
+       trans_cfg.op_mode = op_mode;
+       trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
+       trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
+       trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K;
+
+       /* TODO: this should really be a TLV */
+       if (cfg->device_family == IWL_DEVICE_FAMILY_7000)
+               trans_cfg.bc_table_dword = true;
+
+       if (!iwlwifi_mod_params.wd_disable)
+               trans_cfg.queue_watchdog_timeout = cfg->base_params->wd_timeout;
+       else
+               trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED;
+
+       trans_cfg.command_names = iwl_mvm_cmd_strings;
+
+       trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE;
+       trans_cfg.cmd_fifo = IWL_MVM_CMD_FIFO;
+
+       snprintf(mvm->hw->wiphy->fw_version,
+                sizeof(mvm->hw->wiphy->fw_version),
+                "%s", fw->fw_version);
+
+       /* Configure transport layer */
+       iwl_trans_configure(mvm->trans, &trans_cfg);
+
+       trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
+       trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
+
+       /* set up notification wait support */
+       iwl_notification_wait_init(&mvm->notif_wait);
+
+       /* Init phy db */
+       mvm->phy_db = iwl_phy_db_init(trans);
+       if (!mvm->phy_db) {
+               IWL_ERR(mvm, "Cannot init phy_db\n");
+               goto out_free;
+       }
+
+       IWL_INFO(mvm, "Detected %s, REV=0x%X\n",
+                mvm->cfg->name, mvm->trans->hw_rev);
+
+       err = iwl_trans_start_hw(mvm->trans);
+       if (err)
+               goto out_free;
+
+       mutex_lock(&mvm->mutex);
+       err = iwl_run_init_mvm_ucode(mvm, true);
+       mutex_unlock(&mvm->mutex);
+       if (err && !iwlmvm_mod_params.init_dbg) {
+               IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
+               goto out_free;
+       }
+
+       /* Stop the hw after the ALIVE and NVM has been read */
+       if (!iwlmvm_mod_params.init_dbg)
+               iwl_trans_stop_hw(mvm->trans, false);
+
+       scan_size = sizeof(struct iwl_scan_cmd) +
+               mvm->fw->ucode_capa.max_probe_length +
+               (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel));
+       mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL);
+       if (!mvm->scan_cmd)
+               goto out_free;
+
+       err = iwl_mvm_mac_setup_register(mvm);
+       if (err)
+               goto out_free;
+
+       err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir);
+       if (err)
+               goto out_unregister;
+
+       return op_mode;
+
+ out_unregister:
+       ieee80211_unregister_hw(mvm->hw);
+ out_free:
+       iwl_phy_db_free(mvm->phy_db);
+       kfree(mvm->scan_cmd);
+       kfree(mvm->eeprom_blob);
+       iwl_trans_stop_hw(trans, true);
+       ieee80211_free_hw(mvm->hw);
+       return NULL;
+}
+
+static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       int i;
+
+       iwl_mvm_leds_exit(mvm);
+
+       ieee80211_unregister_hw(mvm->hw);
+
+       kfree(mvm->scan_cmd);
+
+       iwl_trans_stop_hw(mvm->trans, true);
+
+       iwl_phy_db_free(mvm->phy_db);
+       mvm->phy_db = NULL;
+
+       kfree(mvm->eeprom_blob);
+       iwl_free_nvm_data(mvm->nvm_data);
+       for (i = 0; i < NVM_NUM_OF_SECTIONS; i++)
+               kfree(mvm->nvm_sections[i].data);
+
+       ieee80211_free_hw(mvm->hw);
+}
+
+struct iwl_async_handler_entry {
+       struct list_head list;
+       struct iwl_rx_cmd_buffer rxb;
+       int (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                 struct iwl_device_cmd *cmd);
+};
+
+void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm)
+{
+       struct iwl_async_handler_entry *entry, *tmp;
+
+       spin_lock_bh(&mvm->async_handlers_lock);
+       list_for_each_entry_safe(entry, tmp, &mvm->async_handlers_list, list) {
+               iwl_free_rxb(&entry->rxb);
+               list_del(&entry->list);
+               kfree(entry);
+       }
+       spin_unlock_bh(&mvm->async_handlers_lock);
+}
+
+static void iwl_mvm_async_handlers_wk(struct work_struct *wk)
+{
+       struct iwl_mvm *mvm =
+               container_of(wk, struct iwl_mvm, async_handlers_wk);
+       struct iwl_async_handler_entry *entry, *tmp;
+       struct list_head local_list;
+
+       INIT_LIST_HEAD(&local_list);
+
+       /* Ensure that we are not in stop flow (check iwl_mvm_mac_stop) */
+       mutex_lock(&mvm->mutex);
+
+       /*
+        * Sync with Rx path with a lock. Remove all the entries from this list,
+        * add them to a local one (lock free), and then handle them.
+        */
+       spin_lock_bh(&mvm->async_handlers_lock);
+       list_splice_init(&mvm->async_handlers_list, &local_list);
+       spin_unlock_bh(&mvm->async_handlers_lock);
+
+       list_for_each_entry_safe(entry, tmp, &local_list, list) {
+               if (entry->fn(mvm, &entry->rxb, NULL))
+                       IWL_WARN(mvm,
+                                "returned value from ASYNC handlers are ignored\n");
+               iwl_free_rxb(&entry->rxb);
+               list_del(&entry->list);
+               kfree(entry);
+       }
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode,
+                              struct iwl_rx_cmd_buffer *rxb,
+                              struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       u8 i;
+
+       /*
+        * Do the notification wait before RX handlers so
+        * even if the RX handler consumes the RXB we have
+        * access to it in the notification wait entry.
+        */
+       iwl_notification_wait_notify(&mvm->notif_wait, pkt);
+
+       for (i = 0; i < ARRAY_SIZE(iwl_mvm_rx_handlers); i++) {
+               const struct iwl_rx_handlers *rx_h = &iwl_mvm_rx_handlers[i];
+               if (rx_h->cmd_id == pkt->hdr.cmd) {
+                       struct iwl_async_handler_entry *entry;
+                       if (!rx_h->async)
+                               return rx_h->fn(mvm, rxb, cmd);
+
+                       entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+                       /* we can't do much... */
+                       if (!entry)
+                               return 0;
+
+                       entry->rxb._page = rxb_steal_page(rxb);
+                       entry->rxb._offset = rxb->_offset;
+                       entry->rxb._rx_page_order = rxb->_rx_page_order;
+                       entry->fn = rx_h->fn;
+                       spin_lock(&mvm->async_handlers_lock);
+                       list_add_tail(&entry->list, &mvm->async_handlers_list);
+                       spin_unlock(&mvm->async_handlers_lock);
+                       schedule_work(&mvm->async_handlers_wk);
+               }
+       }
+
+       return 0;
+}
+
+static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       int mq = mvm->queue_to_mac80211[queue];
+
+       if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
+               return;
+
+       if (atomic_inc_return(&mvm->queue_stop_count[mq]) > 1) {
+               IWL_DEBUG_TX_QUEUES(mvm,
+                                   "queue %d (mac80211 %d) already stopped\n",
+                                   queue, mq);
+               return;
+       }
+
+       set_bit(mq, &mvm->transport_queue_stop);
+       ieee80211_stop_queue(mvm->hw, mq);
+}
+
+static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       int mq = mvm->queue_to_mac80211[queue];
+
+       if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
+               return;
+
+       if (atomic_dec_return(&mvm->queue_stop_count[mq]) > 0) {
+               IWL_DEBUG_TX_QUEUES(mvm,
+                                   "queue %d (mac80211 %d) already awake\n",
+                                   queue, mq);
+               return;
+       }
+
+       clear_bit(mq, &mvm->transport_queue_stop);
+
+       ieee80211_wake_queue(mvm->hw, mq);
+}
+
+static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+       if (state)
+               set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
+       else
+               clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
+
+       wiphy_rfkill_set_hw_state(mvm->hw->wiphy, state);
+}
+
+static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       struct ieee80211_tx_info *info;
+
+       info = IEEE80211_SKB_CB(skb);
+       iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
+       ieee80211_free_txskb(mvm->hw, skb);
+}
+
+static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+       iwl_mvm_dump_nic_error_log(mvm);
+
+       iwl_abort_notification_waits(&mvm->notif_wait);
+
+       /*
+        * If we're restarting already, don't cycle restarts.
+        * If INIT fw asserted, it will likely fail again.
+        * If WoWLAN fw asserted, don't restart either, mac80211
+        * can't recover this since we're already half suspended.
+        */
+       if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+               IWL_ERR(mvm, "Firmware error during reconfiguration! Abort.\n");
+       } else if (mvm->cur_ucode == IWL_UCODE_REGULAR &&
+                  iwlwifi_mod_params.restart_fw) {
+               /*
+                * This is a bit racy, but worst case we tell mac80211 about
+                * a stopped/aborted (sched) scan when that was already done
+                * which is not a problem. It is necessary to abort any scan
+                * here because mac80211 requires having the scan cleared
+                * before restarting.
+                * We'll reset the scan_status to NONE in restart cleanup in
+                * the next start() call from mac80211.
+                */
+               switch (mvm->scan_status) {
+               case IWL_MVM_SCAN_NONE:
+                       break;
+               case IWL_MVM_SCAN_OS:
+                       ieee80211_scan_completed(mvm->hw, true);
+                       break;
+               }
+
+               ieee80211_restart_hw(mvm->hw);
+       }
+}
+
+static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
+{
+       WARN_ON(1);
+}
+
+static const struct iwl_op_mode_ops iwl_mvm_ops = {
+       .start = iwl_op_mode_mvm_start,
+       .stop = iwl_op_mode_mvm_stop,
+       .rx = iwl_mvm_rx_dispatch,
+       .queue_full = iwl_mvm_stop_sw_queue,
+       .queue_not_full = iwl_mvm_wake_sw_queue,
+       .hw_rf_kill = iwl_mvm_set_hw_rfkill_state,
+       .free_skb = iwl_mvm_free_skb,
+       .nic_error = iwl_mvm_nic_error,
+       .cmd_queue_full = iwl_mvm_cmd_queue_full,
+       .nic_config = iwl_mvm_nic_config,
+};
diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
new file mode 100644 (file)
index 0000000..b428448
--- /dev/null
@@ -0,0 +1,292 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/mac80211.h>
+#include "fw-api.h"
+#include "mvm.h"
+
+/* Maps the driver specific channel width definition to the the fw values */
+static inline u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+       case NL80211_CHAN_WIDTH_20:
+               return PHY_VHT_CHANNEL_MODE20;
+       case NL80211_CHAN_WIDTH_40:
+               return PHY_VHT_CHANNEL_MODE40;
+       case NL80211_CHAN_WIDTH_80:
+               return PHY_VHT_CHANNEL_MODE80;
+       case NL80211_CHAN_WIDTH_160:
+               return PHY_VHT_CHANNEL_MODE160;
+       default:
+               WARN(1, "Invalid channel width=%u", chandef->width);
+               return PHY_VHT_CHANNEL_MODE20;
+       }
+}
+
+/*
+ * Maps the driver specific control channel position (relative to the center
+ * freq) definitions to the the fw values
+ */
+static inline u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->chan->center_freq - chandef->center_freq1) {
+       case -70:
+               return PHY_VHT_CTRL_POS_4_BELOW;
+       case -50:
+               return PHY_VHT_CTRL_POS_3_BELOW;
+       case -30:
+               return PHY_VHT_CTRL_POS_2_BELOW;
+       case -10:
+               return PHY_VHT_CTRL_POS_1_BELOW;
+       case  10:
+               return PHY_VHT_CTRL_POS_1_ABOVE;
+       case  30:
+               return PHY_VHT_CTRL_POS_2_ABOVE;
+       case  50:
+               return PHY_VHT_CTRL_POS_3_ABOVE;
+       case  70:
+               return PHY_VHT_CTRL_POS_4_ABOVE;
+       default:
+               WARN(1, "Invalid channel definition");
+       case 0:
+               /*
+                * The FW is expected to check the control channel position only
+                * when in HT/VHT and the channel width is not 20MHz. Return
+                * this value as the default one.
+                */
+               return PHY_VHT_CTRL_POS_1_BELOW;
+       }
+}
+
+/*
+ * Construct the generic fields of the PHY context command
+ */
+static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
+                                    struct iwl_phy_context_cmd *cmd,
+                                    u32 action, u32 apply_time)
+{
+       memset(cmd, 0, sizeof(struct iwl_phy_context_cmd));
+
+       cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
+                                                           ctxt->color));
+       cmd->action = cpu_to_le32(action);
+       cmd->apply_time = cpu_to_le32(apply_time);
+}
+
+/*
+ * Add the phy configuration to the PHY context command
+ */
+static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
+                                     struct iwl_phy_context_cmd *cmd,
+                                     struct cfg80211_chan_def *chandef,
+                                     u8 chains_static, u8 chains_dynamic)
+{
+       u8 valid_rx_chains, active_cnt, idle_cnt;
+
+       /* Set the channel info data */
+       cmd->ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
+             PHY_BAND_24 : PHY_BAND_5);
+
+       cmd->ci.channel = chandef->chan->hw_value;
+       cmd->ci.width = iwl_mvm_get_channel_width(chandef);
+       cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
+
+       /* Set rx the chains */
+
+       /* TODO:
+        * Need to add on chain noise calibration limitations, and
+        * BT coex considerations.
+        */
+       valid_rx_chains = mvm->nvm_data->valid_rx_ant;
+       idle_cnt = chains_static;
+       active_cnt = chains_dynamic;
+
+       cmd->rxchain_info = cpu_to_le32(valid_rx_chains <<
+                                       PHY_RX_CHAIN_VALID_POS);
+       cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
+       cmd->rxchain_info |= cpu_to_le32(active_cnt <<
+                                        PHY_RX_CHAIN_MIMO_CNT_POS);
+
+       cmd->txchain_info = cpu_to_le32(mvm->nvm_data->valid_tx_ant);
+}
+
+/*
+ * Send a command to apply the current phy configuration. The command is send
+ * only if something in the configuration changed: in case that this is the
+ * first time that the phy configuration is applied or in case that the phy
+ * configuration changed from the previous apply.
+ */
+static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
+                                 struct iwl_mvm_phy_ctxt *ctxt,
+                                 struct cfg80211_chan_def *chandef,
+                                 u8 chains_static, u8 chains_dynamic,
+                                 u32 action, u32 apply_time)
+{
+       struct iwl_phy_context_cmd cmd;
+       int ret;
+
+       /* Set the command header fields */
+       iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time);
+
+       /* Set the command data */
+       iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef,
+                                 chains_static, chains_dynamic);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC,
+                                  sizeof(struct iwl_phy_context_cmd),
+                                  &cmd);
+       if (ret)
+               IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
+       return ret;
+}
+
+
+struct phy_ctx_used_data {
+       unsigned long used[BITS_TO_LONGS(NUM_PHY_CTX)];
+};
+
+static void iwl_mvm_phy_ctx_used_iter(struct ieee80211_hw *hw,
+                                     struct ieee80211_chanctx_conf *ctx,
+                                     void *_data)
+{
+       struct phy_ctx_used_data *data = _data;
+       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+
+       __set_bit(phy_ctxt->id, data->used);
+}
+
+/*
+ * Send a command to add a PHY context based on the current HW configuration.
+ */
+int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+                        struct cfg80211_chan_def *chandef,
+                        u8 chains_static, u8 chains_dynamic)
+{
+       struct phy_ctx_used_data data = {
+               .used = { },
+       };
+
+       /*
+        * If this is a regular PHY context (not the ROC one)
+        * skip the ROC PHY context's ID.
+        */
+       if (ctxt != &mvm->phy_ctxt_roc)
+               __set_bit(mvm->phy_ctxt_roc.id, data.used);
+
+       lockdep_assert_held(&mvm->mutex);
+       ctxt->color++;
+
+       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+               ieee80211_iter_chan_contexts_atomic(
+                       mvm->hw, iwl_mvm_phy_ctx_used_iter, &data);
+
+               ctxt->id = find_first_zero_bit(data.used, NUM_PHY_CTX);
+               if (WARN_ONCE(ctxt->id == NUM_PHY_CTX,
+                             "Failed to init PHY context - no free ID!\n"))
+                       return -EIO;
+       }
+
+       ctxt->channel = chandef->chan;
+       return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
+                                     chains_static, chains_dynamic,
+                                     FW_CTXT_ACTION_ADD, 0);
+}
+
+/*
+ * Send a command to modify the PHY context based on the current HW
+ * configuration. Note that the function does not check that the configuration
+ * changed.
+ */
+int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+                            struct cfg80211_chan_def *chandef,
+                            u8 chains_static, u8 chains_dynamic)
+{
+       lockdep_assert_held(&mvm->mutex);
+
+       ctxt->channel = chandef->chan;
+       return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
+                                     chains_static, chains_dynamic,
+                                     FW_CTXT_ACTION_MODIFY, 0);
+}
+
+/*
+ * Send a command to the FW to remove the given phy context.
+ * Once the command is sent, regardless of success or failure, the context is
+ * marked as invalid
+ */
+void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
+{
+       struct iwl_phy_context_cmd cmd;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, FW_CTXT_ACTION_REMOVE, 0);
+       ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC,
+                                  sizeof(struct iwl_phy_context_cmd),
+                                  &cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send PHY remove: ctxt id=%d\n",
+                       ctxt->id);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c
new file mode 100644 (file)
index 0000000..6362873
--- /dev/null
@@ -0,0 +1,207 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <net/mac80211.h>
+
+#include "iwl-debug.h"
+#include "mvm.h"
+#include "iwl-modparams.h"
+#include "fw-api-power.h"
+
+#define POWER_KEEP_ALIVE_PERIOD_SEC    25
+
+static void iwl_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               struct iwl_powertable_cmd *cmd)
+{
+       struct ieee80211_hw *hw = mvm->hw;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_channel *chan;
+       int dtimper, dtimper_msec;
+       int keep_alive;
+       bool radar_detect = false;
+
+       cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                           mvmvif->color));
+       cmd->action = cpu_to_le32(FW_CTXT_ACTION_MODIFY);
+
+       if ((!vif->bss_conf.ps) ||
+           (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM))
+               return;
+
+       cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+
+       dtimper = hw->conf.ps_dtim_period ?: 1;
+
+       /* Check if radar detection is required on current channel */
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       WARN_ON(!chanctx_conf);
+       if (chanctx_conf) {
+               chan = chanctx_conf->def.chan;
+               radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
+       }
+       rcu_read_unlock();
+
+       /* Check skip over DTIM conditions */
+       if (!radar_detect && (dtimper <= 10) &&
+           (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP)) {
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_SLEEP_OVER_DTIM_MSK);
+               cmd->num_skip_dtim = 2;
+       }
+
+       /* Check that keep alive period is at least 3 * DTIM */
+       dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+       keep_alive = max_t(int, 3 * dtimper_msec,
+                          MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC);
+       keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
+
+       cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
+
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP) {
+               /* TODO: Also for D3 (device sleep / WoWLAN) */
+               cmd->rx_data_timeout = cpu_to_le32(10);
+               cmd->tx_data_timeout = cpu_to_le32(10);
+       } else {
+               cmd->rx_data_timeout = cpu_to_le32(50);
+               cmd->tx_data_timeout = cpu_to_le32(50);
+       }
+}
+
+int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_powertable_cmd cmd = {};
+
+       if (!iwlwifi_mod_params.power_save) {
+               IWL_DEBUG_POWER(mvm, "Power management is not allowed\n");
+               return 0;
+       }
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return 0;
+
+       iwl_power_build_cmd(mvm, vif, &cmd);
+
+       IWL_DEBUG_POWER(mvm,
+                       "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
+                       cmd.id_and_color, iwlmvm_mod_params.power_scheme,
+                       le16_to_cpu(cmd.flags));
+
+       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
+               IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n",
+                               le16_to_cpu(cmd.keep_alive_seconds));
+               IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
+                               le32_to_cpu(cmd.rx_data_timeout));
+               IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
+                               le32_to_cpu(cmd.tx_data_timeout));
+               IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n",
+                               le32_to_cpu(cmd.rx_data_timeout_uapsd));
+               IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
+                               le32_to_cpu(cmd.tx_data_timeout_uapsd));
+               IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
+                               cmd.lprx_rssi_threshold);
+               IWL_DEBUG_POWER(mvm, "DTIMs to skip = %u\n", cmd.num_skip_dtim);
+       }
+
+       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+                                   sizeof(cmd), &cmd);
+}
+
+int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_powertable_cmd cmd = {};
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (!iwlwifi_mod_params.power_save) {
+               IWL_DEBUG_POWER(mvm, "Power management is not allowed\n");
+               return 0;
+       }
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return 0;
+
+       cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                           mvmvif->color));
+       cmd.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY);
+
+       IWL_DEBUG_POWER(mvm,
+                       "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
+                       cmd.id_and_color, iwlmvm_mod_params.power_scheme,
+                       le16_to_cpu(cmd.flags));
+
+       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+                                   sizeof(cmd), &cmd);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+void iwl_power_get_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         struct iwl_powertable_cmd *cmd)
+{
+       iwl_power_build_cmd(mvm, vif, cmd);
+}
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c
new file mode 100644 (file)
index 0000000..2d4611a
--- /dev/null
@@ -0,0 +1,178 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/mac80211.h>
+#include "fw-api.h"
+#include "mvm.h"
+
+struct iwl_mvm_quota_iterator_data {
+       int n_interfaces[MAX_BINDINGS];
+       int colors[MAX_BINDINGS];
+       struct ieee80211_vif *new_vif;
+};
+
+static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
+                                  struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_quota_iterator_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u16 id;
+
+       /*
+        * We'll account for the new interface (if any) below,
+        * skip it here in case we're not called from within
+        * the add_interface callback (otherwise it won't show
+        * up in iteration)
+        */
+       if (vif == data->new_vif)
+               return;
+
+       if (!mvmvif->phy_ctxt)
+               return;
+
+       /* currently, PHY ID == binding ID */
+       id = mvmvif->phy_ctxt->id;
+
+       /* need at least one binding per PHY */
+       BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS);
+
+       if (WARN_ON_ONCE(id >= MAX_BINDINGS))
+               return;
+
+       if (data->colors[id] < 0)
+               data->colors[id] = mvmvif->phy_ctxt->color;
+       else
+               WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color);
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               if (vif->bss_conf.assoc)
+                       data->n_interfaces[id]++;
+               break;
+       case NL80211_IFTYPE_AP:
+               if (mvmvif->ap_active)
+                       data->n_interfaces[id]++;
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               data->n_interfaces[id]++;
+               break;
+       case NL80211_IFTYPE_P2P_DEVICE:
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               if (vif->bss_conf.ibss_joined)
+                       data->n_interfaces[id]++;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               break;
+       }
+}
+
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
+{
+       struct iwl_time_quota_cmd cmd;
+       int i, idx, ret;
+       struct iwl_mvm_quota_iterator_data data = {
+               .n_interfaces = {},
+               .colors = { -1, -1, -1, -1 },
+               .new_vif = newvif,
+       };
+
+       /* update all upon completion */
+       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+               return 0;
+
+       BUILD_BUG_ON(data.colors[MAX_BINDINGS - 1] != -1);
+
+       lockdep_assert_held(&mvm->mutex);
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_quota_iterator, &data);
+       if (newvif) {
+               data.new_vif = NULL;
+               iwl_mvm_quota_iterator(&data, newvif->addr, newvif);
+       }
+
+       for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
+               if (data.n_interfaces[i] <= 0)
+                       continue;
+
+               cmd.quotas[idx].id_and_color =
+                       cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
+               cmd.quotas[idx].quota = cpu_to_le32(100);
+               cmd.quotas[idx].max_duration = cpu_to_le32(1000);
+               idx++;
+       }
+
+       for (i = idx; i < MAX_BINDINGS; i++)
+               cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
+                                  sizeof(cmd), &cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
+       return ret;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
new file mode 100644 (file)
index 0000000..60a4291
--- /dev/null
@@ -0,0 +1,3096 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <net/mac80211.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+
+#include <linux/workqueue.h>
+#include "rs.h"
+#include "fw-api.h"
+#include "sta.h"
+#include "iwl-op-mode.h"
+#include "mvm.h"
+
+#define RS_NAME "iwl-mvm-rs"
+
+#define NUM_TRY_BEFORE_ANT_TOGGLE 1
+#define IWL_NUMBER_TRY      1
+#define IWL_HT_NUMBER_TRY   3
+
+#define IWL_RATE_MAX_WINDOW            62      /* # tx in history window */
+#define IWL_RATE_MIN_FAILURE_TH                6       /* min failures to calc tpt */
+#define IWL_RATE_MIN_SUCCESS_TH                8       /* min successes to calc tpt */
+
+/* max allowed rate miss before sync LQ cmd */
+#define IWL_MISSED_RATE_MAX            15
+/* max time to accum history 2 seconds */
+#define IWL_RATE_SCALE_FLUSH_INTVL   (3*HZ)
+
+static u8 rs_ht_to_legacy[] = {
+       IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
+       IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
+       IWL_RATE_6M_INDEX,
+       IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX,
+       IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX,
+       IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX,
+       IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX
+};
+
+static const u8 ant_toggle_lookup[] = {
+       /*ANT_NONE -> */ ANT_NONE,
+       /*ANT_A    -> */ ANT_B,
+       /*ANT_B    -> */ ANT_C,
+       /*ANT_AB   -> */ ANT_BC,
+       /*ANT_C    -> */ ANT_A,
+       /*ANT_AC   -> */ ANT_AB,
+       /*ANT_BC   -> */ ANT_AC,
+       /*ANT_ABC  -> */ ANT_ABC,
+};
+
+#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np)    \
+       [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,      \
+                                   IWL_RATE_SISO_##s##M_PLCP, \
+                                   IWL_RATE_MIMO2_##s##M_PLCP,\
+                                   IWL_RATE_MIMO3_##s##M_PLCP,\
+                                   IWL_RATE_##r##M_IEEE,      \
+                                   IWL_RATE_##ip##M_INDEX,    \
+                                   IWL_RATE_##in##M_INDEX,    \
+                                   IWL_RATE_##rp##M_INDEX,    \
+                                   IWL_RATE_##rn##M_INDEX,    \
+                                   IWL_RATE_##pp##M_INDEX,    \
+                                   IWL_RATE_##np##M_INDEX }
+
+/*
+ * Parameter order:
+ *   rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate
+ *
+ * If there isn't a valid next or previous rate then INV is used which
+ * maps to IWL_RATE_INVALID
+ *
+ */
+static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = {
+       IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2),    /*  1mbps */
+       IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5),          /*  2mbps */
+       IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11),        /*5.5mbps */
+       IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18),      /* 11mbps */
+       IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11),        /*  6mbps */
+       IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11),       /*  9mbps */
+       IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18),   /* 12mbps */
+       IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24),   /* 18mbps */
+       IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36),   /* 24mbps */
+       IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48),   /* 36mbps */
+       IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54),   /* 48mbps */
+       IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */
+       IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */
+       /* FIXME:RS:          ^^    should be INV (legacy) */
+};
+
+static inline u8 rs_extract_rate(u32 rate_n_flags)
+{
+       /* also works for HT because bits 7:6 are zero there */
+       return (u8)(rate_n_flags & RATE_LEGACY_RATE_MSK);
+}
+
+static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
+{
+       int idx = 0;
+
+       /* HT rate format */
+       if (rate_n_flags & RATE_MCS_HT_MSK) {
+               idx = rs_extract_rate(rate_n_flags);
+
+               if (idx >= IWL_RATE_MIMO3_6M_PLCP)
+                       idx = idx - IWL_RATE_MIMO3_6M_PLCP;
+               else if (idx >= IWL_RATE_MIMO2_6M_PLCP)
+                       idx = idx - IWL_RATE_MIMO2_6M_PLCP;
+
+               idx += IWL_FIRST_OFDM_RATE;
+               /* skip 9M not supported in ht*/
+               if (idx >= IWL_RATE_9M_INDEX)
+                       idx += 1;
+               if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE))
+                       return idx;
+
+       /* legacy rate format, search for match in table */
+       } else {
+               for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++)
+                       if (iwl_rates[idx].plcp ==
+                                       rs_extract_rate(rate_n_flags))
+                               return idx;
+       }
+
+       return -1;
+}
+
+static void rs_rate_scale_perform(struct iwl_mvm *mvm,
+                                  struct sk_buff *skb,
+                                  struct ieee80211_sta *sta,
+                                  struct iwl_lq_sta *lq_sta);
+static void rs_fill_link_cmd(struct iwl_mvm *mvm,
+                            struct iwl_lq_sta *lq_sta, u32 rate_n_flags);
+static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
+
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
+                            u32 *rate_n_flags, int index);
+#else
+static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
+                            u32 *rate_n_flags, int index)
+{}
+#endif
+
+/**
+ * The following tables contain the expected throughput metrics for all rates
+ *
+ *     1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits
+ *
+ * where invalid entries are zeros.
+ *
+ * CCK rates are only valid in legacy table and will only be used in G
+ * (2.4 GHz) band.
+ */
+
+static s32 expected_tpt_legacy[IWL_RATE_COUNT] = {
+       7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0
+};
+
+static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 42, 0,  76, 102, 124, 159, 183, 193, 202}, /* Norm */
+       {0, 0, 0, 0, 46, 0,  82, 110, 132, 168, 192, 202, 210}, /* SGI */
+       {0, 0, 0, 0, 47, 0,  91, 133, 171, 242, 305, 334, 362}, /* AGG */
+       {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */
+};
+
+static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0,  77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */
+       {0, 0, 0, 0,  83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */
+       {0, 0, 0, 0,  94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */
+       {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */
+};
+
+static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0,  74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */
+       {0, 0, 0, 0,  81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */
+       {0, 0, 0, 0,  89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */
+       {0, 0, 0, 0,  97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/
+};
+
+static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */
+       {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */
+       {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */
+       {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */
+};
+
+static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0,  99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */
+       {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */
+       {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */
+       {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */
+};
+
+static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 152, 0, 211, 239, 255, 279,  290,  294,  297}, /* Norm */
+       {0, 0, 0, 0, 160, 0, 219, 245, 261, 284,  294,  297,  300}, /* SGI */
+       {0, 0, 0, 0, 254, 0, 443, 584, 695, 868,  984, 1030, 1070}, /* AGG */
+       {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */
+};
+
+/* mbps, mcs */
+static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = {
+       {  "1", "BPSK DSSS"},
+       {  "2", "QPSK DSSS"},
+       {"5.5", "BPSK CCK"},
+       { "11", "QPSK CCK"},
+       {  "6", "BPSK 1/2"},
+       {  "9", "BPSK 1/2"},
+       { "12", "QPSK 1/2"},
+       { "18", "QPSK 3/4"},
+       { "24", "16QAM 1/2"},
+       { "36", "16QAM 3/4"},
+       { "48", "64QAM 2/3"},
+       { "54", "64QAM 3/4"},
+       { "60", "64QAM 5/6"},
+};
+
+#define MCS_INDEX_PER_STREAM   (8)
+
+static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window)
+{
+       window->data = 0;
+       window->success_counter = 0;
+       window->success_ratio = IWL_INVALID_VALUE;
+       window->counter = 0;
+       window->average_tpt = IWL_INVALID_VALUE;
+       window->stamp = 0;
+}
+
+static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
+{
+       return (ant_type & valid_antenna) == ant_type;
+}
+
+/*
+ *     removes the old data from the statistics. All data that is older than
+ *     TID_MAX_TIME_DIFF, will be deleted.
+ */
+static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time)
+{
+       /* The oldest age we want to keep */
+       u32 oldest_time = curr_time - TID_MAX_TIME_DIFF;
+
+       while (tl->queue_count &&
+              (tl->time_stamp < oldest_time)) {
+               tl->total -= tl->packet_count[tl->head];
+               tl->packet_count[tl->head] = 0;
+               tl->time_stamp += TID_QUEUE_CELL_SPACING;
+               tl->queue_count--;
+               tl->head++;
+               if (tl->head >= TID_QUEUE_MAX_SIZE)
+                       tl->head = 0;
+       }
+}
+
+/*
+ *     increment traffic load value for tid and also remove
+ *     any old values if passed the certain time period
+ */
+static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data,
+                          struct ieee80211_hdr *hdr)
+{
+       u32 curr_time = jiffies_to_msecs(jiffies);
+       u32 time_diff;
+       s32 index;
+       struct iwl_traffic_load *tl = NULL;
+       u8 tid;
+
+       if (ieee80211_is_data_qos(hdr->frame_control)) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               tid = qc[0] & 0xf;
+       } else {
+               return IWL_MAX_TID_COUNT;
+       }
+
+       if (unlikely(tid >= IWL_MAX_TID_COUNT))
+               return IWL_MAX_TID_COUNT;
+
+       tl = &lq_data->load[tid];
+
+       curr_time -= curr_time % TID_ROUND_VALUE;
+
+       /* Happens only for the first packet. Initialize the data */
+       if (!(tl->queue_count)) {
+               tl->total = 1;
+               tl->time_stamp = curr_time;
+               tl->queue_count = 1;
+               tl->head = 0;
+               tl->packet_count[0] = 1;
+               return IWL_MAX_TID_COUNT;
+       }
+
+       time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
+       index = time_diff / TID_QUEUE_CELL_SPACING;
+
+       /* The history is too long: remove data that is older than */
+       /* TID_MAX_TIME_DIFF */
+       if (index >= TID_QUEUE_MAX_SIZE)
+               rs_tl_rm_old_stats(tl, curr_time);
+
+       index = (tl->head + index) % TID_QUEUE_MAX_SIZE;
+       tl->packet_count[index] = tl->packet_count[index] + 1;
+       tl->total = tl->total + 1;
+
+       if ((index + 1) > tl->queue_count)
+               tl->queue_count = index + 1;
+
+       return tid;
+}
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+/**
+ * Program the device to use fixed rate for frame transmit
+ * This is for debugging/testing only
+ * once the device start use fixed rate, we need to reload the module
+ * to being back the normal operation.
+ */
+static void rs_program_fix_rate(struct iwl_mvm *mvm,
+                               struct iwl_lq_sta *lq_sta)
+{
+       lq_sta->active_legacy_rate = 0x0FFF;    /* 1 - 54 MBits, includes CCK */
+       lq_sta->active_siso_rate   = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
+       lq_sta->active_mimo2_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
+       lq_sta->active_mimo3_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
+
+       IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n",
+                      lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
+
+       if (lq_sta->dbg_fixed_rate) {
+               rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate);
+               iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false);
+       }
+}
+#endif
+
+/*
+       get the traffic load value for tid
+*/
+static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid)
+{
+       u32 curr_time = jiffies_to_msecs(jiffies);
+       u32 time_diff;
+       s32 index;
+       struct iwl_traffic_load *tl = NULL;
+
+       if (tid >= IWL_MAX_TID_COUNT)
+               return 0;
+
+       tl = &(lq_data->load[tid]);
+
+       curr_time -= curr_time % TID_ROUND_VALUE;
+
+       if (!(tl->queue_count))
+               return 0;
+
+       time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
+       index = time_diff / TID_QUEUE_CELL_SPACING;
+
+       /* The history is too long: remove data that is older than */
+       /* TID_MAX_TIME_DIFF */
+       if (index >= TID_QUEUE_MAX_SIZE)
+               rs_tl_rm_old_stats(tl, curr_time);
+
+       return tl->total;
+}
+
+static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
+                                     struct iwl_lq_sta *lq_data, u8 tid,
+                                     struct ieee80211_sta *sta)
+{
+       int ret = -EAGAIN;
+       u32 load;
+
+       load = rs_tl_get_load(lq_data, tid);
+
+       if ((iwlwifi_mod_params.auto_agg) || (load > IWL_AGG_LOAD_THRESHOLD)) {
+               IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n",
+                            sta->addr, tid);
+               ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
+               if (ret == -EAGAIN) {
+                       /*
+                        * driver and mac80211 is out of sync
+                        * this might be cause by reloading firmware
+                        * stop the tx ba session here
+                        */
+                       IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n",
+                               tid);
+                       ieee80211_stop_tx_ba_session(sta, tid);
+               }
+       } else {
+               IWL_DEBUG_HT(mvm,
+                            "Aggregation not enabled for tid %d because load = %u\n",
+                            tid, load);
+       }
+       return ret;
+}
+
+static void rs_tl_turn_on_agg(struct iwl_mvm *mvm, u8 tid,
+                             struct iwl_lq_sta *lq_data,
+                             struct ieee80211_sta *sta)
+{
+       if (tid < IWL_MAX_TID_COUNT)
+               rs_tl_turn_on_agg_for_tid(mvm, lq_data, tid, sta);
+       else
+               IWL_ERR(mvm, "tid exceeds max TID count: %d/%d\n",
+                       tid, IWL_MAX_TID_COUNT);
+}
+
+static inline int get_num_of_ant_from_rate(u32 rate_n_flags)
+{
+       return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) +
+              !!(rate_n_flags & RATE_MCS_ANT_B_MSK) +
+              !!(rate_n_flags & RATE_MCS_ANT_C_MSK);
+}
+
+/*
+ * Static function to get the expected throughput from an iwl_scale_tbl_info
+ * that wraps a NULL pointer check
+ */
+static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index)
+{
+       if (tbl->expected_tpt)
+               return tbl->expected_tpt[rs_index];
+       return 0;
+}
+
+/**
+ * rs_collect_tx_data - Update the success/failure sliding window
+ *
+ * We keep a sliding window of the last 62 packets transmitted
+ * at this rate.  window->data contains the bitmask of successful
+ * packets.
+ */
+static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
+                             int scale_index, int attempts, int successes)
+{
+       struct iwl_rate_scale_data *window = NULL;
+       static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1));
+       s32 fail_count, tpt;
+
+       if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
+               return -EINVAL;
+
+       /* Select window for current tx bit rate */
+       window = &(tbl->win[scale_index]);
+
+       /* Get expected throughput */
+       tpt = get_expected_tpt(tbl, scale_index);
+
+       /*
+        * Keep track of only the latest 62 tx frame attempts in this rate's
+        * history window; anything older isn't really relevant any more.
+        * If we have filled up the sliding window, drop the oldest attempt;
+        * if the oldest attempt (highest bit in bitmap) shows "success",
+        * subtract "1" from the success counter (this is the main reason
+        * we keep these bitmaps!).
+        */
+       while (attempts > 0) {
+               if (window->counter >= IWL_RATE_MAX_WINDOW) {
+                       /* remove earliest */
+                       window->counter = IWL_RATE_MAX_WINDOW - 1;
+
+                       if (window->data & mask) {
+                               window->data &= ~mask;
+                               window->success_counter--;
+                       }
+               }
+
+               /* Increment frames-attempted counter */
+               window->counter++;
+
+               /* Shift bitmap by one frame to throw away oldest history */
+               window->data <<= 1;
+
+               /* Mark the most recent #successes attempts as successful */
+               if (successes > 0) {
+                       window->success_counter++;
+                       window->data |= 0x1;
+                       successes--;
+               }
+
+               attempts--;
+       }
+
+       /* Calculate current success ratio, avoid divide-by-0! */
+       if (window->counter > 0)
+               window->success_ratio = 128 * (100 * window->success_counter)
+                                       / window->counter;
+       else
+               window->success_ratio = IWL_INVALID_VALUE;
+
+       fail_count = window->counter - window->success_counter;
+
+       /* Calculate average throughput, if we have enough history. */
+       if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) ||
+           (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH))
+               window->average_tpt = (window->success_ratio * tpt + 64) / 128;
+       else
+               window->average_tpt = IWL_INVALID_VALUE;
+
+       /* Tag this window as having been updated */
+       window->stamp = jiffies;
+
+       return 0;
+}
+
+/*
+ * Fill uCode API rate_n_flags field, based on "search" or "active" table.
+ */
+/* FIXME:RS:remove this function and put the flags statically in the table */
+static u32 rate_n_flags_from_tbl(struct iwl_mvm *mvm,
+                                struct iwl_scale_tbl_info *tbl,
+                                int index, u8 use_green)
+{
+       u32 rate_n_flags = 0;
+
+       if (is_legacy(tbl->lq_type)) {
+               rate_n_flags = iwl_rates[index].plcp;
+               if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE)
+                       rate_n_flags |= RATE_MCS_CCK_MSK;
+       } else if (is_Ht(tbl->lq_type)) {
+               if (index > IWL_LAST_OFDM_RATE) {
+                       IWL_ERR(mvm, "Invalid HT rate index %d\n", index);
+                       index = IWL_LAST_OFDM_RATE;
+               }
+               rate_n_flags = RATE_MCS_HT_MSK;
+
+               if (is_siso(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_siso;
+               else if (is_mimo2(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_mimo2;
+               else
+                       rate_n_flags |= iwl_rates[index].plcp_mimo3;
+       } else {
+               IWL_ERR(mvm, "Invalid tbl->lq_type %d\n", tbl->lq_type);
+       }
+
+       rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) &
+                                                    RATE_MCS_ANT_ABC_MSK);
+
+       if (is_Ht(tbl->lq_type)) {
+               if (tbl->is_ht40)
+                       rate_n_flags |= RATE_MCS_CHAN_WIDTH_40;
+               if (tbl->is_SGI)
+                       rate_n_flags |= RATE_MCS_SGI_MSK;
+
+               if (use_green) {
+                       rate_n_flags |= RATE_HT_MCS_GF_MSK;
+                       if (is_siso(tbl->lq_type) && tbl->is_SGI) {
+                               rate_n_flags &= ~RATE_MCS_SGI_MSK;
+                               IWL_ERR(mvm, "GF was set with SGI:SISO\n");
+                       }
+               }
+       }
+       return rate_n_flags;
+}
+
+/*
+ * Interpret uCode API's rate_n_flags format,
+ * fill "search" or "active" tx mode table.
+ */
+static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags,
+                                   enum ieee80211_band band,
+                                   struct iwl_scale_tbl_info *tbl,
+                                   int *rate_idx)
+{
+       u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK);
+       u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags);
+       u8 mcs;
+
+       memset(tbl, 0, sizeof(struct iwl_scale_tbl_info));
+       *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
+
+       if (*rate_idx  == IWL_RATE_INVALID) {
+               *rate_idx = -1;
+               return -EINVAL;
+       }
+       tbl->is_SGI = 0;        /* default legacy setup */
+       tbl->is_ht40 = 0;
+       tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS);
+       tbl->lq_type = LQ_NONE;
+       tbl->max_search = IWL_MAX_SEARCH;
+
+       /* legacy rate format */
+       if (!(rate_n_flags & RATE_MCS_HT_MSK)) {
+               if (num_of_ant == 1) {
+                       if (band == IEEE80211_BAND_5GHZ)
+                               tbl->lq_type = LQ_A;
+                       else
+                               tbl->lq_type = LQ_G;
+               }
+       /* HT rate format */
+       } else {
+               if (rate_n_flags & RATE_MCS_SGI_MSK)
+                       tbl->is_SGI = 1;
+
+               if (rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */
+                       tbl->is_ht40 = 1;
+
+               mcs = rs_extract_rate(rate_n_flags);
+
+               /* SISO */
+               if (mcs <= IWL_RATE_SISO_60M_PLCP) {
+                       if (num_of_ant == 1)
+                               tbl->lq_type = LQ_SISO; /*else NONE*/
+               /* MIMO2 */
+               } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) {
+                       if (num_of_ant == 2)
+                               tbl->lq_type = LQ_MIMO2;
+               /* MIMO3 */
+               } else {
+                       if (num_of_ant == 3) {
+                               tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
+                               tbl->lq_type = LQ_MIMO3;
+                       }
+               }
+       }
+       return 0;
+}
+
+/* switch to another antenna/antennas and return 1 */
+/* if no other valid antenna found, return 0 */
+static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
+                            struct iwl_scale_tbl_info *tbl)
+{
+       u8 new_ant_type;
+
+       if (!tbl->ant_type || tbl->ant_type > ANT_ABC)
+               return 0;
+
+       if (!rs_is_valid_ant(valid_ant, tbl->ant_type))
+               return 0;
+
+       new_ant_type = ant_toggle_lookup[tbl->ant_type];
+
+       while ((new_ant_type != tbl->ant_type) &&
+              !rs_is_valid_ant(valid_ant, new_ant_type))
+               new_ant_type = ant_toggle_lookup[new_ant_type];
+
+       if (new_ant_type == tbl->ant_type)
+               return 0;
+
+       tbl->ant_type = new_ant_type;
+       *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK;
+       *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS;
+       return 1;
+}
+
+/**
+ * Green-field mode is valid if the station supports it and
+ * there are no non-GF stations present in the BSS.
+ */
+static bool rs_use_green(struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv;
+
+       bool use_green = !(sta_priv->vif->bss_conf.ht_operation_mode &
+                               IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+
+       return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && use_green;
+}
+
+/**
+ * rs_get_supported_rates - get the available rates
+ *
+ * if management frame or broadcast frame only return
+ * basic available rates.
+ *
+ */
+static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta,
+                                 struct ieee80211_hdr *hdr,
+                                 enum iwl_table_type rate_type)
+{
+       if (is_legacy(rate_type)) {
+               return lq_sta->active_legacy_rate;
+       } else {
+               if (is_siso(rate_type))
+                       return lq_sta->active_siso_rate;
+               else if (is_mimo2(rate_type))
+                       return lq_sta->active_mimo2_rate;
+               else
+                       return lq_sta->active_mimo3_rate;
+       }
+}
+
+static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask,
+                               int rate_type)
+{
+       u8 high = IWL_RATE_INVALID;
+       u8 low = IWL_RATE_INVALID;
+
+       /* 802.11A or ht walks to the next literal adjacent rate in
+        * the rate table */
+       if (is_a_band(rate_type) || !is_legacy(rate_type)) {
+               int i;
+               u32 mask;
+
+               /* Find the previous rate that is in the rate mask */
+               i = index - 1;
+               for (mask = (1 << i); i >= 0; i--, mask >>= 1) {
+                       if (rate_mask & mask) {
+                               low = i;
+                               break;
+                       }
+               }
+
+               /* Find the next rate that is in the rate mask */
+               i = index + 1;
+               for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) {
+                       if (rate_mask & mask) {
+                               high = i;
+                               break;
+                       }
+               }
+
+               return (high << 8) | low;
+       }
+
+       low = index;
+       while (low != IWL_RATE_INVALID) {
+               low = iwl_rates[low].prev_rs;
+               if (low == IWL_RATE_INVALID)
+                       break;
+               if (rate_mask & (1 << low))
+                       break;
+               IWL_DEBUG_RATE(mvm, "Skipping masked lower rate: %d\n", low);
+       }
+
+       high = index;
+       while (high != IWL_RATE_INVALID) {
+               high = iwl_rates[high].next_rs;
+               if (high == IWL_RATE_INVALID)
+                       break;
+               if (rate_mask & (1 << high))
+                       break;
+               IWL_DEBUG_RATE(mvm, "Skipping masked higher rate: %d\n", high);
+       }
+
+       return (high << 8) | low;
+}
+
+static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
+                            struct iwl_scale_tbl_info *tbl,
+                            u8 scale_index, u8 ht_possible)
+{
+       s32 low;
+       u16 rate_mask;
+       u16 high_low;
+       u8 switch_to_legacy = 0;
+       u8 is_green = lq_sta->is_green;
+       struct iwl_mvm *mvm = lq_sta->drv;
+
+       /* check if we need to switch from HT to legacy rates.
+        * assumption is that mandatory rates (1Mbps or 6Mbps)
+        * are always supported (spec demand) */
+       if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) {
+               switch_to_legacy = 1;
+               scale_index = rs_ht_to_legacy[scale_index];
+               if (lq_sta->band == IEEE80211_BAND_5GHZ)
+                       tbl->lq_type = LQ_A;
+               else
+                       tbl->lq_type = LQ_G;
+
+               if (num_of_ant(tbl->ant_type) > 1)
+                       tbl->ant_type =
+                           first_antenna(mvm->nvm_data->valid_tx_ant);
+
+               tbl->is_ht40 = 0;
+               tbl->is_SGI = 0;
+               tbl->max_search = IWL_MAX_SEARCH;
+       }
+
+       rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type);
+
+       /* Mask with station rate restriction */
+       if (is_legacy(tbl->lq_type)) {
+               /* supp_rates has no CCK bits in A mode */
+               if (lq_sta->band == IEEE80211_BAND_5GHZ)
+                       rate_mask  = (u16)(rate_mask &
+                          (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
+               else
+                       rate_mask = (u16)(rate_mask & lq_sta->supp_rates);
+       }
+
+       /* If we switched from HT to legacy, check current rate */
+       if (switch_to_legacy && (rate_mask & (1 << scale_index))) {
+               low = scale_index;
+               goto out;
+       }
+
+       high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask,
+                                       tbl->lq_type);
+       low = high_low & 0xff;
+
+       if (low == IWL_RATE_INVALID)
+               low = scale_index;
+
+out:
+       return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green);
+}
+
+/*
+ * Simple function to compare two rate scale table types
+ */
+static bool table_type_matches(struct iwl_scale_tbl_info *a,
+                              struct iwl_scale_tbl_info *b)
+{
+       return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) &&
+               (a->is_SGI == b->is_SGI);
+}
+
+/*
+ * mac80211 sends us Tx status
+ */
+static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
+                        struct ieee80211_sta *sta, void *priv_sta,
+                        struct sk_buff *skb)
+{
+       int legacy_success;
+       int retries;
+       int rs_index, mac_index, i;
+       struct iwl_lq_sta *lq_sta = priv_sta;
+       struct iwl_lq_cmd *table;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_r;
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       enum mac80211_rate_control_flags mac_flags;
+       u32 tx_rate;
+       struct iwl_scale_tbl_info tbl_type;
+       struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
+
+       IWL_DEBUG_RATE_LIMIT(mvm,
+                            "get frame ack response, update rate scale window\n");
+
+       /* Treat uninitialized rate scaling data same as non-existing. */
+       if (!lq_sta) {
+               IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n");
+               return;
+       } else if (!lq_sta->drv) {
+               IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
+               return;
+       }
+
+       if (!ieee80211_is_data(hdr->frame_control) ||
+           info->flags & IEEE80211_TX_CTL_NO_ACK)
+               return;
+
+       /* This packet was aggregated but doesn't carry status info */
+       if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
+           !(info->flags & IEEE80211_TX_STAT_AMPDU))
+               return;
+
+       /*
+        * Ignore this Tx frame response if its initial rate doesn't match
+        * that of latest Link Quality command.  There may be stragglers
+        * from a previous Link Quality command, but we're no longer interested
+        * in those; they're either from the "active" mode while we're trying
+        * to check "search" mode, or a prior "search" mode after we've moved
+        * to a new "search" mode (which might become the new "active" mode).
+        */
+       table = &lq_sta->lq;
+       tx_rate = le32_to_cpu(table->rs_table[0]);
+       rs_get_tbl_info_from_mcs(tx_rate, info->band, &tbl_type, &rs_index);
+       if (info->band == IEEE80211_BAND_5GHZ)
+               rs_index -= IWL_FIRST_OFDM_RATE;
+       mac_flags = info->status.rates[0].flags;
+       mac_index = info->status.rates[0].idx;
+       /* For HT packets, map MCS to PLCP */
+       if (mac_flags & IEEE80211_TX_RC_MCS) {
+               /* Remove # of streams */
+               mac_index &= RATE_HT_MCS_RATE_CODE_MSK;
+               if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE))
+                       mac_index++;
+               /*
+                * mac80211 HT index is always zero-indexed; we need to move
+                * HT OFDM rates after CCK rates in 2.4 GHz band
+                */
+               if (info->band == IEEE80211_BAND_2GHZ)
+                       mac_index += IWL_FIRST_OFDM_RATE;
+       }
+       /* Here we actually compare this rate to the latest LQ command */
+       if ((mac_index < 0) ||
+           (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
+           (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) ||
+           (tbl_type.ant_type != info->status.antenna) ||
+           (!!(tx_rate & RATE_MCS_HT_MSK) !=
+                               !!(mac_flags & IEEE80211_TX_RC_MCS)) ||
+           (!!(tx_rate & RATE_HT_MCS_GF_MSK) !=
+                               !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
+           (rs_index != mac_index)) {
+               IWL_DEBUG_RATE(mvm,
+                              "initial rate %d does not match %d (0x%x)\n",
+                              mac_index, rs_index, tx_rate);
+               /*
+                * Since rates mis-match, the last LQ command may have failed.
+                * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with
+                * ... driver.
+                */
+               lq_sta->missed_rate_counter++;
+               if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) {
+                       lq_sta->missed_rate_counter = 0;
+                       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
+               }
+               /* Regardless, ignore this status info for outdated rate */
+               return;
+       } else
+               /* Rate did match, so reset the missed_rate_counter */
+               lq_sta->missed_rate_counter = 0;
+
+       /* Figure out if rate scale algorithm is in active or search table */
+       if (table_type_matches(&tbl_type,
+                              &(lq_sta->lq_info[lq_sta->active_tbl]))) {
+               curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+               other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+       } else if (table_type_matches(
+                       &tbl_type, &lq_sta->lq_info[1 - lq_sta->active_tbl])) {
+               curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+               other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+       } else {
+               IWL_DEBUG_RATE(mvm,
+                              "Neither active nor search matches tx rate\n");
+               tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+               IWL_DEBUG_RATE(mvm, "active- lq:%x, ant:%x, SGI:%d\n",
+                              tmp_tbl->lq_type, tmp_tbl->ant_type,
+                              tmp_tbl->is_SGI);
+               tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+               IWL_DEBUG_RATE(mvm, "search- lq:%x, ant:%x, SGI:%d\n",
+                              tmp_tbl->lq_type, tmp_tbl->ant_type,
+                              tmp_tbl->is_SGI);
+               IWL_DEBUG_RATE(mvm, "actual- lq:%x, ant:%x, SGI:%d\n",
+                              tbl_type.lq_type, tbl_type.ant_type,
+                              tbl_type.is_SGI);
+               /*
+                * no matching table found, let's by-pass the data collection
+                * and continue to perform rate scale to find the rate table
+                */
+               rs_stay_in_table(lq_sta, true);
+               goto done;
+       }
+
+       /*
+        * Updating the frame history depends on whether packets were
+        * aggregated.
+        *
+        * For aggregation, all packets were transmitted at the same rate, the
+        * first index into rate scale table.
+        */
+       if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+               tx_rate = le32_to_cpu(table->rs_table[0]);
+               rs_get_tbl_info_from_mcs(tx_rate, info->band, &tbl_type,
+                                        &rs_index);
+               rs_collect_tx_data(curr_tbl, rs_index,
+                                  info->status.ampdu_len,
+                                  info->status.ampdu_ack_len);
+
+               /* Update success/fail counts if not searching for new mode */
+               if (lq_sta->stay_in_tbl) {
+                       lq_sta->total_success += info->status.ampdu_ack_len;
+                       lq_sta->total_failed += (info->status.ampdu_len -
+                                       info->status.ampdu_ack_len);
+               }
+       } else {
+       /*
+        * For legacy, update frame history with for each Tx retry.
+        */
+               retries = info->status.rates[0].count - 1;
+               /* HW doesn't send more than 15 retries */
+               retries = min(retries, 15);
+
+               /* The last transmission may have been successful */
+               legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK);
+               /* Collect data for each rate used during failed TX attempts */
+               for (i = 0; i <= retries; ++i) {
+                       tx_rate = le32_to_cpu(table->rs_table[i]);
+                       rs_get_tbl_info_from_mcs(tx_rate, info->band,
+                                                &tbl_type, &rs_index);
+                       /*
+                        * Only collect stats if retried rate is in the same RS
+                        * table as active/search.
+                        */
+                       if (table_type_matches(&tbl_type, curr_tbl))
+                               tmp_tbl = curr_tbl;
+                       else if (table_type_matches(&tbl_type, other_tbl))
+                               tmp_tbl = other_tbl;
+                       else
+                               continue;
+                       rs_collect_tx_data(tmp_tbl, rs_index, 1,
+                                          i < retries ? 0 : legacy_success);
+               }
+
+               /* Update success/fail counts if not searching for new mode */
+               if (lq_sta->stay_in_tbl) {
+                       lq_sta->total_success += legacy_success;
+                       lq_sta->total_failed += retries + (1 - legacy_success);
+               }
+       }
+       /* The last TX rate is cached in lq_sta; it's set in if/else above */
+       lq_sta->last_rate_n_flags = tx_rate;
+done:
+       /* See if there's a better rate or modulation mode to try. */
+       if (sta && sta->supp_rates[sband->band])
+               rs_rate_scale_perform(mvm, skb, sta, lq_sta);
+}
+
+/*
+ * Begin a period of staying with a selected modulation mode.
+ * Set "stay_in_tbl" flag to prevent any mode switches.
+ * Set frame tx success limits according to legacy vs. high-throughput,
+ * and reset overall (spanning all rates) tx success history statistics.
+ * These control how long we stay using same modulation mode before
+ * searching for a new mode.
+ */
+static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy,
+                                struct iwl_lq_sta *lq_sta)
+{
+       IWL_DEBUG_RATE(mvm, "we are staying in the same table\n");
+       lq_sta->stay_in_tbl = 1;        /* only place this gets set */
+       if (is_legacy) {
+               lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT;
+               lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT;
+               lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT;
+       } else {
+               lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT;
+               lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT;
+               lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT;
+       }
+       lq_sta->table_count = 0;
+       lq_sta->total_failed = 0;
+       lq_sta->total_success = 0;
+       lq_sta->flush_timer = jiffies;
+       lq_sta->action_counter = 0;
+}
+
+/*
+ * Find correct throughput table for given mode of modulation
+ */
+static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
+                                     struct iwl_scale_tbl_info *tbl)
+{
+       /* Used to choose among HT tables */
+       s32 (*ht_tbl_pointer)[IWL_RATE_COUNT];
+
+       /* Check for invalid LQ type */
+       if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) {
+               tbl->expected_tpt = expected_tpt_legacy;
+               return;
+       }
+
+       /* Legacy rates have only one table */
+       if (is_legacy(tbl->lq_type)) {
+               tbl->expected_tpt = expected_tpt_legacy;
+               return;
+       }
+
+       /* Choose among many HT tables depending on number of streams
+        * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation
+        * status */
+       if (is_siso(tbl->lq_type) && !tbl->is_ht40)
+               ht_tbl_pointer = expected_tpt_siso20MHz;
+       else if (is_siso(tbl->lq_type))
+               ht_tbl_pointer = expected_tpt_siso40MHz;
+       else if (is_mimo2(tbl->lq_type) && !tbl->is_ht40)
+               ht_tbl_pointer = expected_tpt_mimo2_20MHz;
+       else if (is_mimo2(tbl->lq_type))
+               ht_tbl_pointer = expected_tpt_mimo2_40MHz;
+       else if (is_mimo3(tbl->lq_type) && !tbl->is_ht40)
+               ht_tbl_pointer = expected_tpt_mimo3_20MHz;
+       else /* if (is_mimo3(tbl->lq_type)) <-- must be true */
+               ht_tbl_pointer = expected_tpt_mimo3_40MHz;
+
+       if (!tbl->is_SGI && !lq_sta->is_agg)            /* Normal */
+               tbl->expected_tpt = ht_tbl_pointer[0];
+       else if (tbl->is_SGI && !lq_sta->is_agg)        /* SGI */
+               tbl->expected_tpt = ht_tbl_pointer[1];
+       else if (!tbl->is_SGI && lq_sta->is_agg)        /* AGG */
+               tbl->expected_tpt = ht_tbl_pointer[2];
+       else                                            /* AGG+SGI */
+               tbl->expected_tpt = ht_tbl_pointer[3];
+}
+
+/*
+ * Find starting rate for new "search" high-throughput mode of modulation.
+ * Goal is to find lowest expected rate (under perfect conditions) that is
+ * above the current measured throughput of "active" mode, to give new mode
+ * a fair chance to prove itself without too many challenges.
+ *
+ * This gets called when transitioning to more aggressive modulation
+ * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive
+ * (i.e. MIMO to SISO).  When moving to MIMO, bit rate will typically need
+ * to decrease to match "active" throughput.  When moving from MIMO to SISO,
+ * bit rate will typically need to increase, but not if performance was bad.
+ */
+static s32 rs_get_best_rate(struct iwl_mvm *mvm,
+                           struct iwl_lq_sta *lq_sta,
+                           struct iwl_scale_tbl_info *tbl,     /* "search" */
+                           u16 rate_mask, s8 index)
+{
+       /* "active" values */
+       struct iwl_scale_tbl_info *active_tbl =
+           &(lq_sta->lq_info[lq_sta->active_tbl]);
+       s32 active_sr = active_tbl->win[index].success_ratio;
+       s32 active_tpt = active_tbl->expected_tpt[index];
+
+       /* expected "search" throughput */
+       s32 *tpt_tbl = tbl->expected_tpt;
+
+       s32 new_rate, high, low, start_hi;
+       u16 high_low;
+       s8 rate = index;
+
+       new_rate = high = low = start_hi = IWL_RATE_INVALID;
+
+       while (1) {
+               high_low = rs_get_adjacent_rate(mvm, rate, rate_mask,
+                                               tbl->lq_type);
+
+               low = high_low & 0xff;
+               high = (high_low >> 8) & 0xff;
+
+               /*
+                * Lower the "search" bit rate, to give new "search" mode
+                * approximately the same throughput as "active" if:
+                *
+                * 1) "Active" mode has been working modestly well (but not
+                *    great), and expected "search" throughput (under perfect
+                *    conditions) at candidate rate is above the actual
+                *    measured "active" throughput (but less than expected
+                *    "active" throughput under perfect conditions).
+                * OR
+                * 2) "Active" mode has been working perfectly or very well
+                *    and expected "search" throughput (under perfect
+                *    conditions) at candidate rate is above expected
+                *    "active" throughput (under perfect conditions).
+                */
+               if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) &&
+                    ((active_sr > IWL_RATE_DECREASE_TH) &&
+                     (active_sr <= IWL_RATE_HIGH_TH) &&
+                     (tpt_tbl[rate] <= active_tpt))) ||
+                   ((active_sr >= IWL_RATE_SCALE_SWITCH) &&
+                    (tpt_tbl[rate] > active_tpt))) {
+                       /* (2nd or later pass)
+                        * If we've already tried to raise the rate, and are
+                        * now trying to lower it, use the higher rate. */
+                       if (start_hi != IWL_RATE_INVALID) {
+                               new_rate = start_hi;
+                               break;
+                       }
+
+                       new_rate = rate;
+
+                       /* Loop again with lower rate */
+                       if (low != IWL_RATE_INVALID)
+                               rate = low;
+
+                       /* Lower rate not available, use the original */
+                       else
+                               break;
+
+               /* Else try to raise the "search" rate to match "active" */
+               } else {
+                       /* (2nd or later pass)
+                        * If we've already tried to lower the rate, and are
+                        * now trying to raise it, use the lower rate. */
+                       if (new_rate != IWL_RATE_INVALID)
+                               break;
+
+                       /* Loop again with higher rate */
+                       else if (high != IWL_RATE_INVALID) {
+                               start_hi = high;
+                               rate = high;
+
+                       /* Higher rate not available, use the original */
+                       } else {
+                               new_rate = rate;
+                               break;
+                       }
+               }
+       }
+
+       return new_rate;
+}
+
+static bool iwl_is_ht40_tx_allowed(struct iwl_mvm *mvm,
+                           struct ieee80211_sta_ht_cap *ht_cap)
+{
+       /*
+        * Remainder of this function checks ht_cap, but if it's
+        * NULL then we can do HT40 (special case for RXON)
+        */
+       if (!ht_cap)
+               return true;
+
+       if (!ht_cap->ht_supported)
+               return false;
+
+       if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+               return false;
+
+       return true;
+}
+
+/*
+ * Set up search table for MIMO2
+ */
+static int rs_switch_to_mimo2(struct iwl_mvm *mvm,
+                            struct iwl_lq_sta *lq_sta,
+                            struct ieee80211_sta *sta,
+                            struct iwl_scale_tbl_info *tbl, int index)
+{
+       u16 rate_mask;
+       s32 rate;
+       s8 is_green = lq_sta->is_green;
+
+       if (!sta->ht_cap.ht_supported)
+               return -1;
+
+       if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2)
+                                               == WLAN_HT_CAP_SM_PS_STATIC)
+               return -1;
+
+       /* Need both Tx chains/antennas to support MIMO */
+       if (num_of_ant(mvm->nvm_data->valid_tx_ant) < 2)
+               return -1;
+
+       IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO2\n");
+
+       tbl->lq_type = LQ_MIMO2;
+       tbl->action = 0;
+       tbl->max_search = IWL_MAX_SEARCH;
+       rate_mask = lq_sta->active_mimo2_rate;
+
+       if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap))
+               tbl->is_ht40 = 1;
+       else
+               tbl->is_ht40 = 0;
+
+       rs_set_expected_tpt_table(lq_sta, tbl);
+
+       rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index);
+
+       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 best rate %d mask %X\n",
+                      rate, rate_mask);
+       if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
+               IWL_DEBUG_RATE(mvm, "Can't switch with index %d rate mask %x\n",
+                              rate, rate_mask);
+               return -1;
+       }
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green);
+
+       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n",
+                      tbl->current_rate, is_green);
+       return 0;
+}
+
+/*
+ * Set up search table for MIMO3
+ */
+static int rs_switch_to_mimo3(struct iwl_mvm *mvm,
+                            struct iwl_lq_sta *lq_sta,
+                            struct ieee80211_sta *sta,
+                            struct iwl_scale_tbl_info *tbl, int index)
+{
+       u16 rate_mask;
+       s32 rate;
+       s8 is_green = lq_sta->is_green;
+
+       if (!sta->ht_cap.ht_supported)
+               return -1;
+
+       if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2)
+                                               == WLAN_HT_CAP_SM_PS_STATIC)
+               return -1;
+
+       /* Need both Tx chains/antennas to support MIMO */
+       if (num_of_ant(mvm->nvm_data->valid_tx_ant) < 3)
+               return -1;
+
+       IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO3\n");
+
+       tbl->lq_type = LQ_MIMO3;
+       tbl->action = 0;
+       tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
+       rate_mask = lq_sta->active_mimo3_rate;
+
+       if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap))
+               tbl->is_ht40 = 1;
+       else
+               tbl->is_ht40 = 0;
+
+       rs_set_expected_tpt_table(lq_sta, tbl);
+
+       rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index);
+
+       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 best rate %d mask %X\n",
+                      rate, rate_mask);
+       if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
+               IWL_DEBUG_RATE(mvm, "Can't switch with index %d rate mask %x\n",
+                              rate, rate_mask);
+               return -1;
+       }
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green);
+
+       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n",
+                      tbl->current_rate, is_green);
+       return 0;
+}
+
+/*
+ * Set up search table for SISO
+ */
+static int rs_switch_to_siso(struct iwl_mvm *mvm,
+                            struct iwl_lq_sta *lq_sta,
+                            struct ieee80211_sta *sta,
+                            struct iwl_scale_tbl_info *tbl, int index)
+{
+       u16 rate_mask;
+       u8 is_green = lq_sta->is_green;
+       s32 rate;
+
+       if (!sta->ht_cap.ht_supported)
+               return -1;
+
+       IWL_DEBUG_RATE(mvm, "LQ: try to switch to SISO\n");
+
+       tbl->lq_type = LQ_SISO;
+       tbl->action = 0;
+       tbl->max_search = IWL_MAX_SEARCH;
+       rate_mask = lq_sta->active_siso_rate;
+
+       if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap))
+               tbl->is_ht40 = 1;
+       else
+               tbl->is_ht40 = 0;
+
+       if (is_green)
+               tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/
+
+       rs_set_expected_tpt_table(lq_sta, tbl);
+       rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index);
+
+       IWL_DEBUG_RATE(mvm, "LQ: get best rate %d mask %X\n", rate, rate_mask);
+       if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
+               IWL_DEBUG_RATE(mvm,
+                              "can not switch with index %d rate mask %x\n",
+                              rate, rate_mask);
+               return -1;
+       }
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green);
+       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n",
+                      tbl->current_rate, is_green);
+       return 0;
+}
+
+/*
+ * Try to switch to new modulation mode from legacy
+ */
+static int rs_move_legacy_other(struct iwl_mvm *mvm,
+                               struct iwl_lq_sta *lq_sta,
+                               struct ieee80211_sta *sta,
+                               int index)
+{
+       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+       struct iwl_scale_tbl_info *search_tbl =
+                               &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+       struct iwl_rate_scale_data *window = &(tbl->win[index]);
+       u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+                 (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+       u8 start_action;
+       u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+       u8 tx_chains_num = num_of_ant(valid_tx_ant);
+       int ret;
+       u8 update_search_tbl_counter = 0;
+
+       start_action = tbl->action;
+       while (1) {
+               lq_sta->action_counter++;
+               switch (tbl->action) {
+               case IWL_LEGACY_SWITCH_ANTENNA1:
+               case IWL_LEGACY_SWITCH_ANTENNA2:
+                       IWL_DEBUG_RATE(mvm, "LQ: Legacy toggle Antenna\n");
+
+                       if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 &&
+                            tx_chains_num <= 1) ||
+                           (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 &&
+                            tx_chains_num <= 2))
+                               break;
+
+                       /* Don't change antenna if success has been great */
+                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+                               break;
+
+                       /* Set up search table to try other antenna */
+                       memcpy(search_tbl, tbl, sz);
+
+                       if (rs_toggle_antenna(valid_tx_ant,
+                                             &search_tbl->current_rate,
+                                             search_tbl)) {
+                               update_search_tbl_counter = 1;
+                               rs_set_expected_tpt_table(lq_sta, search_tbl);
+                               goto out;
+                       }
+                       break;
+               case IWL_LEGACY_SWITCH_SISO:
+                       IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to SISO\n");
+
+                       /* Set up search table to try SISO */
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+                       ret = rs_switch_to_siso(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret) {
+                               lq_sta->action_counter = 0;
+                               goto out;
+                       }
+
+                       break;
+               case IWL_LEGACY_SWITCH_MIMO2_AB:
+               case IWL_LEGACY_SWITCH_MIMO2_AC:
+               case IWL_LEGACY_SWITCH_MIMO2_BC:
+                       IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to MIMO2\n");
+
+                       /* Set up search table to try MIMO */
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+
+                       if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB)
+                               search_tbl->ant_type = ANT_AB;
+                       else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC)
+                               search_tbl->ant_type = ANT_AC;
+                       else
+                               search_tbl->ant_type = ANT_BC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo2(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret) {
+                               lq_sta->action_counter = 0;
+                               goto out;
+                       }
+                       break;
+
+               case IWL_LEGACY_SWITCH_MIMO3_ABC:
+                       IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to MIMO3\n");
+
+                       /* Set up search table to try MIMO3 */
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+
+                       search_tbl->ant_type = ANT_ABC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret) {
+                               lq_sta->action_counter = 0;
+                               goto out;
+                       }
+                       break;
+               }
+               tbl->action++;
+               if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+                       tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+
+               if (tbl->action == start_action)
+                       break;
+       }
+       search_tbl->lq_type = LQ_NONE;
+       return 0;
+
+out:
+       lq_sta->search_better_tbl = 1;
+       tbl->action++;
+       if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+               tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+       if (update_search_tbl_counter)
+               search_tbl->action = tbl->action;
+       return 0;
+}
+
+/*
+ * Try to switch to new modulation mode from SISO
+ */
+static int rs_move_siso_to_other(struct iwl_mvm *mvm,
+                                struct iwl_lq_sta *lq_sta,
+                                struct ieee80211_sta *sta, int index)
+{
+       u8 is_green = lq_sta->is_green;
+       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+       struct iwl_scale_tbl_info *search_tbl =
+                               &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+       struct iwl_rate_scale_data *window = &(tbl->win[index]);
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+                 (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+       u8 start_action;
+       u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+       u8 tx_chains_num = num_of_ant(valid_tx_ant);
+       u8 update_search_tbl_counter = 0;
+       int ret;
+
+       start_action = tbl->action;
+       while (1) {
+               lq_sta->action_counter++;
+               switch (tbl->action) {
+               case IWL_SISO_SWITCH_ANTENNA1:
+               case IWL_SISO_SWITCH_ANTENNA2:
+                       IWL_DEBUG_RATE(mvm, "LQ: SISO toggle Antenna\n");
+                       if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 &&
+                            tx_chains_num <= 1) ||
+                           (tbl->action == IWL_SISO_SWITCH_ANTENNA2 &&
+                            tx_chains_num <= 2))
+                               break;
+
+                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+                               break;
+
+                       memcpy(search_tbl, tbl, sz);
+                       if (rs_toggle_antenna(valid_tx_ant,
+                                             &search_tbl->current_rate,
+                                             search_tbl)) {
+                               update_search_tbl_counter = 1;
+                               goto out;
+                       }
+                       break;
+               case IWL_SISO_SWITCH_MIMO2_AB:
+               case IWL_SISO_SWITCH_MIMO2_AC:
+               case IWL_SISO_SWITCH_MIMO2_BC:
+                       IWL_DEBUG_RATE(mvm, "LQ: SISO switch to MIMO2\n");
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+
+                       if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB)
+                               search_tbl->ant_type = ANT_AB;
+                       else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC)
+                               search_tbl->ant_type = ANT_AC;
+                       else
+                               search_tbl->ant_type = ANT_BC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo2(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret)
+                               goto out;
+                       break;
+               case IWL_SISO_SWITCH_GI:
+                       if (!tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_20))
+                               break;
+                       if (tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_40))
+                               break;
+
+                       IWL_DEBUG_RATE(mvm, "LQ: SISO toggle SGI/NGI\n");
+
+                       memcpy(search_tbl, tbl, sz);
+                       if (is_green) {
+                               if (!tbl->is_SGI)
+                                       break;
+                               else
+                                       IWL_ERR(mvm,
+                                               "SGI was set in GF+SISO\n");
+                       }
+                       search_tbl->is_SGI = !tbl->is_SGI;
+                       rs_set_expected_tpt_table(lq_sta, search_tbl);
+                       if (tbl->is_SGI) {
+                               s32 tpt = lq_sta->last_tpt / 100;
+                               if (tpt >= search_tbl->expected_tpt[index])
+                                       break;
+                       }
+                       search_tbl->current_rate =
+                               rate_n_flags_from_tbl(mvm, search_tbl,
+                                                     index, is_green);
+                       update_search_tbl_counter = 1;
+                       goto out;
+               case IWL_SISO_SWITCH_MIMO3_ABC:
+                       IWL_DEBUG_RATE(mvm, "LQ: SISO switch to MIMO3\n");
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+                       search_tbl->ant_type = ANT_ABC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret)
+                               goto out;
+                       break;
+               }
+               tbl->action++;
+               if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+                       tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+
+               if (tbl->action == start_action)
+                       break;
+       }
+       search_tbl->lq_type = LQ_NONE;
+       return 0;
+
+ out:
+       lq_sta->search_better_tbl = 1;
+       tbl->action++;
+       if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC)
+               tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+       if (update_search_tbl_counter)
+               search_tbl->action = tbl->action;
+
+       return 0;
+}
+
+/*
+ * Try to switch to new modulation mode from MIMO2
+ */
+static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
+                                struct iwl_lq_sta *lq_sta,
+                                struct ieee80211_sta *sta, int index)
+{
+       s8 is_green = lq_sta->is_green;
+       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+       struct iwl_scale_tbl_info *search_tbl =
+                               &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+       struct iwl_rate_scale_data *window = &(tbl->win[index]);
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+                 (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+       u8 start_action;
+       u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+       u8 tx_chains_num = num_of_ant(valid_tx_ant);
+       u8 update_search_tbl_counter = 0;
+       int ret;
+
+       start_action = tbl->action;
+       while (1) {
+               lq_sta->action_counter++;
+               switch (tbl->action) {
+               case IWL_MIMO2_SWITCH_ANTENNA1:
+               case IWL_MIMO2_SWITCH_ANTENNA2:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle Antennas\n");
+
+                       if (tx_chains_num <= 2)
+                               break;
+
+                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+                               break;
+
+                       memcpy(search_tbl, tbl, sz);
+                       if (rs_toggle_antenna(valid_tx_ant,
+                                             &search_tbl->current_rate,
+                                             search_tbl)) {
+                               update_search_tbl_counter = 1;
+                               goto out;
+                       }
+                       break;
+               case IWL_MIMO2_SWITCH_SISO_A:
+               case IWL_MIMO2_SWITCH_SISO_B:
+               case IWL_MIMO2_SWITCH_SISO_C:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to SISO\n");
+
+                       /* Set up new search table for SISO */
+                       memcpy(search_tbl, tbl, sz);
+
+                       if (tbl->action == IWL_MIMO2_SWITCH_SISO_A)
+                               search_tbl->ant_type = ANT_A;
+                       else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
+                               search_tbl->ant_type = ANT_B;
+                       else
+                               search_tbl->ant_type = ANT_C;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_siso(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret)
+                               goto out;
+
+                       break;
+
+               case IWL_MIMO2_SWITCH_GI:
+                       if (!tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_20))
+                               break;
+                       if (tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_40))
+                               break;
+
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle SGI/NGI\n");
+
+                       /* Set up new search table for MIMO2 */
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = !tbl->is_SGI;
+                       rs_set_expected_tpt_table(lq_sta, search_tbl);
+                       /*
+                        * If active table already uses the fastest possible
+                        * modulation (dual stream with short guard interval),
+                        * and it's working well, there's no need to look
+                        * for a better type of modulation!
+                        */
+                       if (tbl->is_SGI) {
+                               s32 tpt = lq_sta->last_tpt / 100;
+                               if (tpt >= search_tbl->expected_tpt[index])
+                                       break;
+                       }
+                       search_tbl->current_rate =
+                               rate_n_flags_from_tbl(mvm, search_tbl,
+                                                     index, is_green);
+                       update_search_tbl_counter = 1;
+                       goto out;
+
+               case IWL_MIMO2_SWITCH_MIMO3_ABC:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to MIMO3\n");
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+                       search_tbl->ant_type = ANT_ABC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret)
+                               goto out;
+
+                       break;
+               }
+               tbl->action++;
+               if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
+                       tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
+
+               if (tbl->action == start_action)
+                       break;
+       }
+       search_tbl->lq_type = LQ_NONE;
+       return 0;
+ out:
+       lq_sta->search_better_tbl = 1;
+       tbl->action++;
+       if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
+               tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
+       if (update_search_tbl_counter)
+               search_tbl->action = tbl->action;
+
+       return 0;
+}
+
+/*
+ * Try to switch to new modulation mode from MIMO3
+ */
+static int rs_move_mimo3_to_other(struct iwl_mvm *mvm,
+                                struct iwl_lq_sta *lq_sta,
+                                struct ieee80211_sta *sta, int index)
+{
+       s8 is_green = lq_sta->is_green;
+       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+       struct iwl_scale_tbl_info *search_tbl =
+                               &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+       struct iwl_rate_scale_data *window = &(tbl->win[index]);
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+                 (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+       u8 start_action;
+       u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+       u8 tx_chains_num = num_of_ant(valid_tx_ant);
+       int ret;
+       u8 update_search_tbl_counter = 0;
+
+       start_action = tbl->action;
+       while (1) {
+               lq_sta->action_counter++;
+               switch (tbl->action) {
+               case IWL_MIMO3_SWITCH_ANTENNA1:
+               case IWL_MIMO3_SWITCH_ANTENNA2:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 toggle Antennas\n");
+
+                       if (tx_chains_num <= 3)
+                               break;
+
+                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+                               break;
+
+                       memcpy(search_tbl, tbl, sz);
+                       if (rs_toggle_antenna(valid_tx_ant,
+                                             &search_tbl->current_rate,
+                                             search_tbl))
+                               goto out;
+                       break;
+               case IWL_MIMO3_SWITCH_SISO_A:
+               case IWL_MIMO3_SWITCH_SISO_B:
+               case IWL_MIMO3_SWITCH_SISO_C:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 switch to SISO\n");
+
+                       /* Set up new search table for SISO */
+                       memcpy(search_tbl, tbl, sz);
+
+                       if (tbl->action == IWL_MIMO3_SWITCH_SISO_A)
+                               search_tbl->ant_type = ANT_A;
+                       else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B)
+                               search_tbl->ant_type = ANT_B;
+                       else
+                               search_tbl->ant_type = ANT_C;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_siso(mvm, lq_sta, sta,
+                                               search_tbl, index);
+                       if (!ret)
+                               goto out;
+
+                       break;
+
+               case IWL_MIMO3_SWITCH_MIMO2_AB:
+               case IWL_MIMO3_SWITCH_MIMO2_AC:
+               case IWL_MIMO3_SWITCH_MIMO2_BC:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 switch to MIMO2\n");
+
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+                       if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB)
+                               search_tbl->ant_type = ANT_AB;
+                       else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC)
+                               search_tbl->ant_type = ANT_AC;
+                       else
+                               search_tbl->ant_type = ANT_BC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo2(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret)
+                               goto out;
+
+                       break;
+
+               case IWL_MIMO3_SWITCH_GI:
+                       if (!tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_20))
+                               break;
+                       if (tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_40))
+                               break;
+
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 toggle SGI/NGI\n");
+
+                       /* Set up new search table for MIMO */
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = !tbl->is_SGI;
+                       rs_set_expected_tpt_table(lq_sta, search_tbl);
+                       /*
+                        * If active table already uses the fastest possible
+                        * modulation (dual stream with short guard interval),
+                        * and it's working well, there's no need to look
+                        * for a better type of modulation!
+                        */
+                       if (tbl->is_SGI) {
+                               s32 tpt = lq_sta->last_tpt / 100;
+                               if (tpt >= search_tbl->expected_tpt[index])
+                                       break;
+                       }
+                       search_tbl->current_rate =
+                               rate_n_flags_from_tbl(mvm, search_tbl,
+                                                     index, is_green);
+                       update_search_tbl_counter = 1;
+                       goto out;
+               }
+               tbl->action++;
+               if (tbl->action > IWL_MIMO3_SWITCH_GI)
+                       tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
+
+               if (tbl->action == start_action)
+                       break;
+       }
+       search_tbl->lq_type = LQ_NONE;
+       return 0;
+ out:
+       lq_sta->search_better_tbl = 1;
+       tbl->action++;
+       if (tbl->action > IWL_MIMO3_SWITCH_GI)
+               tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
+       if (update_search_tbl_counter)
+               search_tbl->action = tbl->action;
+
+       return 0;
+}
+
+/*
+ * Check whether we should continue using same modulation mode, or
+ * begin search for a new mode, based on:
+ * 1) # tx successes or failures while using this mode
+ * 2) # times calling this function
+ * 3) elapsed time in this mode (not used, for now)
+ */
+static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
+{
+       struct iwl_scale_tbl_info *tbl;
+       int i;
+       int active_tbl;
+       int flush_interval_passed = 0;
+       struct iwl_mvm *mvm;
+
+       mvm = lq_sta->drv;
+       active_tbl = lq_sta->active_tbl;
+
+       tbl = &(lq_sta->lq_info[active_tbl]);
+
+       /* If we've been disallowing search, see if we should now allow it */
+       if (lq_sta->stay_in_tbl) {
+               /* Elapsed time using current modulation mode */
+               if (lq_sta->flush_timer)
+                       flush_interval_passed =
+                               time_after(jiffies,
+                                          (unsigned long)(lq_sta->flush_timer +
+                                               IWL_RATE_SCALE_FLUSH_INTVL));
+
+               /*
+                * Check if we should allow search for new modulation mode.
+                * If many frames have failed or succeeded, or we've used
+                * this same modulation for a long time, allow search, and
+                * reset history stats that keep track of whether we should
+                * allow a new search.  Also (below) reset all bitmaps and
+                * stats in active history.
+                */
+               if (force_search ||
+                   (lq_sta->total_failed > lq_sta->max_failure_limit) ||
+                   (lq_sta->total_success > lq_sta->max_success_limit) ||
+                   ((!lq_sta->search_better_tbl) &&
+                    (lq_sta->flush_timer) && (flush_interval_passed))) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "LQ: stay is expired %d %d %d\n",
+                                    lq_sta->total_failed,
+                                    lq_sta->total_success,
+                                    flush_interval_passed);
+
+                       /* Allow search for new mode */
+                       lq_sta->stay_in_tbl = 0;        /* only place reset */
+                       lq_sta->total_failed = 0;
+                       lq_sta->total_success = 0;
+                       lq_sta->flush_timer = 0;
+               /*
+                * Else if we've used this modulation mode enough repetitions
+                * (regardless of elapsed time or success/failure), reset
+                * history bitmaps and rate-specific stats for all rates in
+                * active table.
+                */
+               } else {
+                       lq_sta->table_count++;
+                       if (lq_sta->table_count >=
+                           lq_sta->table_count_limit) {
+                               lq_sta->table_count = 0;
+
+                               IWL_DEBUG_RATE(mvm,
+                                              "LQ: stay in table clear win\n");
+                               for (i = 0; i < IWL_RATE_COUNT; i++)
+                                       rs_rate_scale_clear_window(
+                                               &(tbl->win[i]));
+                       }
+               }
+
+               /* If transitioning to allow "search", reset all history
+                * bitmaps and stats in active table (this will become the new
+                * "search" table). */
+               if (!lq_sta->stay_in_tbl) {
+                       for (i = 0; i < IWL_RATE_COUNT; i++)
+                               rs_rate_scale_clear_window(&(tbl->win[i]));
+               }
+       }
+}
+
+/*
+ * setup rate table in uCode
+ */
+static void rs_update_rate_tbl(struct iwl_mvm *mvm,
+                              struct iwl_lq_sta *lq_sta,
+                              struct iwl_scale_tbl_info *tbl,
+                              int index, u8 is_green)
+{
+       u32 rate;
+
+       /* Update uCode's rate table. */
+       rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green);
+       rs_fill_link_cmd(mvm, lq_sta, rate);
+       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
+}
+
+/*
+ * Do rate scaling and search for new modulation mode.
+ */
+static void rs_rate_scale_perform(struct iwl_mvm *mvm,
+                                 struct sk_buff *skb,
+                                 struct ieee80211_sta *sta,
+                                 struct iwl_lq_sta *lq_sta)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       int low = IWL_RATE_INVALID;
+       int high = IWL_RATE_INVALID;
+       int index;
+       int i;
+       struct iwl_rate_scale_data *window = NULL;
+       int current_tpt = IWL_INVALID_VALUE;
+       int low_tpt = IWL_INVALID_VALUE;
+       int high_tpt = IWL_INVALID_VALUE;
+       u32 fail_count;
+       s8 scale_action = 0;
+       u16 rate_mask;
+       u8 update_lq = 0;
+       struct iwl_scale_tbl_info *tbl, *tbl1;
+       u16 rate_scale_index_msk = 0;
+       u8 is_green = 0;
+       u8 active_tbl = 0;
+       u8 done_search = 0;
+       u16 high_low;
+       s32 sr;
+       u8 tid = IWL_MAX_TID_COUNT;
+       struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data;
+
+       IWL_DEBUG_RATE(mvm, "rate scale calculate new rate for skb\n");
+
+       /* Send management frames and NO_ACK data using lowest rate. */
+       /* TODO: this could probably be improved.. */
+       if (!ieee80211_is_data(hdr->frame_control) ||
+           info->flags & IEEE80211_TX_CTL_NO_ACK)
+               return;
+
+       lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
+
+       tid = rs_tl_add_packet(lq_sta, hdr);
+       if ((tid != IWL_MAX_TID_COUNT) &&
+           (lq_sta->tx_agg_tid_en & (1 << tid))) {
+               tid_data = &sta_priv->tid_data[tid];
+               if (tid_data->state == IWL_AGG_OFF)
+                       lq_sta->is_agg = 0;
+               else
+                       lq_sta->is_agg = 1;
+       } else {
+               lq_sta->is_agg = 0;
+       }
+
+       /*
+        * Select rate-scale / modulation-mode table to work with in
+        * the rest of this function:  "search" if searching for better
+        * modulation mode, or "active" if doing rate scaling within a mode.
+        */
+       if (!lq_sta->search_better_tbl)
+               active_tbl = lq_sta->active_tbl;
+       else
+               active_tbl = 1 - lq_sta->active_tbl;
+
+       tbl = &(lq_sta->lq_info[active_tbl]);
+       if (is_legacy(tbl->lq_type))
+               lq_sta->is_green = 0;
+       else
+               lq_sta->is_green = rs_use_green(sta);
+       is_green = lq_sta->is_green;
+
+       /* current tx rate */
+       index = lq_sta->last_txrate_idx;
+
+       IWL_DEBUG_RATE(mvm, "Rate scale index %d for type %d\n", index,
+                      tbl->lq_type);
+
+       /* rates available for this association, and for modulation mode */
+       rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type);
+
+       IWL_DEBUG_RATE(mvm, "mask 0x%04X\n", rate_mask);
+
+       /* mask with station rate restriction */
+       if (is_legacy(tbl->lq_type)) {
+               if (lq_sta->band == IEEE80211_BAND_5GHZ)
+                       /* supp_rates has no CCK bits in A mode */
+                       rate_scale_index_msk = (u16) (rate_mask &
+                               (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
+               else
+                       rate_scale_index_msk = (u16) (rate_mask &
+                                                     lq_sta->supp_rates);
+
+       } else {
+               rate_scale_index_msk = rate_mask;
+       }
+
+       if (!rate_scale_index_msk)
+               rate_scale_index_msk = rate_mask;
+
+       if (!((1 << index) & rate_scale_index_msk)) {
+               IWL_ERR(mvm, "Current Rate is not valid\n");
+               if (lq_sta->search_better_tbl) {
+                       /* revert to active table if search table is not valid*/
+                       tbl->lq_type = LQ_NONE;
+                       lq_sta->search_better_tbl = 0;
+                       tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+                       /* get "active" rate info */
+                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+                       rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green);
+               }
+               return;
+       }
+
+       /* Get expected throughput table and history window for current rate */
+       if (!tbl->expected_tpt) {
+               IWL_ERR(mvm, "tbl->expected_tpt is NULL\n");
+               return;
+       }
+
+       /* force user max rate if set by user */
+       if ((lq_sta->max_rate_idx != -1) &&
+           (lq_sta->max_rate_idx < index)) {
+               index = lq_sta->max_rate_idx;
+               update_lq = 1;
+               window = &(tbl->win[index]);
+               goto lq_update;
+       }
+
+       window = &(tbl->win[index]);
+
+       /*
+        * If there is not enough history to calculate actual average
+        * throughput, keep analyzing results of more tx frames, without
+        * changing rate or mode (bypass most of the rest of this function).
+        * Set up new rate table in uCode only if old rate is not supported
+        * in current association (use new rate found above).
+        */
+       fail_count = window->counter - window->success_counter;
+       if ((fail_count < IWL_RATE_MIN_FAILURE_TH) &&
+           (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) {
+               IWL_DEBUG_RATE(mvm,
+                              "LQ: still below TH. succ=%d total=%d for index %d\n",
+                              window->success_counter, window->counter, index);
+
+               /* Can't calculate this yet; not enough history */
+               window->average_tpt = IWL_INVALID_VALUE;
+
+               /* Should we stay with this modulation mode,
+                * or search for a new one? */
+               rs_stay_in_table(lq_sta, false);
+
+               goto out;
+       }
+       /* Else we have enough samples; calculate estimate of
+        * actual average throughput */
+       if (window->average_tpt != ((window->success_ratio *
+                       tbl->expected_tpt[index] + 64) / 128)) {
+               IWL_ERR(mvm,
+                       "expected_tpt should have been calculated by now\n");
+               window->average_tpt = ((window->success_ratio *
+                                       tbl->expected_tpt[index] + 64) / 128);
+       }
+
+       /* If we are searching for better modulation mode, check success. */
+       if (lq_sta->search_better_tbl) {
+               /* If good success, continue using the "search" mode;
+                * no need to send new link quality command, since we're
+                * continuing to use the setup that we've been trying. */
+               if (window->average_tpt > lq_sta->last_tpt) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "LQ: SWITCHING TO NEW TABLE suc=%d cur-tpt=%d old-tpt=%d\n",
+                                      window->success_ratio,
+                                      window->average_tpt,
+                                      lq_sta->last_tpt);
+
+                       if (!is_legacy(tbl->lq_type))
+                               lq_sta->enable_counter = 1;
+
+                       /* Swap tables; "search" becomes "active" */
+                       lq_sta->active_tbl = active_tbl;
+                       current_tpt = window->average_tpt;
+               /* Else poor success; go back to mode in "active" table */
+               } else {
+                       IWL_DEBUG_RATE(mvm,
+                                      "LQ: GOING BACK TO THE OLD TABLE suc=%d cur-tpt=%d old-tpt=%d\n",
+                                      window->success_ratio,
+                                      window->average_tpt,
+                                      lq_sta->last_tpt);
+
+                       /* Nullify "search" table */
+                       tbl->lq_type = LQ_NONE;
+
+                       /* Revert to "active" table */
+                       active_tbl = lq_sta->active_tbl;
+                       tbl = &(lq_sta->lq_info[active_tbl]);
+
+                       /* Revert to "active" rate and throughput info */
+                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+                       current_tpt = lq_sta->last_tpt;
+
+                       /* Need to set up a new rate table in uCode */
+                       update_lq = 1;
+               }
+
+               /* Either way, we've made a decision; modulation mode
+                * search is done, allow rate adjustment next time. */
+               lq_sta->search_better_tbl = 0;
+               done_search = 1;        /* Don't switch modes below! */
+               goto lq_update;
+       }
+
+       /* (Else) not in search of better modulation mode, try for better
+        * starting rate, while staying in this mode. */
+       high_low = rs_get_adjacent_rate(mvm, index, rate_scale_index_msk,
+                                       tbl->lq_type);
+       low = high_low & 0xff;
+       high = (high_low >> 8) & 0xff;
+
+       /* If user set max rate, dont allow higher than user constrain */
+       if ((lq_sta->max_rate_idx != -1) &&
+           (lq_sta->max_rate_idx < high))
+               high = IWL_RATE_INVALID;
+
+       sr = window->success_ratio;
+
+       /* Collect measured throughputs for current and adjacent rates */
+       current_tpt = window->average_tpt;
+       if (low != IWL_RATE_INVALID)
+               low_tpt = tbl->win[low].average_tpt;
+       if (high != IWL_RATE_INVALID)
+               high_tpt = tbl->win[high].average_tpt;
+
+       scale_action = 0;
+
+       /* Too many failures, decrease rate */
+       if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) {
+               IWL_DEBUG_RATE(mvm,
+                              "decrease rate because of low success_ratio\n");
+               scale_action = -1;
+       /* No throughput measured yet for adjacent rates; try increase. */
+       } else if ((low_tpt == IWL_INVALID_VALUE) &&
+                  (high_tpt == IWL_INVALID_VALUE)) {
+               if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH)
+                       scale_action = 1;
+               else if (low != IWL_RATE_INVALID)
+                       scale_action = 0;
+       }
+
+       /* Both adjacent throughputs are measured, but neither one has better
+        * throughput; we're using the best rate, don't change it! */
+       else if ((low_tpt != IWL_INVALID_VALUE) &&
+                (high_tpt != IWL_INVALID_VALUE) &&
+                (low_tpt < current_tpt) &&
+                (high_tpt < current_tpt))
+               scale_action = 0;
+
+       /* At least one adjacent rate's throughput is measured,
+        * and may have better performance. */
+       else {
+               /* Higher adjacent rate's throughput is measured */
+               if (high_tpt != IWL_INVALID_VALUE) {
+                       /* Higher rate has better throughput */
+                       if (high_tpt > current_tpt &&
+                           sr >= IWL_RATE_INCREASE_TH) {
+                               scale_action = 1;
+                       } else {
+                               scale_action = 0;
+                       }
+
+               /* Lower adjacent rate's throughput is measured */
+               } else if (low_tpt != IWL_INVALID_VALUE) {
+                       /* Lower rate has better throughput */
+                       if (low_tpt > current_tpt) {
+                               IWL_DEBUG_RATE(mvm,
+                                              "decrease rate because of low tpt\n");
+                               scale_action = -1;
+                       } else if (sr >= IWL_RATE_INCREASE_TH) {
+                               scale_action = 1;
+                       }
+               }
+       }
+
+       /* Sanity check; asked for decrease, but success rate or throughput
+        * has been good at old rate.  Don't change it. */
+       if ((scale_action == -1) && (low != IWL_RATE_INVALID) &&
+           ((sr > IWL_RATE_HIGH_TH) ||
+            (current_tpt > (100 * tbl->expected_tpt[low]))))
+               scale_action = 0;
+
+       switch (scale_action) {
+       case -1:
+               /* Decrease starting rate, update uCode's rate table */
+               if (low != IWL_RATE_INVALID) {
+                       update_lq = 1;
+                       index = low;
+               }
+
+               break;
+       case 1:
+               /* Increase starting rate, update uCode's rate table */
+               if (high != IWL_RATE_INVALID) {
+                       update_lq = 1;
+                       index = high;
+               }
+
+               break;
+       case 0:
+               /* No change */
+       default:
+               break;
+       }
+
+       IWL_DEBUG_RATE(mvm,
+                      "choose rate scale index %d action %d low %d high %d type %d\n",
+                      index, scale_action, low, high, tbl->lq_type);
+
+lq_update:
+       /* Replace uCode's rate table for the destination station. */
+       if (update_lq)
+               rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green);
+
+       rs_stay_in_table(lq_sta, false);
+
+       /*
+        * Search for new modulation mode if we're:
+        * 1)  Not changing rates right now
+        * 2)  Not just finishing up a search
+        * 3)  Allowing a new search
+        */
+       if (!update_lq && !done_search &&
+           !lq_sta->stay_in_tbl && window->counter) {
+               /* Save current throughput to compare with "search" throughput*/
+               lq_sta->last_tpt = current_tpt;
+
+               /* Select a new "search" modulation mode to try.
+                * If one is found, set up the new "search" table. */
+               if (is_legacy(tbl->lq_type))
+                       rs_move_legacy_other(mvm, lq_sta, sta, index);
+               else if (is_siso(tbl->lq_type))
+                       rs_move_siso_to_other(mvm, lq_sta, sta, index);
+               else if (is_mimo2(tbl->lq_type))
+                       rs_move_mimo2_to_other(mvm, lq_sta, sta, index);
+               else
+                       rs_move_mimo3_to_other(mvm, lq_sta, sta, index);
+
+               /* If new "search" mode was selected, set up in uCode table */
+               if (lq_sta->search_better_tbl) {
+                       /* Access the "search" table, clear its history. */
+                       tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+                       for (i = 0; i < IWL_RATE_COUNT; i++)
+                               rs_rate_scale_clear_window(&(tbl->win[i]));
+
+                       /* Use new "search" start rate */
+                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+
+                       IWL_DEBUG_RATE(mvm,
+                                      "Switch current  mcs: %X index: %d\n",
+                                      tbl->current_rate, index);
+                       rs_fill_link_cmd(mvm, lq_sta, tbl->current_rate);
+                       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
+               } else {
+                       done_search = 1;
+               }
+       }
+
+       if (done_search && !lq_sta->stay_in_tbl) {
+               /* If the "active" (non-search) mode was legacy,
+                * and we've tried switching antennas,
+                * but we haven't been able to try HT modes (not available),
+                * stay with best antenna legacy modulation for a while
+                * before next round of mode comparisons. */
+               tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]);
+               if (is_legacy(tbl1->lq_type) && !sta->ht_cap.ht_supported &&
+                   lq_sta->action_counter > tbl1->max_search) {
+                       IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n");
+                       rs_set_stay_in_table(mvm, 1, lq_sta);
+               }
+
+               /* If we're in an HT mode, and all 3 mode switch actions
+                * have been tried and compared, stay in this best modulation
+                * mode for a while before next round of mode comparisons. */
+               if (lq_sta->enable_counter &&
+                   (lq_sta->action_counter >= tbl1->max_search)) {
+                       if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) &&
+                           (lq_sta->tx_agg_tid_en & (1 << tid)) &&
+                           (tid != IWL_MAX_TID_COUNT)) {
+                               tid_data = &sta_priv->tid_data[tid];
+                               if (tid_data->state == IWL_AGG_OFF) {
+                                       IWL_DEBUG_RATE(mvm,
+                                                      "try to aggregate tid %d\n",
+                                                      tid);
+                                       rs_tl_turn_on_agg(mvm, tid,
+                                                         lq_sta, sta);
+                               }
+                       }
+                       rs_set_stay_in_table(mvm, 0, lq_sta);
+               }
+       }
+
+out:
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green);
+       lq_sta->last_txrate_idx = index;
+}
+
+/**
+ * rs_initialize_lq - Initialize a station's hardware rate table
+ *
+ * The uCode's station table contains a table of fallback rates
+ * for automatic fallback during transmission.
+ *
+ * NOTE: This sets up a default set of values.  These will be replaced later
+ *       if the driver's iwl-agn-rs rate scaling algorithm is used, instead of
+ *       rc80211_simple.
+ *
+ * NOTE: Run REPLY_ADD_STA command to set up station table entry, before
+ *       calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
+ *       which requires station table entry to exist).
+ */
+static void rs_initialize_lq(struct iwl_mvm *mvm,
+                            struct ieee80211_sta *sta,
+                            struct iwl_lq_sta *lq_sta,
+                            enum ieee80211_band band)
+{
+       struct iwl_scale_tbl_info *tbl;
+       int rate_idx;
+       int i;
+       u32 rate;
+       u8 use_green = rs_use_green(sta);
+       u8 active_tbl = 0;
+       u8 valid_tx_ant;
+
+       if (!sta || !lq_sta)
+               return;
+
+       i = lq_sta->last_txrate_idx;
+
+       valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+
+       if (!lq_sta->search_better_tbl)
+               active_tbl = lq_sta->active_tbl;
+       else
+               active_tbl = 1 - lq_sta->active_tbl;
+
+       tbl = &(lq_sta->lq_info[active_tbl]);
+
+       if ((i < 0) || (i >= IWL_RATE_COUNT))
+               i = 0;
+
+       rate = iwl_rates[i].plcp;
+       tbl->ant_type = first_antenna(valid_tx_ant);
+       rate |= tbl->ant_type << RATE_MCS_ANT_POS;
+
+       if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE)
+               rate |= RATE_MCS_CCK_MSK;
+
+       rs_get_tbl_info_from_mcs(rate, band, tbl, &rate_idx);
+       if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type))
+               rs_toggle_antenna(valid_tx_ant, &rate, tbl);
+
+       rate = rate_n_flags_from_tbl(mvm, tbl, rate_idx, use_green);
+       tbl->current_rate = rate;
+       rs_set_expected_tpt_table(lq_sta, tbl);
+       rs_fill_link_cmd(NULL, lq_sta, rate);
+       /* TODO restore station should remember the lq cmd */
+       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_SYNC, true);
+}
+
+static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
+                       struct ieee80211_tx_rate_control *txrc)
+{
+       struct sk_buff *skb = txrc->skb;
+       struct ieee80211_supported_band *sband = txrc->sband;
+       struct iwl_op_mode *op_mode __maybe_unused =
+                       (struct iwl_op_mode *)mvm_r;
+       struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct iwl_lq_sta *lq_sta = mvm_sta;
+       int rate_idx;
+
+       IWL_DEBUG_RATE_LIMIT(mvm, "rate scale calculate new rate for skb\n");
+
+       /* Get max rate if user set max rate */
+       if (lq_sta) {
+               lq_sta->max_rate_idx = txrc->max_rate_idx;
+               if ((sband->band == IEEE80211_BAND_5GHZ) &&
+                   (lq_sta->max_rate_idx != -1))
+                       lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE;
+               if ((lq_sta->max_rate_idx < 0) ||
+                   (lq_sta->max_rate_idx >= IWL_RATE_COUNT))
+                       lq_sta->max_rate_idx = -1;
+       }
+
+       /* Treat uninitialized rate scaling data same as non-existing. */
+       if (lq_sta && !lq_sta->drv) {
+               IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
+               mvm_sta = NULL;
+       }
+
+       /* Send management frames and NO_ACK data using lowest rate. */
+       if (rate_control_send_low(sta, mvm_sta, txrc))
+               return;
+
+       rate_idx  = lq_sta->last_txrate_idx;
+
+       if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
+               rate_idx -= IWL_FIRST_OFDM_RATE;
+               /* 6M and 9M shared same MCS index */
+               rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0;
+               if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
+                   IWL_RATE_MIMO3_6M_PLCP)
+                       rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM);
+               else if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
+                        IWL_RATE_MIMO2_6M_PLCP)
+                       rate_idx = rate_idx + MCS_INDEX_PER_STREAM;
+               info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
+               if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK)
+                       info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI;
+               if (lq_sta->last_rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */
+                       info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+               if (lq_sta->last_rate_n_flags & RATE_HT_MCS_GF_MSK)
+                       info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD;
+       } else {
+               /* Check for invalid rates */
+               if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) ||
+                   ((sband->band == IEEE80211_BAND_5GHZ) &&
+                    (rate_idx < IWL_FIRST_OFDM_RATE)))
+                       rate_idx = rate_lowest_index(sband, sta);
+               /* On valid 5 GHz rate, adjust index */
+               else if (sband->band == IEEE80211_BAND_5GHZ)
+                       rate_idx -= IWL_FIRST_OFDM_RATE;
+               info->control.rates[0].flags = 0;
+       }
+       info->control.rates[0].idx = rate_idx;
+}
+
+static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
+                         gfp_t gfp)
+{
+       struct iwl_mvm_sta *sta_priv = (struct iwl_mvm_sta *)sta->drv_priv;
+       struct iwl_op_mode *op_mode __maybe_unused =
+                       (struct iwl_op_mode *)mvm_rate;
+       struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
+
+       IWL_DEBUG_RATE(mvm, "create station rate scale window\n");
+
+       return &sta_priv->lq_sta;
+}
+
+/*
+ * Called after adding a new station to initialize rate scaling
+ */
+void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                         enum ieee80211_band band)
+{
+       int i, j;
+       struct ieee80211_hw *hw = mvm->hw;
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       struct iwl_mvm_sta *sta_priv;
+       struct iwl_lq_sta *lq_sta;
+       struct ieee80211_supported_band *sband;
+       unsigned long supp; /* must be unsigned long for for_each_set_bit */
+
+       sta_priv = (struct iwl_mvm_sta *)sta->drv_priv;
+       lq_sta = &sta_priv->lq_sta;
+       sband = hw->wiphy->bands[band];
+
+       lq_sta->lq.sta_id = sta_priv->sta_id;
+
+       for (j = 0; j < LQ_SIZE; j++)
+               for (i = 0; i < IWL_RATE_COUNT; i++)
+                       rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
+
+       lq_sta->flush_timer = 0;
+       lq_sta->supp_rates = sta->supp_rates[sband->band];
+       for (j = 0; j < LQ_SIZE; j++)
+               for (i = 0; i < IWL_RATE_COUNT; i++)
+                       rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
+
+       IWL_DEBUG_RATE(mvm,
+                      "LQ: *** rate scale station global init for station %d ***\n",
+                      sta_priv->sta_id);
+       /* TODO: what is a good starting rate for STA? About middle? Maybe not
+        * the lowest or the highest rate.. Could consider using RSSI from
+        * previous packets? Need to have IEEE 802.1X auth succeed immediately
+        * after assoc.. */
+
+       lq_sta->max_rate_idx = -1;
+       lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX;
+       lq_sta->is_green = rs_use_green(sta);
+       lq_sta->band = sband->band;
+       /*
+        * active legacy rates as per supported rates bitmap
+        */
+       supp = sta->supp_rates[sband->band];
+       lq_sta->active_legacy_rate = 0;
+       for_each_set_bit(i, &supp, BITS_PER_LONG)
+               lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value);
+
+       /*
+        * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3),
+        * supp_rates[] does not; shift to convert format, force 9 MBits off.
+        */
+       lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
+       lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
+       lq_sta->active_siso_rate &= ~((u16)0x2);
+       lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
+
+       /* Same here */
+       lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
+       lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
+       lq_sta->active_mimo2_rate &= ~((u16)0x2);
+       lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
+
+       lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1;
+       lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1;
+       lq_sta->active_mimo3_rate &= ~((u16)0x2);
+       lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE;
+
+       IWL_DEBUG_RATE(mvm,
+                      "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n",
+                      lq_sta->active_siso_rate,
+                      lq_sta->active_mimo2_rate,
+                      lq_sta->active_mimo3_rate);
+
+       /* These values will be overridden later */
+       lq_sta->lq.single_stream_ant_msk =
+               first_antenna(mvm->nvm_data->valid_tx_ant);
+       lq_sta->lq.dual_stream_ant_msk =
+               mvm->nvm_data->valid_tx_ant &
+               ~first_antenna(mvm->nvm_data->valid_tx_ant);
+       if (!lq_sta->lq.dual_stream_ant_msk) {
+               lq_sta->lq.dual_stream_ant_msk = ANT_AB;
+       } else if (num_of_ant(mvm->nvm_data->valid_tx_ant) == 2) {
+               lq_sta->lq.dual_stream_ant_msk =
+                       mvm->nvm_data->valid_tx_ant;
+       }
+
+       /* as default allow aggregation for all tids */
+       lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
+       lq_sta->drv = mvm;
+
+       /* Set last_txrate_idx to lowest rate */
+       lq_sta->last_txrate_idx = rate_lowest_index(sband, sta);
+       if (sband->band == IEEE80211_BAND_5GHZ)
+               lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
+       lq_sta->is_agg = 0;
+#ifdef CONFIG_MAC80211_DEBUGFS
+       lq_sta->dbg_fixed_rate = 0;
+#endif
+
+       rs_initialize_lq(mvm, sta, lq_sta, band);
+}
+
+static void rs_fill_link_cmd(struct iwl_mvm *mvm,
+                            struct iwl_lq_sta *lq_sta, u32 new_rate)
+{
+       struct iwl_scale_tbl_info tbl_type;
+       int index = 0;
+       int rate_idx;
+       int repeat_rate = 0;
+       u8 ant_toggle_cnt = 0;
+       u8 use_ht_possible = 1;
+       u8 valid_tx_ant = 0;
+       struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
+
+       /* Override starting rate (index 0) if needed for debug purposes */
+       rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+
+       /* Interpret new_rate (rate_n_flags) */
+       rs_get_tbl_info_from_mcs(new_rate, lq_sta->band,
+                                &tbl_type, &rate_idx);
+
+       /* How many times should we repeat the initial rate? */
+       if (is_legacy(tbl_type.lq_type)) {
+               ant_toggle_cnt = 1;
+               repeat_rate = IWL_NUMBER_TRY;
+       } else {
+               repeat_rate = min(IWL_HT_NUMBER_TRY,
+                                 LINK_QUAL_AGG_DISABLE_START_DEF - 1);
+       }
+
+       lq_cmd->mimo_delim = is_mimo(tbl_type.lq_type) ? 1 : 0;
+
+       /* Fill 1st table entry (index 0) */
+       lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
+
+       if (num_of_ant(tbl_type.ant_type) == 1)
+               lq_cmd->single_stream_ant_msk = tbl_type.ant_type;
+       else if (num_of_ant(tbl_type.ant_type) == 2)
+               lq_cmd->dual_stream_ant_msk = tbl_type.ant_type;
+       /* otherwise we don't modify the existing value */
+
+       index++;
+       repeat_rate--;
+       if (mvm)
+               valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+
+       /* Fill rest of rate table */
+       while (index < LINK_QUAL_MAX_RETRY_NUM) {
+               /* Repeat initial/next rate.
+                * For legacy IWL_NUMBER_TRY == 1, this loop will not execute.
+                * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */
+               while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) {
+                       if (is_legacy(tbl_type.lq_type)) {
+                               if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
+                                       ant_toggle_cnt++;
+                               else if (mvm &&
+                                        rs_toggle_antenna(valid_tx_ant,
+                                                       &new_rate, &tbl_type))
+                                       ant_toggle_cnt = 1;
+                       }
+
+                       /* Override next rate if needed for debug purposes */
+                       rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+
+                       /* Fill next table entry */
+                       lq_cmd->rs_table[index] =
+                                       cpu_to_le32(new_rate);
+                       repeat_rate--;
+                       index++;
+               }
+
+               rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type,
+                                        &rate_idx);
+
+
+               /* Indicate to uCode which entries might be MIMO.
+                * If initial rate was MIMO, this will finally end up
+                * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */
+               if (is_mimo(tbl_type.lq_type))
+                       lq_cmd->mimo_delim = index;
+
+               /* Get next rate */
+               new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx,
+                                            use_ht_possible);
+
+               /* How many times should we repeat the next rate? */
+               if (is_legacy(tbl_type.lq_type)) {
+                       if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
+                               ant_toggle_cnt++;
+                       else if (mvm &&
+                                rs_toggle_antenna(valid_tx_ant,
+                                                  &new_rate, &tbl_type))
+                               ant_toggle_cnt = 1;
+
+                       repeat_rate = IWL_NUMBER_TRY;
+               } else {
+                       repeat_rate = IWL_HT_NUMBER_TRY;
+               }
+
+               /* Don't allow HT rates after next pass.
+                * rs_get_lower_rate() will change type to LQ_A or LQ_G. */
+               use_ht_possible = 0;
+
+               /* Override next rate if needed for debug purposes */
+               rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+
+               /* Fill next table entry */
+               lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
+
+               index++;
+               repeat_rate--;
+       }
+
+       lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+       lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
+
+       lq_cmd->agg_time_limit =
+               cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
+}
+
+static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+{
+       return hw->priv;
+}
+/* rate scale requires free function to be implemented */
+static void rs_free(void *mvm_rate)
+{
+       return;
+}
+
+static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta,
+                       void *mvm_sta)
+{
+       struct iwl_op_mode *op_mode __maybe_unused = mvm_r;
+       struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
+
+       IWL_DEBUG_RATE(mvm, "enter\n");
+       IWL_DEBUG_RATE(mvm, "leave\n");
+}
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
+                            u32 *rate_n_flags, int index)
+{
+       struct iwl_mvm *mvm;
+       u8 valid_tx_ant;
+       u8 ant_sel_tx;
+
+       mvm = lq_sta->drv;
+       valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+       if (lq_sta->dbg_fixed_rate) {
+               ant_sel_tx =
+                 ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK)
+                 >> RATE_MCS_ANT_POS);
+               if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) {
+                       *rate_n_flags = lq_sta->dbg_fixed_rate;
+                       IWL_DEBUG_RATE(mvm, "Fixed rate ON\n");
+               } else {
+                       lq_sta->dbg_fixed_rate = 0;
+                       IWL_ERR(mvm,
+                               "Invalid antenna selection 0x%X, Valid is 0x%X\n",
+                               ant_sel_tx, valid_tx_ant);
+                       IWL_DEBUG_RATE(mvm, "Fixed rate OFF\n");
+               }
+       } else {
+               IWL_DEBUG_RATE(mvm, "Fixed rate OFF\n");
+       }
+}
+
+static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
+                       const char __user *user_buf, size_t count, loff_t *ppos)
+{
+       struct iwl_lq_sta *lq_sta = file->private_data;
+       struct iwl_mvm *mvm;
+       char buf[64];
+       size_t buf_size;
+       u32 parsed_rate;
+
+
+       mvm = lq_sta->drv;
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) -  1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       if (sscanf(buf, "%x", &parsed_rate) == 1)
+               lq_sta->dbg_fixed_rate = parsed_rate;
+       else
+               lq_sta->dbg_fixed_rate = 0;
+
+       rs_program_fix_rate(mvm, lq_sta);
+
+       return count;
+}
+
+static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
+                       char __user *user_buf, size_t count, loff_t *ppos)
+{
+       char *buff;
+       int desc = 0;
+       int i = 0;
+       int index = 0;
+       ssize_t ret;
+
+       struct iwl_lq_sta *lq_sta = file->private_data;
+       struct iwl_mvm *mvm;
+       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+
+       mvm = lq_sta->drv;
+       buff = kmalloc(1024, GFP_KERNEL);
+       if (!buff)
+               return -ENOMEM;
+
+       desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id);
+       desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n",
+                       lq_sta->total_failed, lq_sta->total_success,
+                       lq_sta->active_legacy_rate);
+       desc += sprintf(buff+desc, "fixed rate 0x%X\n",
+                       lq_sta->dbg_fixed_rate);
+       desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
+           (mvm->nvm_data->valid_tx_ant & ANT_A) ? "ANT_A," : "",
+           (mvm->nvm_data->valid_tx_ant & ANT_B) ? "ANT_B," : "",
+           (mvm->nvm_data->valid_tx_ant & ANT_C) ? "ANT_C" : "");
+       desc += sprintf(buff+desc, "lq type %s\n",
+          (is_legacy(tbl->lq_type)) ? "legacy" : "HT");
+       if (is_Ht(tbl->lq_type)) {
+               desc += sprintf(buff+desc, " %s",
+                  (is_siso(tbl->lq_type)) ? "SISO" :
+                  ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3"));
+                  desc += sprintf(buff+desc, " %s",
+                  (tbl->is_ht40) ? "40MHz" : "20MHz");
+                  desc += sprintf(buff+desc, " %s %s %s\n",
+                                  (tbl->is_SGI) ? "SGI" : "",
+                  (lq_sta->is_green) ? "GF enabled" : "",
+                  (lq_sta->is_agg) ? "AGG on" : "");
+       }
+       desc += sprintf(buff+desc, "last tx rate=0x%X\n",
+                       lq_sta->last_rate_n_flags);
+       desc += sprintf(buff+desc,
+                       "general: flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n",
+                       lq_sta->lq.flags,
+                       lq_sta->lq.mimo_delim,
+                       lq_sta->lq.single_stream_ant_msk,
+                       lq_sta->lq.dual_stream_ant_msk);
+
+       desc += sprintf(buff+desc,
+                       "agg: time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n",
+                       le16_to_cpu(lq_sta->lq.agg_time_limit),
+                       lq_sta->lq.agg_disable_start_th,
+                       lq_sta->lq.agg_frame_cnt_limit);
+
+       desc += sprintf(buff+desc,
+                       "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
+                       lq_sta->lq.initial_rate_index[0],
+                       lq_sta->lq.initial_rate_index[1],
+                       lq_sta->lq.initial_rate_index[2],
+                       lq_sta->lq.initial_rate_index[3]);
+
+       for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+               index = iwl_hwrate_to_plcp_idx(
+                       le32_to_cpu(lq_sta->lq.rs_table[i]));
+               if (is_legacy(tbl->lq_type)) {
+                       desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n",
+                                       i, le32_to_cpu(lq_sta->lq.rs_table[i]),
+                                       iwl_rate_mcs[index].mbps);
+               } else {
+                       desc += sprintf(buff+desc,
+                                       " rate[%d] 0x%X %smbps (%s)\n",
+                                       i, le32_to_cpu(lq_sta->lq.rs_table[i]),
+                                       iwl_rate_mcs[index].mbps,
+                                       iwl_rate_mcs[index].mcs);
+               }
+       }
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+       kfree(buff);
+       return ret;
+}
+
+static const struct file_operations rs_sta_dbgfs_scale_table_ops = {
+       .write = rs_sta_dbgfs_scale_table_write,
+       .read = rs_sta_dbgfs_scale_table_read,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
+                       char __user *user_buf, size_t count, loff_t *ppos)
+{
+       char *buff;
+       int desc = 0;
+       int i, j;
+       ssize_t ret;
+
+       struct iwl_lq_sta *lq_sta = file->private_data;
+
+       buff = kmalloc(1024, GFP_KERNEL);
+       if (!buff)
+               return -ENOMEM;
+
+       for (i = 0; i < LQ_SIZE; i++) {
+               desc += sprintf(buff+desc,
+                               "%s type=%d SGI=%d HT40=%d DUP=0 GF=%d\n"
+                               "rate=0x%X\n",
+                               lq_sta->active_tbl == i ? "*" : "x",
+                               lq_sta->lq_info[i].lq_type,
+                               lq_sta->lq_info[i].is_SGI,
+                               lq_sta->lq_info[i].is_ht40,
+                               lq_sta->is_green,
+                               lq_sta->lq_info[i].current_rate);
+               for (j = 0; j < IWL_RATE_COUNT; j++) {
+                       desc += sprintf(buff+desc,
+                               "counter=%d success=%d %%=%d\n",
+                               lq_sta->lq_info[i].win[j].counter,
+                               lq_sta->lq_info[i].win[j].success_counter,
+                               lq_sta->lq_info[i].win[j].success_ratio);
+               }
+       }
+       ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+       kfree(buff);
+       return ret;
+}
+
+static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
+       .read = rs_sta_dbgfs_stats_table_read,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+
+static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file,
+                       char __user *user_buf, size_t count, loff_t *ppos)
+{
+       struct iwl_lq_sta *lq_sta = file->private_data;
+       struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl];
+       char buff[120];
+       int desc = 0;
+
+       if (is_Ht(tbl->lq_type))
+               desc += sprintf(buff+desc,
+                               "Bit Rate= %d Mb/s\n",
+                               tbl->expected_tpt[lq_sta->last_txrate_idx]);
+       else
+               desc += sprintf(buff+desc,
+                               "Bit Rate= %d Mb/s\n",
+                               iwl_rates[lq_sta->last_txrate_idx].ieee >> 1);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+}
+
+static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = {
+       .read = rs_sta_dbgfs_rate_scale_data_read,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+
+static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
+{
+       struct iwl_lq_sta *lq_sta = mvm_sta;
+       lq_sta->rs_sta_dbgfs_scale_table_file =
+               debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
+                                   lq_sta, &rs_sta_dbgfs_scale_table_ops);
+       lq_sta->rs_sta_dbgfs_stats_table_file =
+               debugfs_create_file("rate_stats_table", S_IRUSR, dir,
+                                   lq_sta, &rs_sta_dbgfs_stats_table_ops);
+       lq_sta->rs_sta_dbgfs_rate_scale_data_file =
+               debugfs_create_file("rate_scale_data", S_IRUSR, dir,
+                                   lq_sta, &rs_sta_dbgfs_rate_scale_data_ops);
+       lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
+               debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
+                                 &lq_sta->tx_agg_tid_en);
+}
+
+static void rs_remove_debugfs(void *mvm, void *mvm_sta)
+{
+       struct iwl_lq_sta *lq_sta = mvm_sta;
+       debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file);
+       debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
+       debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file);
+       debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
+}
+#endif
+
+/*
+ * Initialization of rate scaling information is done by driver after
+ * the station is added. Since mac80211 calls this function before a
+ * station is added we ignore it.
+ */
+static void rs_rate_init_stub(void *mvm_r,
+                                struct ieee80211_supported_band *sband,
+                                struct ieee80211_sta *sta, void *mvm_sta)
+{
+}
+static struct rate_control_ops rs_mvm_ops = {
+       .module = NULL,
+       .name = RS_NAME,
+       .tx_status = rs_tx_status,
+       .get_rate = rs_get_rate,
+       .rate_init = rs_rate_init_stub,
+       .alloc = rs_alloc,
+       .free = rs_free,
+       .alloc_sta = rs_alloc_sta,
+       .free_sta = rs_free_sta,
+#ifdef CONFIG_MAC80211_DEBUGFS
+       .add_sta_debugfs = rs_add_debugfs,
+       .remove_sta_debugfs = rs_remove_debugfs,
+#endif
+};
+
+int iwl_mvm_rate_control_register(void)
+{
+       return ieee80211_rate_control_register(&rs_mvm_ops);
+}
+
+void iwl_mvm_rate_control_unregister(void)
+{
+       ieee80211_rate_control_unregister(&rs_mvm_ops);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h
new file mode 100644 (file)
index 0000000..219c685
--- /dev/null
@@ -0,0 +1,393 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __rs_h__
+#define __rs_h__
+
+#include <net/mac80211.h>
+
+#include "iwl-config.h"
+
+#include "fw-api.h"
+#include "iwl-trans.h"
+
+struct iwl_rs_rate_info {
+       u8 plcp;        /* uCode API:  IWL_RATE_6M_PLCP, etc. */
+       u8 plcp_siso;   /* uCode API:  IWL_RATE_SISO_6M_PLCP, etc. */
+       u8 plcp_mimo2;  /* uCode API:  IWL_RATE_MIMO2_6M_PLCP, etc. */
+       u8 plcp_mimo3;  /* uCode API:  IWL_RATE_MIMO3_6M_PLCP, etc. */
+       u8 ieee;        /* MAC header:  IWL_RATE_6M_IEEE, etc. */
+       u8 prev_ieee;    /* previous rate in IEEE speeds */
+       u8 next_ieee;    /* next rate in IEEE speeds */
+       u8 prev_rs;      /* previous rate used in rs algo */
+       u8 next_rs;      /* next rate used in rs algo */
+       u8 prev_rs_tgg;  /* previous rate used in TGG rs algo */
+       u8 next_rs_tgg;  /* next rate used in TGG rs algo */
+};
+
+#define IWL_RATE_60M_PLCP 3
+
+enum {
+       IWL_RATE_INVM_INDEX = IWL_RATE_COUNT,
+       IWL_RATE_INVALID = IWL_RATE_COUNT,
+};
+
+#define LINK_QUAL_MAX_RETRY_NUM 16
+
+enum {
+       IWL_RATE_6M_INDEX_TABLE = 0,
+       IWL_RATE_9M_INDEX_TABLE,
+       IWL_RATE_12M_INDEX_TABLE,
+       IWL_RATE_18M_INDEX_TABLE,
+       IWL_RATE_24M_INDEX_TABLE,
+       IWL_RATE_36M_INDEX_TABLE,
+       IWL_RATE_48M_INDEX_TABLE,
+       IWL_RATE_54M_INDEX_TABLE,
+       IWL_RATE_1M_INDEX_TABLE,
+       IWL_RATE_2M_INDEX_TABLE,
+       IWL_RATE_5M_INDEX_TABLE,
+       IWL_RATE_11M_INDEX_TABLE,
+       IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1,
+};
+
+/* #define vs. enum to keep from defaulting to 'large integer' */
+#define        IWL_RATE_6M_MASK   (1 << IWL_RATE_6M_INDEX)
+#define        IWL_RATE_9M_MASK   (1 << IWL_RATE_9M_INDEX)
+#define        IWL_RATE_12M_MASK  (1 << IWL_RATE_12M_INDEX)
+#define        IWL_RATE_18M_MASK  (1 << IWL_RATE_18M_INDEX)
+#define        IWL_RATE_24M_MASK  (1 << IWL_RATE_24M_INDEX)
+#define        IWL_RATE_36M_MASK  (1 << IWL_RATE_36M_INDEX)
+#define        IWL_RATE_48M_MASK  (1 << IWL_RATE_48M_INDEX)
+#define        IWL_RATE_54M_MASK  (1 << IWL_RATE_54M_INDEX)
+#define IWL_RATE_60M_MASK  (1 << IWL_RATE_60M_INDEX)
+#define        IWL_RATE_1M_MASK   (1 << IWL_RATE_1M_INDEX)
+#define        IWL_RATE_2M_MASK   (1 << IWL_RATE_2M_INDEX)
+#define        IWL_RATE_5M_MASK   (1 << IWL_RATE_5M_INDEX)
+#define        IWL_RATE_11M_MASK  (1 << IWL_RATE_11M_INDEX)
+
+
+/* uCode API values for OFDM high-throughput (HT) bit rates */
+enum {
+       IWL_RATE_SISO_6M_PLCP = 0,
+       IWL_RATE_SISO_12M_PLCP = 1,
+       IWL_RATE_SISO_18M_PLCP = 2,
+       IWL_RATE_SISO_24M_PLCP = 3,
+       IWL_RATE_SISO_36M_PLCP = 4,
+       IWL_RATE_SISO_48M_PLCP = 5,
+       IWL_RATE_SISO_54M_PLCP = 6,
+       IWL_RATE_SISO_60M_PLCP = 7,
+       IWL_RATE_MIMO2_6M_PLCP  = 0x8,
+       IWL_RATE_MIMO2_12M_PLCP = 0x9,
+       IWL_RATE_MIMO2_18M_PLCP = 0xa,
+       IWL_RATE_MIMO2_24M_PLCP = 0xb,
+       IWL_RATE_MIMO2_36M_PLCP = 0xc,
+       IWL_RATE_MIMO2_48M_PLCP = 0xd,
+       IWL_RATE_MIMO2_54M_PLCP = 0xe,
+       IWL_RATE_MIMO2_60M_PLCP = 0xf,
+       IWL_RATE_MIMO3_6M_PLCP  = 0x10,
+       IWL_RATE_MIMO3_12M_PLCP = 0x11,
+       IWL_RATE_MIMO3_18M_PLCP = 0x12,
+       IWL_RATE_MIMO3_24M_PLCP = 0x13,
+       IWL_RATE_MIMO3_36M_PLCP = 0x14,
+       IWL_RATE_MIMO3_48M_PLCP = 0x15,
+       IWL_RATE_MIMO3_54M_PLCP = 0x16,
+       IWL_RATE_MIMO3_60M_PLCP = 0x17,
+       IWL_RATE_SISO_INVM_PLCP,
+       IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
+       IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
+};
+
+/* MAC header values for bit rates */
+enum {
+       IWL_RATE_6M_IEEE  = 12,
+       IWL_RATE_9M_IEEE  = 18,
+       IWL_RATE_12M_IEEE = 24,
+       IWL_RATE_18M_IEEE = 36,
+       IWL_RATE_24M_IEEE = 48,
+       IWL_RATE_36M_IEEE = 72,
+       IWL_RATE_48M_IEEE = 96,
+       IWL_RATE_54M_IEEE = 108,
+       IWL_RATE_60M_IEEE = 120,
+       IWL_RATE_1M_IEEE  = 2,
+       IWL_RATE_2M_IEEE  = 4,
+       IWL_RATE_5M_IEEE  = 11,
+       IWL_RATE_11M_IEEE = 22,
+};
+
+#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
+
+#define IWL_INVALID_VALUE    -1
+
+#define IWL_MIN_RSSI_VAL                 -100
+#define IWL_MAX_RSSI_VAL                    0
+
+/* These values specify how many Tx frame attempts before
+ * searching for a new modulation mode */
+#define IWL_LEGACY_FAILURE_LIMIT       160
+#define IWL_LEGACY_SUCCESS_LIMIT       480
+#define IWL_LEGACY_TABLE_COUNT         160
+
+#define IWL_NONE_LEGACY_FAILURE_LIMIT  400
+#define IWL_NONE_LEGACY_SUCCESS_LIMIT  4500
+#define IWL_NONE_LEGACY_TABLE_COUNT    1500
+
+/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */
+#define IWL_RS_GOOD_RATIO              12800   /* 100% */
+#define IWL_RATE_SCALE_SWITCH          10880   /*  85% */
+#define IWL_RATE_HIGH_TH               10880   /*  85% */
+#define IWL_RATE_INCREASE_TH           6400    /*  50% */
+#define IWL_RATE_DECREASE_TH           1920    /*  15% */
+
+/* possible actions when in legacy mode */
+#define IWL_LEGACY_SWITCH_ANTENNA1      0
+#define IWL_LEGACY_SWITCH_ANTENNA2      1
+#define IWL_LEGACY_SWITCH_SISO          2
+#define IWL_LEGACY_SWITCH_MIMO2_AB      3
+#define IWL_LEGACY_SWITCH_MIMO2_AC      4
+#define IWL_LEGACY_SWITCH_MIMO2_BC      5
+#define IWL_LEGACY_SWITCH_MIMO3_ABC     6
+
+/* possible actions when in siso mode */
+#define IWL_SISO_SWITCH_ANTENNA1        0
+#define IWL_SISO_SWITCH_ANTENNA2        1
+#define IWL_SISO_SWITCH_MIMO2_AB        2
+#define IWL_SISO_SWITCH_MIMO2_AC        3
+#define IWL_SISO_SWITCH_MIMO2_BC        4
+#define IWL_SISO_SWITCH_GI              5
+#define IWL_SISO_SWITCH_MIMO3_ABC       6
+
+
+/* possible actions when in mimo mode */
+#define IWL_MIMO2_SWITCH_ANTENNA1       0
+#define IWL_MIMO2_SWITCH_ANTENNA2       1
+#define IWL_MIMO2_SWITCH_SISO_A         2
+#define IWL_MIMO2_SWITCH_SISO_B         3
+#define IWL_MIMO2_SWITCH_SISO_C         4
+#define IWL_MIMO2_SWITCH_GI             5
+#define IWL_MIMO2_SWITCH_MIMO3_ABC      6
+
+
+/* possible actions when in mimo3 mode */
+#define IWL_MIMO3_SWITCH_ANTENNA1       0
+#define IWL_MIMO3_SWITCH_ANTENNA2       1
+#define IWL_MIMO3_SWITCH_SISO_A         2
+#define IWL_MIMO3_SWITCH_SISO_B         3
+#define IWL_MIMO3_SWITCH_SISO_C         4
+#define IWL_MIMO3_SWITCH_MIMO2_AB       5
+#define IWL_MIMO3_SWITCH_MIMO2_AC       6
+#define IWL_MIMO3_SWITCH_MIMO2_BC       7
+#define IWL_MIMO3_SWITCH_GI             8
+
+
+#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI
+#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC
+
+/*FIXME:RS:add possible actions for MIMO3*/
+
+#define IWL_ACTION_LIMIT               3       /* # possible actions */
+
+#define LINK_QUAL_AGG_TIME_LIMIT_DEF   (4000) /* 4 milliseconds */
+#define LINK_QUAL_AGG_TIME_LIMIT_MAX   (8000)
+#define LINK_QUAL_AGG_TIME_LIMIT_MIN   (100)
+
+#define LINK_QUAL_AGG_DISABLE_START_DEF        (3)
+#define LINK_QUAL_AGG_DISABLE_START_MAX        (255)
+#define LINK_QUAL_AGG_DISABLE_START_MIN        (0)
+
+#define LINK_QUAL_AGG_FRAME_LIMIT_DEF  (63)
+#define LINK_QUAL_AGG_FRAME_LIMIT_MAX  (63)
+#define LINK_QUAL_AGG_FRAME_LIMIT_MIN  (0)
+
+#define LQ_SIZE                2       /* 2 mode tables:  "Active" and "Search" */
+
+/* load per tid defines for A-MPDU activation */
+#define IWL_AGG_TPT_THREHOLD   0
+#define IWL_AGG_LOAD_THRESHOLD 10
+#define IWL_AGG_ALL_TID                0xff
+#define TID_QUEUE_CELL_SPACING 50      /*mS */
+#define TID_QUEUE_MAX_SIZE     20
+#define TID_ROUND_VALUE                5       /* mS */
+
+#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING)
+#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y))
+
+enum iwl_table_type {
+       LQ_NONE,
+       LQ_G,           /* legacy types */
+       LQ_A,
+       LQ_SISO,        /* high-throughput types */
+       LQ_MIMO2,
+       LQ_MIMO3,
+       LQ_MAX,
+};
+
+#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A))
+#define is_siso(tbl) ((tbl) == LQ_SISO)
+#define is_mimo2(tbl) ((tbl) == LQ_MIMO2)
+#define is_mimo3(tbl) ((tbl) == LQ_MIMO3)
+#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl))
+#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl))
+#define is_a_band(tbl) ((tbl) == LQ_A)
+#define is_g_and(tbl) ((tbl) == LQ_G)
+
+#define IWL_MAX_MCS_DISPLAY_SIZE       12
+
+struct iwl_rate_mcs_info {
+       char    mbps[IWL_MAX_MCS_DISPLAY_SIZE];
+       char    mcs[IWL_MAX_MCS_DISPLAY_SIZE];
+};
+
+/**
+ * struct iwl_rate_scale_data -- tx success history for one rate
+ */
+struct iwl_rate_scale_data {
+       u64 data;               /* bitmap of successful frames */
+       s32 success_counter;    /* number of frames successful */
+       s32 success_ratio;      /* per-cent * 128  */
+       s32 counter;            /* number of frames attempted */
+       s32 average_tpt;        /* success ratio * expected throughput */
+       unsigned long stamp;
+};
+
+/**
+ * struct iwl_scale_tbl_info -- tx params and success history for all rates
+ *
+ * There are two of these in struct iwl_lq_sta,
+ * one for "active", and one for "search".
+ */
+struct iwl_scale_tbl_info {
+       enum iwl_table_type lq_type;
+       u8 ant_type;
+       u8 is_SGI;      /* 1 = short guard interval */
+       u8 is_ht40;     /* 1 = 40 MHz channel width */
+       u8 action;      /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
+       u8 max_search;  /* maximun number of tables we can search */
+       s32 *expected_tpt;      /* throughput metrics; expected_tpt_G, etc. */
+       u32 current_rate;  /* rate_n_flags, uCode API format */
+       struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
+};
+
+struct iwl_traffic_load {
+       unsigned long time_stamp;       /* age of the oldest statistics */
+       u32 packet_count[TID_QUEUE_MAX_SIZE];   /* packet count in this time
+                                                * slice */
+       u32 total;                      /* total num of packets during the
+                                        * last TID_MAX_TIME_DIFF */
+       u8 queue_count;                 /* number of queues that has
+                                        * been used since the last cleanup */
+       u8 head;                        /* start of the circular buffer */
+};
+
+/**
+ * struct iwl_lq_sta -- driver's rate scaling private structure
+ *
+ * Pointer to this gets passed back and forth between driver and mac80211.
+ */
+struct iwl_lq_sta {
+       u8 active_tbl;          /* index of active table, range 0-1 */
+       u8 enable_counter;      /* indicates HT mode */
+       u8 stay_in_tbl;         /* 1: disallow, 0: allow search for new mode */
+       u8 search_better_tbl;   /* 1: currently trying alternate mode */
+       s32 last_tpt;
+
+       /* The following determine when to search for a new mode */
+       u32 table_count_limit;
+       u32 max_failure_limit;  /* # failed frames before new search */
+       u32 max_success_limit;  /* # successful frames before new search */
+       u32 table_count;
+       u32 total_failed;       /* total failed frames, any/all rates */
+       u32 total_success;      /* total successful frames, any/all rates */
+       u64 flush_timer;        /* time staying in mode before new search */
+
+       u8 action_counter;      /* # mode-switch actions tried */
+       u8 is_green;
+       enum ieee80211_band band;
+
+       /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
+       u32 supp_rates;
+       u16 active_legacy_rate;
+       u16 active_siso_rate;
+       u16 active_mimo2_rate;
+       u16 active_mimo3_rate;
+       s8 max_rate_idx;     /* Max rate set by user */
+       u8 missed_rate_counter;
+
+       struct iwl_lq_cmd lq;
+       struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
+       struct iwl_traffic_load load[IWL_MAX_TID_COUNT];
+       u8 tx_agg_tid_en;
+#ifdef CONFIG_MAC80211_DEBUGFS
+       struct dentry *rs_sta_dbgfs_scale_table_file;
+       struct dentry *rs_sta_dbgfs_stats_table_file;
+       struct dentry *rs_sta_dbgfs_rate_scale_data_file;
+       struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
+       u32 dbg_fixed_rate;
+#endif
+       struct iwl_mvm *drv;
+
+       /* used to be in sta_info */
+       int last_txrate_idx;
+       /* last tx rate_n_flags */
+       u32 last_rate_n_flags;
+       /* packets destined for this STA are aggregated */
+       u8 is_agg;
+       /* BT traffic this sta was last updated in */
+       u8 last_bt_traffic;
+};
+
+static inline u8 num_of_ant(u8 mask)
+{
+       return  !!((mask) & ANT_A) +
+               !!((mask) & ANT_B) +
+               !!((mask) & ANT_C);
+}
+
+/* Initialize station's rate scaling information after adding station */
+extern void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm,
+                                struct ieee80211_sta *sta,
+                                enum ieee80211_band band);
+
+/**
+ * iwl_rate_control_register - Register the rate control algorithm callbacks
+ *
+ * Since the rate control algorithm is hardware specific, there is no need
+ * or reason to place it as a stand alone module.  The driver can call
+ * iwl_rate_control_register in order to register the rate control callbacks
+ * with the mac80211 subsystem.  This should be performed prior to calling
+ * ieee80211_register_hw
+ *
+ */
+extern int iwl_mvm_rate_control_register(void);
+
+/**
+ * iwl_rate_control_unregister - Unregister the rate control callbacks
+ *
+ * This should be called after calling ieee80211_unregister_hw, but before
+ * the driver is unloaded.
+ */
+extern void iwl_mvm_rate_control_unregister(void);
+
+#endif /* __rs__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
new file mode 100644 (file)
index 0000000..52da375
--- /dev/null
@@ -0,0 +1,355 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include "iwl-trans.h"
+
+#include "mvm.h"
+#include "fw-api.h"
+
+/*
+ * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler
+ *
+ * Copies the phy information in mvm->last_phy_info, it will be used when the
+ * actual data will come from the fw in the next packet.
+ */
+int iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+
+       memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info));
+       mvm->ampdu_ref++;
+       return 0;
+}
+
+/*
+ * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211
+ *
+ * Adds the rxb to a new skb and give it to mac80211
+ */
+static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
+                                           struct ieee80211_hdr *hdr, u16 len,
+                                           u32 ampdu_status,
+                                           struct iwl_rx_cmd_buffer *rxb,
+                                           struct ieee80211_rx_status *stats)
+{
+       struct sk_buff *skb;
+       unsigned int hdrlen, fraglen;
+
+       /* Dont use dev_alloc_skb(), we'll have enough headroom once
+        * ieee80211_hdr pulled.
+        */
+       skb = alloc_skb(128, GFP_ATOMIC);
+       if (!skb) {
+               IWL_ERR(mvm, "alloc_skb failed\n");
+               return;
+       }
+       /* If frame is small enough to fit in skb->head, pull it completely.
+        * If not, only pull ieee80211_hdr so that splice() or TCP coalesce
+        * are more efficient.
+        */
+       hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr);
+
+       memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
+       fraglen = len - hdrlen;
+
+       if (fraglen) {
+               int offset = (void *)hdr + hdrlen -
+                            rxb_addr(rxb) + rxb_offset(rxb);
+
+               skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
+                               fraglen, rxb->truesize);
+       }
+
+       memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
+
+       ieee80211_rx(mvm->hw, skb);
+}
+
+/*
+ * iwl_mvm_calc_rssi - calculate the rssi in dBm
+ * @phy_info: the phy information for the coming packet
+ */
+static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
+                            struct iwl_rx_phy_info *phy_info)
+{
+       u32 rssi_a, rssi_b, rssi_c, max_rssi, agc_db;
+       u32 val;
+
+       /* Find max rssi among 3 possible receivers.
+        * These values are measured by the Digital Signal Processor (DSP).
+        * They should stay fairly constant even as the signal strength varies,
+        * if the radio's Automatic Gain Control (AGC) is working right.
+        * AGC value (see below) will provide the "interesting" info.
+        */
+       val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]);
+       rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS;
+       rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS;
+       val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_C_IDX]);
+       rssi_c = (val & IWL_OFDM_RSSI_INBAND_C_MSK) >> IWL_OFDM_RSSI_C_POS;
+
+       val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]);
+       agc_db = (val & IWL_OFDM_AGC_DB_MSK) >> IWL_OFDM_AGC_DB_POS;
+
+       max_rssi = max_t(u32, rssi_a, rssi_b);
+       max_rssi = max_t(u32, max_rssi, rssi_c);
+
+       IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d C %d Max %d AGC dB %d\n",
+                       rssi_a, rssi_b, rssi_c, max_rssi, agc_db);
+
+       /* dBm = max_rssi dB - agc dB - constant.
+        * Higher AGC (higher radio gain) means lower signal. */
+       return max_rssi - agc_db - IWL_RSSI_OFFSET;
+}
+
+/*
+ * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format
+ * @mvm: the mvm object
+ * @hdr: 80211 header
+ * @stats: status in mac80211's format
+ * @rx_pkt_status: status coming from fw
+ *
+ * returns non 0 value if the packet should be dropped
+ */
+static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
+                                       struct ieee80211_hdr *hdr,
+                                       struct ieee80211_rx_status *stats,
+                                       u32 rx_pkt_status)
+{
+       if (!ieee80211_has_protected(hdr->frame_control) ||
+           (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
+                            RX_MPDU_RES_STATUS_SEC_NO_ENC)
+               return 0;
+
+       /* packet was encrypted with unknown alg */
+       if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
+                                       RX_MPDU_RES_STATUS_SEC_ENC_ERR)
+               return 0;
+
+       switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) {
+       case RX_MPDU_RES_STATUS_SEC_CCM_ENC:
+               /* alg is CCM: check MIC only */
+               if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
+                       return -1;
+
+               stats->flag |= RX_FLAG_DECRYPTED;
+               IWL_DEBUG_WEP(mvm, "hw decrypted CCMP successfully\n");
+               return 0;
+
+       case RX_MPDU_RES_STATUS_SEC_TKIP_ENC:
+               /* Don't drop the frame and decrypt it in SW */
+               if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK))
+                       return 0;
+               /* fall through if TTAK OK */
+
+       case RX_MPDU_RES_STATUS_SEC_WEP_ENC:
+               if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK))
+                       return -1;
+
+               stats->flag |= RX_FLAG_DECRYPTED;
+               return 0;
+
+       default:
+               IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
+       }
+
+       return 0;
+}
+
+/*
+ * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler
+ *
+ * Handles the actual data of the Rx packet from the fw
+ */
+int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                      struct iwl_device_cmd *cmd)
+{
+       struct ieee80211_hdr *hdr;
+       struct ieee80211_rx_status rx_status = {};
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_rx_phy_info *phy_info;
+       struct iwl_rx_mpdu_res_start *rx_res;
+       u32 len;
+       u32 ampdu_status;
+       u32 rate_n_flags;
+       u32 rx_pkt_status;
+
+       phy_info = &mvm->last_phy_info;
+       rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
+       hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res));
+       len = le16_to_cpu(rx_res->byte_count);
+       rx_pkt_status = le32_to_cpup((__le32 *)
+               (pkt->data + sizeof(*rx_res) + len));
+
+       memset(&rx_status, 0, sizeof(rx_status));
+
+       /*
+        * drop the packet if it has failed being decrypted by HW
+        */
+       if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, &rx_status, rx_pkt_status)) {
+               IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
+                              rx_pkt_status);
+               return 0;
+       }
+
+       if ((unlikely(phy_info->cfg_phy_cnt > 20))) {
+               IWL_DEBUG_DROP(mvm, "dsp size out of range [0,20]: %d\n",
+                              phy_info->cfg_phy_cnt);
+               return 0;
+       }
+
+       if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
+           !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
+               IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
+               return 0;
+       }
+
+       /* This will be used in several places later */
+       rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
+
+       /* rx_status carries information about the packet to mac80211 */
+       rx_status.mactime = le64_to_cpu(phy_info->timestamp);
+       rx_status.band =
+               (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
+                               IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+       rx_status.freq =
+               ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
+                                              rx_status.band);
+       /*
+        * TSF as indicated by the fw is at INA time, but mac80211 expects the
+        * TSF at the beginning of the MPDU.
+        */
+       /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
+
+       /* Find max signal strength (dBm) among 3 antenna/receiver chains */
+       rx_status.signal = iwl_mvm_calc_rssi(mvm, phy_info);
+
+       IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
+                             (unsigned long long)rx_status.mactime);
+
+       /*
+        * "antenna number"
+        *
+        * It seems that the antenna field in the phy flags value
+        * is actually a bit field. This is undefined by radiotap,
+        * it wants an actual antenna number but I always get "7"
+        * for most legacy frames I receive indicating that the
+        * same frame was received on all three RX chains.
+        *
+        * I think this field should be removed in favor of a
+        * new 802.11n radiotap field "RX chains" that is defined
+        * as a bitmask.
+        */
+       rx_status.antenna = (le16_to_cpu(phy_info->phy_flags) &
+                               RX_RES_PHY_FLAGS_ANTENNA)
+                               >> RX_RES_PHY_FLAGS_ANTENNA_POS;
+
+       /* set the preamble flag if appropriate */
+       if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
+               rx_status.flag |= RX_FLAG_SHORTPRE;
+
+       if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
+               /*
+                * We know which subframes of an A-MPDU belong
+                * together since we get a single PHY response
+                * from the firmware for all of them
+                */
+               rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
+               rx_status.ampdu_reference = mvm->ampdu_ref;
+       }
+
+       /* Set up the HT phy flags */
+       switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
+       case RATE_MCS_CHAN_WIDTH_20:
+               break;
+       case RATE_MCS_CHAN_WIDTH_40:
+               rx_status.flag |= RX_FLAG_40MHZ;
+               break;
+       case RATE_MCS_CHAN_WIDTH_80:
+               rx_status.flag |= RX_FLAG_80MHZ;
+               break;
+       case RATE_MCS_CHAN_WIDTH_160:
+               rx_status.flag |= RX_FLAG_160MHZ;
+               break;
+       }
+       if (rate_n_flags & RATE_MCS_SGI_MSK)
+               rx_status.flag |= RX_FLAG_SHORT_GI;
+       if (rate_n_flags & RATE_HT_MCS_GF_MSK)
+               rx_status.flag |= RX_FLAG_HT_GF;
+       if (rate_n_flags & RATE_MCS_HT_MSK) {
+               rx_status.flag |= RX_FLAG_HT;
+               rx_status.rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+       } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+               rx_status.vht_nss =
+                       ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
+                                               RATE_VHT_MCS_NSS_POS) + 1;
+               rx_status.rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
+               rx_status.flag |= RX_FLAG_VHT;
+       } else {
+               rx_status.rate_idx =
+                       iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
+                                                           rx_status.band);
+       }
+
+       iwl_mvm_pass_packet_to_mac80211(mvm, hdr, len, ampdu_status,
+                                       rxb, &rx_status);
+       return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
new file mode 100644 (file)
index 0000000..406c53a
--- /dev/null
@@ -0,0 +1,437 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+
+#include "mvm.h"
+#include "iwl-eeprom-parse.h"
+#include "fw-api-scan.h"
+
+#define IWL_PLCP_QUIET_THRESH 1
+#define IWL_ACTIVE_QUIET_TIME 10
+
+static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
+{
+       u16 rx_chain;
+       u8 rx_ant = mvm->nvm_data->valid_rx_ant;
+
+       rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
+       rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
+       rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS;
+       rx_chain |= 0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS;
+       return cpu_to_le16(rx_chain);
+}
+
+static inline __le32 iwl_mvm_scan_max_out_time(struct ieee80211_vif *vif)
+{
+       if (vif->bss_conf.assoc)
+               return cpu_to_le32(200 * 1024);
+       else
+               return 0;
+}
+
+static inline __le32 iwl_mvm_scan_suspend_time(struct ieee80211_vif *vif)
+{
+       if (vif->bss_conf.assoc)
+               return cpu_to_le32(vif->bss_conf.beacon_int);
+       else
+               return 0;
+}
+
+static inline __le32
+iwl_mvm_scan_rxon_flags(struct cfg80211_scan_request *req)
+{
+       if (req->channels[0]->band == IEEE80211_BAND_2GHZ)
+               return cpu_to_le32(PHY_BAND_24);
+       else
+               return cpu_to_le32(PHY_BAND_5);
+}
+
+static inline __le32
+iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
+                         bool no_cck)
+{
+       u32 tx_ant;
+
+       mvm->scan_last_antenna_idx =
+               iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant,
+                                    mvm->scan_last_antenna_idx);
+       tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS;
+
+       if (band == IEEE80211_BAND_2GHZ && !no_cck)
+               return cpu_to_le32(IWL_RATE_1M_PLCP | RATE_MCS_CCK_MSK |
+                                  tx_ant);
+       else
+               return cpu_to_le32(IWL_RATE_6M_PLCP | tx_ant);
+}
+
+/*
+ * We insert the SSIDs in an inverted order, because the FW will
+ * invert it back. The most prioritized SSID, which is first in the
+ * request list, is not copied here, but inserted directly to the probe
+ * request.
+ */
+static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd,
+                                   struct cfg80211_scan_request *req)
+{
+       int fw_idx, req_idx;
+
+       fw_idx = 0;
+       for (req_idx = req->n_ssids - 1; req_idx > 0; req_idx--) {
+               cmd->direct_scan[fw_idx].id = WLAN_EID_SSID;
+               cmd->direct_scan[fw_idx].len = req->ssids[req_idx].ssid_len;
+               memcpy(cmd->direct_scan[fw_idx].ssid,
+                      req->ssids[req_idx].ssid,
+                      req->ssids[req_idx].ssid_len);
+       }
+}
+
+/*
+ * If req->n_ssids > 0, it means we should do an active scan.
+ * In case of active scan w/o directed scan, we receive a zero-length SSID
+ * just to notify that this scan is active and not passive.
+ * In order to notify the FW of the number of SSIDs we wish to scan (including
+ * the zero-length one), we need to set the corresponding bits in chan->type,
+ * one for each SSID, and set the active bit (first).
+ */
+static u16 iwl_mvm_get_active_dwell(enum ieee80211_band band, int n_ssids)
+{
+       if (band == IEEE80211_BAND_2GHZ)
+               return 30  + 3 * (n_ssids + 1);
+       return 20  + 2 * (n_ssids + 1);
+}
+
+static u16 iwl_mvm_get_passive_dwell(enum ieee80211_band band)
+{
+       return band == IEEE80211_BAND_2GHZ ? 100 + 20 : 100 + 10;
+}
+
+static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
+                                      struct cfg80211_scan_request *req)
+{
+       u16 passive_dwell = iwl_mvm_get_passive_dwell(req->channels[0]->band);
+       u16 active_dwell = iwl_mvm_get_active_dwell(req->channels[0]->band,
+                                                   req->n_ssids);
+       struct iwl_scan_channel *chan = (struct iwl_scan_channel *)
+               (cmd->data + le16_to_cpu(cmd->tx_cmd.len));
+       int i;
+       __le32 chan_type_value;
+
+       if (req->n_ssids > 0)
+               chan_type_value = cpu_to_le32(BIT(req->n_ssids + 1) - 1);
+       else
+               chan_type_value = SCAN_CHANNEL_TYPE_PASSIVE;
+
+       for (i = 0; i < cmd->channel_count; i++) {
+               chan->channel = cpu_to_le16(req->channels[i]->hw_value);
+               if (req->channels[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+                       chan->type = SCAN_CHANNEL_TYPE_PASSIVE;
+               else
+                       chan->type = chan_type_value;
+               chan->active_dwell = cpu_to_le16(active_dwell);
+               chan->passive_dwell = cpu_to_le16(passive_dwell);
+               chan->iteration_count = cpu_to_le16(1);
+               chan++;
+       }
+}
+
+/*
+ * Fill in probe request with the following parameters:
+ * TA is our vif HW address, which mac80211 ensures we have.
+ * Packet is broadcasted, so this is both SA and DA.
+ * The probe request IE is made out of two: first comes the most prioritized
+ * SSID if a directed scan is requested. Second comes whatever extra
+ * information was given to us as the scan request IE.
+ */
+static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
+                                 int n_ssids, const u8 *ssid, int ssid_len,
+                                 const u8 *ie, int ie_len,
+                                 int left)
+{
+       int len = 0;
+       u8 *pos = NULL;
+
+       /* Make sure there is enough space for the probe request,
+        * two mandatory IEs and the data */
+       left -= 24;
+       if (left < 0)
+               return 0;
+
+       frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
+       eth_broadcast_addr(frame->da);
+       memcpy(frame->sa, ta, ETH_ALEN);
+       eth_broadcast_addr(frame->bssid);
+       frame->seq_ctrl = 0;
+
+       len += 24;
+
+       /* for passive scans, no need to fill anything */
+       if (n_ssids == 0)
+               return (u16)len;
+
+       /* points to the payload of the request */
+       pos = &frame->u.probe_req.variable[0];
+
+       /* fill in our SSID IE */
+       left -= ssid_len + 2;
+       if (left < 0)
+               return 0;
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = ssid_len;
+       if (ssid && ssid_len) { /* ssid_len may be == 0 even if ssid is valid */
+               memcpy(pos, ssid, ssid_len);
+               pos += ssid_len;
+       }
+
+       len += ssid_len + 2;
+
+       if (WARN_ON(left < ie_len))
+               return len;
+
+       if (ie && ie_len) {
+               memcpy(pos, ie, ie_len);
+               len += ie_len;
+       }
+
+       return (u16)len;
+}
+
+int iwl_mvm_scan_request(struct iwl_mvm *mvm,
+                        struct ieee80211_vif *vif,
+                        struct cfg80211_scan_request *req)
+{
+       struct iwl_host_cmd hcmd = {
+               .id = SCAN_REQUEST_CMD,
+               .len = { 0, },
+               .data = { mvm->scan_cmd, },
+               .flags = CMD_SYNC,
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       struct iwl_scan_cmd *cmd = mvm->scan_cmd;
+       int ret;
+       u32 status;
+       int ssid_len = 0;
+       u8 *ssid = NULL;
+
+       lockdep_assert_held(&mvm->mutex);
+       BUG_ON(mvm->scan_cmd == NULL);
+
+       IWL_DEBUG_SCAN(mvm, "Handling mac80211 scan request\n");
+       mvm->scan_status = IWL_MVM_SCAN_OS;
+       memset(cmd, 0, sizeof(struct iwl_scan_cmd) +
+              mvm->fw->ucode_capa.max_probe_length +
+              (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel)));
+
+       cmd->channel_count = (u8)req->n_channels;
+       cmd->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME);
+       cmd->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH);
+       cmd->rxchain_sel_flags = iwl_mvm_scan_rx_chain(mvm);
+       cmd->max_out_time = iwl_mvm_scan_max_out_time(vif);
+       cmd->suspend_time = iwl_mvm_scan_suspend_time(vif);
+       cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req);
+       cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
+                                       MAC_FILTER_IN_BEACON);
+       cmd->type = SCAN_TYPE_FORCED;
+       cmd->repeats = cpu_to_le32(1);
+
+       /*
+        * If the user asked for passive scan, don't change to active scan if
+        * you see any activity on the channel - remain passive.
+        */
+       if (req->n_ssids > 0) {
+               cmd->passive2active = cpu_to_le16(1);
+               ssid = req->ssids[0].ssid;
+               ssid_len = req->ssids[0].ssid_len;
+       } else {
+               cmd->passive2active = 0;
+       }
+
+       iwl_mvm_scan_fill_ssids(cmd, req);
+
+       cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL);
+       cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id;
+       cmd->tx_cmd.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
+       cmd->tx_cmd.rate_n_flags =
+                       iwl_mvm_scan_rate_n_flags(mvm, req->channels[0]->band,
+                                                 req->no_cck);
+
+       cmd->tx_cmd.len =
+               cpu_to_le16(iwl_mvm_fill_probe_req(
+                           (struct ieee80211_mgmt *)cmd->data,
+                           vif->addr,
+                           req->n_ssids, ssid, ssid_len,
+                           req->ie, req->ie_len,
+                           mvm->fw->ucode_capa.max_probe_length));
+
+       iwl_mvm_scan_fill_channels(cmd, req);
+
+       cmd->len = cpu_to_le16(sizeof(struct iwl_scan_cmd) +
+               le16_to_cpu(cmd->tx_cmd.len) +
+               (cmd->channel_count * sizeof(struct iwl_scan_channel)));
+       hcmd.len[0] = le16_to_cpu(cmd->len);
+
+       status = SCAN_RESPONSE_OK;
+       ret = iwl_mvm_send_cmd_status(mvm, &hcmd, &status);
+       if (!ret && status == SCAN_RESPONSE_OK) {
+               IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n");
+       } else {
+               /*
+                * If the scan failed, it usually means that the FW was unable
+                * to allocate the time events. Warn on it, but maybe we
+                * should try to send the command again with different params.
+                */
+               IWL_ERR(mvm, "Scan failed! status 0x%x ret %d\n",
+                       status, ret);
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               ret = -EIO;
+       }
+       return ret;
+}
+
+int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_cmd_response *resp = (void *)pkt->data;
+
+       IWL_DEBUG_SCAN(mvm, "Scan response received. status 0x%x\n",
+                      le32_to_cpu(resp->status));
+       return 0;
+}
+
+int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_scan_complete_notif *notif = (void *)pkt->data;
+
+       IWL_DEBUG_SCAN(mvm, "Scan complete: status=0x%x scanned channels=%d\n",
+                      notif->status, notif->scanned_channels);
+
+       mvm->scan_status = IWL_MVM_SCAN_NONE;
+       ieee80211_scan_completed(mvm->hw, notif->status != SCAN_COMP_STATUS_OK);
+
+       return 0;
+}
+
+static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
+                                    struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_mvm *mvm =
+               container_of(notif_wait, struct iwl_mvm, notif_wait);
+       struct iwl_scan_complete_notif *notif;
+       u32 *resp;
+
+       switch (pkt->hdr.cmd) {
+       case SCAN_ABORT_CMD:
+               resp = (void *)pkt->data;
+               if (*resp == CAN_ABORT_STATUS) {
+                       IWL_DEBUG_SCAN(mvm,
+                                      "Scan can be aborted, wait until completion\n");
+                       return false;
+               }
+
+               IWL_DEBUG_SCAN(mvm, "Scan cannot be aborted, exit now: %d\n",
+                              *resp);
+               return true;
+
+       case SCAN_COMPLETE_NOTIFICATION:
+               notif = (void *)pkt->data;
+               IWL_DEBUG_SCAN(mvm, "Scan aborted: status 0x%x\n",
+                              notif->status);
+               return true;
+
+       default:
+               WARN_ON(1);
+               return false;
+       };
+}
+
+void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
+{
+       struct iwl_notification_wait wait_scan_abort;
+       static const u8 scan_abort_notif[] = { SCAN_ABORT_CMD,
+                                              SCAN_COMPLETE_NOTIFICATION };
+       int ret;
+
+       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);
+       if (ret) {
+               IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
+               goto out_remove_notif;
+       }
+
+       ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_abort, 1 * HZ);
+       if (ret)
+               IWL_ERR(mvm, "%s - failed on timeout\n", __func__);
+
+       return;
+
+out_remove_notif:
+       iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
new file mode 100644 (file)
index 0000000..69603c3
--- /dev/null
@@ -0,0 +1,1211 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <net/mac80211.h>
+
+#include "mvm.h"
+#include "sta.h"
+
+static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm)
+{
+       int sta_id;
+
+       WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Don't take rcu_read_lock() since we are protected by mvm->mutex */
+       for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++)
+               if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                              lockdep_is_held(&mvm->mutex)))
+                       return sta_id;
+       return IWL_MVM_STATION_COUNT;
+}
+
+/* add a NEW station to fw */
+int iwl_mvm_sta_add_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct iwl_mvm_add_sta_cmd add_sta_cmd;
+       int ret;
+       u32 status;
+       u32 agg_size = 0, mpdu_dens = 0;
+
+       memset(&add_sta_cmd, 0, sizeof(add_sta_cmd));
+
+       add_sta_cmd.sta_id = mvm_sta->sta_id;
+       add_sta_cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+       add_sta_cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk);
+       memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN);
+
+       /* STA_FLG_FAT_EN_MSK ? */
+       /* STA_FLG_MIMO_EN_MSK ? */
+
+       if (sta->ht_cap.ht_supported) {
+               add_sta_cmd.station_flags_msk |=
+                       cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK |
+                                   STA_FLG_AGG_MPDU_DENS_MSK);
+
+               mpdu_dens = sta->ht_cap.ampdu_density;
+       }
+
+       if (sta->vht_cap.vht_supported) {
+               agg_size = sta->vht_cap.cap &
+                       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+               agg_size >>=
+                       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+       } else if (sta->ht_cap.ht_supported) {
+               agg_size = sta->ht_cap.ampdu_factor;
+       }
+
+       add_sta_cmd.station_flags |=
+               cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT);
+       add_sta_cmd.station_flags |=
+               cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT);
+
+       status = ADD_STA_SUCCESS;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd),
+                                         &add_sta_cmd, &status);
+       if (ret)
+               return ret;
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_ASSOC(mvm, "ADD_STA PASSED\n");
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "ADD_STA failed\n");
+               break;
+       }
+
+       return ret;
+}
+
+int iwl_mvm_add_sta(struct iwl_mvm *mvm,
+                   struct ieee80211_vif *vif,
+                   struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       int i, ret, sta_id;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+               sta_id = iwl_mvm_find_free_sta_id(mvm);
+       else
+               sta_id = mvm_sta->sta_id;
+
+       if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
+               return -ENOSPC;
+
+       spin_lock_init(&mvm_sta->lock);
+
+       mvm_sta->sta_id = sta_id;
+       mvm_sta->mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                     mvmvif->color);
+       mvm_sta->vif = vif;
+       mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+
+       /* HW restart, don't assume the memory has been zeroed */
+       atomic_set(&mvm_sta->pending_frames, 0);
+       mvm_sta->tid_disable_agg = 0;
+       mvm_sta->tfd_queue_msk = 0;
+       for (i = 0; i < IEEE80211_NUM_ACS; i++)
+               if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
+                       mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
+
+       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
+               mvm_sta->tfd_queue_msk |= BIT(vif->cab_queue);
+
+       /* for HW restart - need to reset the seq_number etc... */
+       memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data));
+
+       ret = iwl_mvm_sta_add_to_fw(mvm, sta);
+       if (ret)
+               return ret;
+
+       /* The first station added is the AP, the others are TDLS STAs */
+       if (vif->type == NL80211_IFTYPE_STATION &&
+           mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+               mvmvif->ap_sta_id = sta_id;
+
+       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
+
+       return 0;
+}
+
+int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+                     bool drain)
+{
+       struct iwl_mvm_add_sta_cmd cmd = {};
+       int ret;
+       u32 status;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color);
+       cmd.sta_id = mvmsta->sta_id;
+       cmd.add_modify = STA_MODE_MODIFY;
+       cmd.station_flags = drain ? cpu_to_le32(STA_FLG_DRAIN_FLOW) : 0;
+       cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW);
+
+       status = ADD_STA_SUCCESS;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
+       if (ret)
+               return ret;
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_INFO(mvm, "Frames for staid %d will drained in fw\n",
+                              mvmsta->sta_id);
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "Couldn't drain frames for staid %d\n",
+                       mvmsta->sta_id);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * Remove a station from the FW table. Before sending the command to remove
+ * the station validate that the station is indeed known to the driver (sanity
+ * only).
+ */
+static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id)
+{
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_rm_sta_cmd rm_sta_cmd = {
+               .sta_id = sta_id,
+       };
+       int ret;
+
+       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                       lockdep_is_held(&mvm->mutex));
+
+       /* Note: internal stations are marked as error values */
+       if (!sta) {
+               IWL_ERR(mvm, "Invalid station id\n");
+               return -EINVAL;
+       }
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, CMD_SYNC,
+                                  sizeof(rm_sta_cmd), &rm_sta_cmd);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id);
+               return ret;
+       }
+
+       return 0;
+}
+
+void iwl_mvm_sta_drained_wk(struct work_struct *wk)
+{
+       struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, sta_drained_wk);
+       u8 sta_id;
+
+       /*
+        * The mutex is needed because of the SYNC cmd, but not only: if the
+        * work would run concurrently with iwl_mvm_rm_sta, it would run before
+        * iwl_mvm_rm_sta sets the station as busy, and exit. Then
+        * iwl_mvm_rm_sta would set the station as busy, and nobody will clean
+        * that later.
+        */
+       mutex_lock(&mvm->mutex);
+
+       for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) {
+               int ret;
+               struct ieee80211_sta *sta =
+                       rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                                 lockdep_is_held(&mvm->mutex));
+
+               /* This station is in use */
+               if (!IS_ERR(sta))
+                       continue;
+
+               if (PTR_ERR(sta) == -EINVAL) {
+                       IWL_ERR(mvm, "Drained sta %d, but it is internal?\n",
+                               sta_id);
+                       continue;
+               }
+
+               if (!sta) {
+                       IWL_ERR(mvm, "Drained sta %d, but it was NULL?\n",
+                               sta_id);
+                       continue;
+               }
+
+               WARN_ON(PTR_ERR(sta) != -EBUSY);
+               /* This station was removed and we waited until it got drained,
+                * we can now proceed and remove it.
+                */
+               ret = iwl_mvm_rm_sta_common(mvm, sta_id);
+               if (ret) {
+                       IWL_ERR(mvm,
+                               "Couldn't remove sta %d after it was drained\n",
+                               sta_id);
+                       continue;
+               }
+               rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL);
+               clear_bit(sta_id, mvm->sta_drained);
+       }
+
+       mutex_unlock(&mvm->mutex);
+}
+
+int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
+                  struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (vif->type == NL80211_IFTYPE_STATION &&
+           mvmvif->ap_sta_id == mvm_sta->sta_id) {
+               /*
+                * Put a non-NULL since the fw station isn't removed.
+                * It will be removed after the MAC will be set as
+                * unassoc.
+                */
+               rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
+                                  ERR_PTR(-EINVAL));
+
+               /* flush its queues here since we are freeing mvm_sta */
+               ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
+
+               /* if we are associated - we can't remove the AP STA now */
+               if (vif->bss_conf.assoc)
+                       return ret;
+
+               /* unassoc - go ahead - remove the AP STA now */
+               mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+       }
+
+       /*
+        * There are frames pending on the AC queues for this station.
+        * We need to wait until all the frames are drained...
+        */
+       if (atomic_read(&mvm_sta->pending_frames)) {
+               ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
+               rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
+                                  ERR_PTR(-EBUSY));
+       } else {
+               ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id);
+               rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
+       }
+
+       return ret;
+}
+
+int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
+                     struct ieee80211_vif *vif,
+                     u8 sta_id)
+{
+       int ret = iwl_mvm_rm_sta_common(mvm, sta_id);
+
+       lockdep_assert_held(&mvm->mutex);
+
+       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL);
+       return ret;
+}
+
+int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
+                            u32 qmask)
+{
+       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+               sta->sta_id = iwl_mvm_find_free_sta_id(mvm);
+               if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT))
+                       return -ENOSPC;
+       }
+
+       sta->tfd_queue_msk = qmask;
+
+       /* put a non-NULL value so iterating over the stations won't stop */
+       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], ERR_PTR(-EINVAL));
+       return 0;
+}
+
+void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta)
+{
+       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], NULL);
+       memset(sta, 0, sizeof(struct iwl_mvm_int_sta));
+       sta->sta_id = IWL_MVM_STATION_COUNT;
+}
+
+static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
+                                     struct iwl_mvm_int_sta *sta,
+                                     const u8 *addr,
+                                     u16 mac_id, u16 color)
+{
+       struct iwl_mvm_add_sta_cmd cmd;
+       int ret;
+       u32 status;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd));
+       cmd.sta_id = sta->sta_id;
+       cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
+                                                            color));
+
+       cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk);
+
+       if (addr)
+               memcpy(cmd.addr, addr, ETH_ALEN);
+
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
+       if (ret)
+               return ret;
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_INFO(mvm, "Internal station added.\n");
+               return 0;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "Add internal station failed, status=0x%x\n",
+                       status);
+               break;
+       }
+       return ret;
+}
+
+int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
+{
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Add the aux station, but without any queues */
+       ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0);
+       if (ret)
+               return ret;
+
+       ret = iwl_mvm_add_int_sta_common(mvm, &mvm->aux_sta, NULL,
+                                        MAC_INDEX_AUX, 0);
+
+       if (ret)
+               iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta);
+       return ret;
+}
+
+/*
+ * Send the add station command for the vif's broadcast station.
+ * Assumes that the station was already allocated.
+ *
+ * @mvm: the mvm component
+ * @vif: the interface to which the broadcast station is added
+ * @bsta: the broadcast station to add.
+ */
+int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                          struct iwl_mvm_int_sta *bsta)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       static const u8 baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT))
+               return -ENOSPC;
+
+       return iwl_mvm_add_int_sta_common(mvm, bsta, baddr,
+                                         mvmvif->id, mvmvif->color);
+}
+
+/* Send the FW a request to remove the station from it's internal data
+ * structures, but DO NOT remove the entry from the local data structures. */
+int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm,
+                             struct iwl_mvm_int_sta *bsta)
+{
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_mvm_rm_sta_common(mvm, bsta->sta_id);
+       if (ret)
+               IWL_WARN(mvm, "Failed sending remove station\n");
+       return ret;
+}
+
+/* Allocate a new station entry for the broadcast station to the given vif,
+ * and send it to the FW.
+ * Note that each P2P mac should have its own broadcast station.
+ *
+ * @mvm: the mvm component
+ * @vif: the interface to which the broadcast station is added
+ * @bsta: the broadcast station to add. */
+int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         struct iwl_mvm_int_sta *bsta)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       static const u8 baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+       u32 qmask;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
+       ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask);
+       if (ret)
+               return ret;
+
+       ret = iwl_mvm_add_int_sta_common(mvm, bsta, baddr,
+                                        mvmvif->id, mvmvif->color);
+
+       if (ret)
+               iwl_mvm_dealloc_int_sta(mvm, bsta);
+       return ret;
+}
+
+/*
+ * Send the FW a request to remove the station from it's internal data
+ * structures, and in addition remove it from the local data structure.
+ */
+int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *bsta)
+{
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_mvm_rm_sta_common(mvm, bsta->sta_id);
+       if (ret)
+               return ret;
+
+       iwl_mvm_dealloc_int_sta(mvm, bsta);
+       return ret;
+}
+
+int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                      int tid, u16 ssn, bool start)
+{
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct iwl_mvm_add_sta_cmd cmd = {};
+       int ret;
+       u32 status;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+       cmd.sta_id = mvm_sta->sta_id;
+       cmd.add_modify = STA_MODE_MODIFY;
+       cmd.add_immediate_ba_tid = (u8) tid;
+       cmd.add_immediate_ba_ssn = cpu_to_le16(ssn);
+       cmd.modify_mask = start ? STA_MODIFY_ADD_BA_TID :
+                                 STA_MODIFY_REMOVE_BA_TID;
+
+       status = ADD_STA_SUCCESS;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
+       if (ret)
+               return ret;
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_INFO(mvm, "RX BA Session %sed in fw\n",
+                              start ? "start" : "stopp");
+               break;
+       case ADD_STA_IMMEDIATE_BA_FAILURE:
+               IWL_WARN(mvm, "RX BA Session refused by fw\n");
+               ret = -ENOSPC;
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "RX BA Session failed %sing, status 0x%x\n",
+                       start ? "start" : "stopp", status);
+               break;
+       }
+
+       return ret;
+}
+
+static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                             int tid, u8 queue, bool start)
+{
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct iwl_mvm_add_sta_cmd cmd = {};
+       int ret;
+       u32 status;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (start) {
+               mvm_sta->tfd_queue_msk |= BIT(queue);
+               mvm_sta->tid_disable_agg &= ~BIT(tid);
+       } else {
+               mvm_sta->tfd_queue_msk &= ~BIT(queue);
+               mvm_sta->tid_disable_agg |= BIT(tid);
+       }
+
+       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+       cmd.sta_id = mvm_sta->sta_id;
+       cmd.add_modify = STA_MODE_MODIFY;
+       cmd.modify_mask = STA_MODIFY_QUEUES | STA_MODIFY_TID_DISABLE_TX;
+       cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk);
+       cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg);
+
+       status = ADD_STA_SUCCESS;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
+       if (ret)
+               return ret;
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "TX BA Session failed %sing, status 0x%x\n",
+                       start ? "start" : "stopp", status);
+               break;
+       }
+
+       return ret;
+}
+
+static const u8 tid_to_ac[] = {
+       IEEE80211_AC_BE,
+       IEEE80211_AC_BK,
+       IEEE80211_AC_BK,
+       IEEE80211_AC_BE,
+       IEEE80211_AC_VI,
+       IEEE80211_AC_VI,
+       IEEE80211_AC_VO,
+       IEEE80211_AC_VO,
+};
+
+int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                            struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data;
+       int txq_id;
+
+       if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+               return -EINVAL;
+
+       if (mvmsta->tid_data[tid].state != IWL_AGG_OFF) {
+               IWL_ERR(mvm, "Start AGG when state is not IWL_AGG_OFF %d!\n",
+                       mvmsta->tid_data[tid].state);
+               return -ENXIO;
+       }
+
+       lockdep_assert_held(&mvm->mutex);
+
+       for (txq_id = IWL_MVM_FIRST_AGG_QUEUE;
+            txq_id <= IWL_MVM_LAST_AGG_QUEUE; txq_id++)
+               if (mvm->queue_to_mac80211[txq_id] ==
+                   IWL_INVALID_MAC80211_QUEUE)
+                       break;
+
+       if (txq_id > IWL_MVM_LAST_AGG_QUEUE) {
+               IWL_ERR(mvm, "Failed to allocate agg queue\n");
+               return -EIO;
+       }
+
+       /* the new tx queue is still connected to the same mac80211 queue */
+       mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_ac[tid]];
+
+       spin_lock_bh(&mvmsta->lock);
+       tid_data = &mvmsta->tid_data[tid];
+       tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+       tid_data->txq_id = txq_id;
+       *ssn = tid_data->ssn;
+
+       IWL_DEBUG_TX_QUEUES(mvm,
+                           "Start AGG: sta %d tid %d queue %d - ssn = %d, next_recl = %d\n",
+                           mvmsta->sta_id, tid, txq_id, tid_data->ssn,
+                           tid_data->next_reclaimed);
+
+       if (tid_data->ssn == tid_data->next_reclaimed) {
+               tid_data->state = IWL_AGG_STARTING;
+               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+       } else {
+               tid_data->state = IWL_EMPTYING_HW_QUEUE_ADDBA;
+       }
+
+       spin_unlock_bh(&mvmsta->lock);
+
+       return 0;
+}
+
+int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta, u16 tid, u8 buf_size)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+       int queue, fifo, ret;
+       u16 ssn;
+
+       buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF);
+
+       spin_lock_bh(&mvmsta->lock);
+       ssn = tid_data->ssn;
+       queue = tid_data->txq_id;
+       tid_data->state = IWL_AGG_ON;
+       tid_data->ssn = 0xffff;
+       spin_unlock_bh(&mvmsta->lock);
+
+       fifo = iwl_mvm_ac_to_tx_fifo[tid_to_ac[tid]];
+
+       ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true);
+       if (ret)
+               return -EIO;
+
+       iwl_trans_txq_enable(mvm->trans, queue, fifo, mvmsta->sta_id, tid,
+                            buf_size, ssn);
+
+       /*
+        * Even though in theory the peer could have different
+        * aggregation reorder buffer sizes for different sessions,
+        * our ucode doesn't allow for that and has a global limit
+        * for each station. Therefore, use the minimum of all the
+        * aggregation sessions and our default value.
+        */
+       mvmsta->max_agg_bufsize =
+               min(mvmsta->max_agg_bufsize, buf_size);
+       mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
+
+       if (mvm->cfg->ht_params->use_rts_for_aggregation) {
+               /*
+                * switch to RTS/CTS if it is the prefer protection
+                * method for HT traffic
+                */
+               mvmsta->lq_sta.lq.flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK;
+               /*
+                * TODO: remove the TLC_RTS flag when we tear down the last
+                * AGG session (agg_tids_count in DVM)
+                */
+       }
+
+       IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
+                    sta->addr, tid);
+
+       return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false);
+}
+
+int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta, u16 tid)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+       u16 txq_id;
+       int err;
+
+       spin_lock_bh(&mvmsta->lock);
+
+       txq_id = tid_data->txq_id;
+
+       IWL_DEBUG_TX_QUEUES(mvm, "Stop AGG: sta %d tid %d q %d state %d\n",
+                           mvmsta->sta_id, tid, txq_id, tid_data->state);
+
+       switch (tid_data->state) {
+       case IWL_AGG_ON:
+               tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+
+               IWL_DEBUG_TX_QUEUES(mvm,
+                                   "ssn = %d, next_recl = %d\n",
+                                   tid_data->ssn, tid_data->next_reclaimed);
+
+               /* There are still packets for this RA / TID in the HW */
+               if (tid_data->ssn != tid_data->next_reclaimed) {
+                       tid_data->state = IWL_EMPTYING_HW_QUEUE_DELBA;
+                       err = 0;
+                       break;
+               }
+
+               tid_data->ssn = 0xffff;
+               iwl_trans_txq_disable(mvm->trans, txq_id);
+               /* fall through */
+       case IWL_AGG_STARTING:
+       case IWL_EMPTYING_HW_QUEUE_ADDBA:
+               /*
+                * The agg session has been stopped before it was set up. This
+                * can happen when the AddBA timer times out for example.
+                */
+
+               /* No barriers since we are under mutex */
+               lockdep_assert_held(&mvm->mutex);
+               mvm->queue_to_mac80211[txq_id] = IWL_INVALID_MAC80211_QUEUE;
+
+               ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               tid_data->state = IWL_AGG_OFF;
+               err = 0;
+               break;
+       default:
+               IWL_ERR(mvm,
+                       "Stopping AGG while state not ON or starting for %d on %d (%d)\n",
+                       mvmsta->sta_id, tid, tid_data->state);
+               IWL_ERR(mvm,
+                       "\ttid_data->txq_id = %d\n", tid_data->txq_id);
+               err = -EINVAL;
+       }
+
+       spin_unlock_bh(&mvmsta->lock);
+
+       return err;
+}
+
+static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm)
+{
+       int i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       i = find_first_zero_bit(mvm->fw_key_table, STA_KEY_MAX_NUM);
+
+       if (i == STA_KEY_MAX_NUM)
+               return STA_KEY_IDX_INVALID;
+
+       __set_bit(i, mvm->fw_key_table);
+
+       return i;
+}
+
+static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif,
+                                struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+
+       if (sta) {
+               struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+
+               return mvm_sta->sta_id;
+       }
+
+       /*
+        * The device expects GTKs for station interfaces to be
+        * installed as GTKs for the AP station. If we have no
+        * station ID, then use AP's station ID.
+        */
+       if (vif->type == NL80211_IFTYPE_STATION &&
+           mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT)
+               return mvmvif->ap_sta_id;
+
+       return IWL_INVALID_STATION;
+}
+
+static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
+                               struct iwl_mvm_sta *mvm_sta,
+                               struct ieee80211_key_conf *keyconf,
+                               u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,
+                               u32 cmd_flags)
+{
+       __le16 key_flags;
+       struct iwl_mvm_add_sta_cmd cmd = {};
+       int ret, status;
+       u16 keyidx;
+       int i;
+
+       keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
+                STA_KEY_FLG_KEYID_MSK;
+       key_flags = cpu_to_le16(keyidx);
+       key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_KEY_MAP);
+
+       switch (keyconf->cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+               key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP);
+               cmd.key.tkip_rx_tsc_byte2 = tkip_iv32;
+               for (i = 0; i < 5; i++)
+                       cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
+               memcpy(cmd.key.key, keyconf->key, keyconf->keylen);
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               key_flags |= cpu_to_le16(STA_KEY_FLG_CCM);
+               memcpy(cmd.key.key, keyconf->key, keyconf->keylen);
+               break;
+       default:
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
+       if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+               key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
+
+       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+       cmd.key.key_offset = keyconf->hw_key_idx;
+       cmd.key.key_flags = key_flags;
+       cmd.add_modify = STA_MODE_MODIFY;
+       cmd.modify_mask = STA_MODIFY_KEY;
+       cmd.sta_id = sta_id;
+
+       status = ADD_STA_SUCCESS;
+       if (cmd_flags == CMD_SYNC)
+               ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                                 &cmd, &status);
+       else
+               ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC,
+                                          sizeof(cmd), &cmd);
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_WEP(mvm, "MODIFY_STA: set dynamic key passed\n");
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "MODIFY_STA: set dynamic key failed\n");
+               break;
+       }
+
+       return ret;
+}
+
+static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm,
+                                struct ieee80211_key_conf *keyconf,
+                                u8 sta_id, bool remove_key)
+{
+       struct iwl_mvm_mgmt_mcast_key_cmd igtk_cmd = {};
+
+       /* verify the key details match the required command's expectations */
+       if (WARN_ON((keyconf->cipher != WLAN_CIPHER_SUITE_AES_CMAC) ||
+                   (keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE) ||
+                   (keyconf->keyidx != 4 && keyconf->keyidx != 5)))
+               return -EINVAL;
+
+       igtk_cmd.key_id = cpu_to_le32(keyconf->keyidx);
+       igtk_cmd.sta_id = cpu_to_le32(sta_id);
+
+       if (remove_key) {
+               igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_NOT_VALID);
+       } else {
+               struct ieee80211_key_seq seq;
+               const u8 *pn;
+
+               memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen);
+               ieee80211_aes_cmac_calculate_k1_k2(keyconf,
+                                                  igtk_cmd.K1, igtk_cmd.K2);
+               ieee80211_get_key_rx_seq(keyconf, 0, &seq);
+               pn = seq.aes_cmac.pn;
+               igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) |
+                                                      ((u64) pn[4] << 8) |
+                                                      ((u64) pn[3] << 16) |
+                                                      ((u64) pn[2] << 24) |
+                                                      ((u64) pn[1] << 32) |
+                                                      ((u64) pn[0] << 40));
+       }
+
+       IWL_DEBUG_INFO(mvm, "%s igtk for sta %u\n",
+                      remove_key ? "removing" : "installing",
+                      igtk_cmd.sta_id);
+
+       return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, CMD_SYNC,
+                                   sizeof(igtk_cmd), &igtk_cmd);
+}
+
+
+static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif,
+                                      struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+
+       if (sta)
+               return sta->addr;
+
+       if (vif->type == NL80211_IFTYPE_STATION &&
+           mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+               u8 sta_id = mvmvif->ap_sta_id;
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                               lockdep_is_held(&mvm->mutex));
+               return sta->addr;
+       }
+
+
+       return NULL;
+}
+
+int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
+                       struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta,
+                       struct ieee80211_key_conf *keyconf,
+                       bool have_key_offset)
+{
+       struct iwl_mvm_sta *mvm_sta;
+       int ret;
+       u8 *addr, sta_id;
+       struct ieee80211_key_seq seq;
+       u16 p1k[5];
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Get the station id from the mvm local station table */
+       sta_id = iwl_mvm_get_key_sta_id(vif, sta);
+       if (sta_id == IWL_INVALID_STATION) {
+               IWL_ERR(mvm, "Failed to find station id\n");
+               return -EINVAL;
+       }
+
+       if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
+               ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false);
+               goto end;
+       }
+
+       /*
+        * It is possible that the 'sta' parameter is NULL, and thus
+        * there is a need to retrieve  the sta from the local station table.
+        */
+       if (!sta) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                               lockdep_is_held(&mvm->mutex));
+               if (IS_ERR_OR_NULL(sta)) {
+                       IWL_ERR(mvm, "Invalid station id\n");
+                       return -EINVAL;
+               }
+       }
+
+       mvm_sta = (struct iwl_mvm_sta *)sta->drv_priv;
+       if (WARN_ON_ONCE(mvm_sta->vif != vif))
+               return -EINVAL;
+
+       if (!have_key_offset) {
+               /*
+                * The D3 firmware hardcodes the PTK offset to 0, so we have to
+                * configure it there. As a result, this workaround exists to
+                * let the caller set the key offset (hw_key_idx), see d3.c.
+                */
+               keyconf->hw_key_idx = iwl_mvm_set_fw_key_idx(mvm);
+               if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID)
+                       return -ENOSPC;
+       }
+
+       switch (keyconf->cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+               addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
+               /* get phase 1 key from mac80211 */
+               ieee80211_get_key_rx_seq(keyconf, 0, &seq);
+               ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
+               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
+                                          seq.tkip.iv32, p1k, CMD_SYNC);
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
+                                          0, NULL, CMD_SYNC);
+               break;
+       default:
+               IWL_ERR(mvm, "Unknown cipher %x\n", keyconf->cipher);
+               ret = -EINVAL;
+       }
+
+       if (ret)
+               __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+
+end:
+       IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
+                     keyconf->cipher, keyconf->keylen, keyconf->keyidx,
+                     sta->addr, ret);
+       return ret;
+}
+
+int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
+                          struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta,
+                          struct ieee80211_key_conf *keyconf)
+{
+       struct iwl_mvm_sta *mvm_sta;
+       struct iwl_mvm_add_sta_cmd cmd = {};
+       __le16 key_flags;
+       int ret, status;
+       u8 sta_id;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Get the station id from the mvm local station table */
+       sta_id = iwl_mvm_get_key_sta_id(vif, sta);
+
+       IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n",
+                     keyconf->keyidx, sta_id);
+
+       if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+               return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true);
+
+       ret = __test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+       if (!ret) {
+               IWL_ERR(mvm, "offset %d not used in fw key table.\n",
+                       keyconf->hw_key_idx);
+               return -ENOENT;
+       }
+
+       if (sta_id == IWL_INVALID_STATION) {
+               IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n");
+               return 0;
+       }
+
+       /*
+        * It is possible that the 'sta' parameter is NULL, and thus
+        * there is a need to retrieve the sta from the local station table,
+        * for example when a GTK is removed (where the sta_id will then be
+        * the AP ID, and no station was passed by mac80211.)
+        */
+       if (!sta) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                               lockdep_is_held(&mvm->mutex));
+               if (!sta) {
+                       IWL_ERR(mvm, "Invalid station id\n");
+                       return -EINVAL;
+               }
+       }
+
+       mvm_sta = (struct iwl_mvm_sta *)sta->drv_priv;
+       if (WARN_ON_ONCE(mvm_sta->vif != vif))
+               return -EINVAL;
+
+       key_flags = cpu_to_le16(keyconf->keyidx & STA_KEY_FLG_KEYID_MSK);
+       key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
+       key_flags |= cpu_to_le16(STA_KEY_NOT_VALID);
+
+       if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+               key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
+
+       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+       cmd.key.key_flags = key_flags;
+       cmd.key.key_offset = keyconf->hw_key_idx;
+       cmd.sta_id = sta_id;
+
+       cmd.modify_mask = STA_MODIFY_KEY;
+       cmd.add_modify = STA_MODE_MODIFY;
+
+       status = ADD_STA_SUCCESS;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n");
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n");
+               break;
+       }
+
+       return ret;
+}
+
+void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_key_conf *keyconf,
+                            struct ieee80211_sta *sta, u32 iv32,
+                            u16 *phase1key)
+{
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta);
+
+       if (sta_id == IWL_INVALID_STATION)
+               return;
+
+       iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
+                            iv32, phase1key, CMD_ASYNC);
+}
+
+void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, int sta_id)
+{
+       struct iwl_mvm_add_sta_cmd cmd = {
+               .add_modify = STA_MODE_MODIFY,
+               .sta_id = sta_id,
+               .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
+               .sleep_state_flags = cpu_to_le16(STA_SLEEP_STATE_AWAKE),
+       };
+       int ret;
+
+       /*
+        * Same modify mask for sleep_tx_count and sleep_state_flags but this
+        * should be fine since if we set the STA as "awake", then
+        * sleep_tx_count is not relevant.
+        */
+       ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
+}
+
+void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, int sta_id,
+                                      enum ieee80211_frame_release_type reason,
+                                      u16 cnt)
+{
+       u16 sleep_state_flags =
+               (reason == IEEE80211_FRAME_RELEASE_UAPSD) ?
+                       STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL;
+       struct iwl_mvm_add_sta_cmd cmd = {
+               .add_modify = STA_MODE_MODIFY,
+               .sta_id = sta_id,
+               .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
+               .sleep_tx_count = cpu_to_le16(cnt),
+               /*
+                * Same modify mask for sleep_tx_count and sleep_state_flags so
+                * we must set the sleep_state_flags too.
+                */
+               .sleep_state_flags = cpu_to_le16(sleep_state_flags),
+       };
+       int ret;
+
+       /* TODO: somehow the fw doesn't seem to take PS_POLL into account */
+       ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
new file mode 100644 (file)
index 0000000..1bf3010
--- /dev/null
@@ -0,0 +1,368 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __sta_h__
+#define __sta_h__
+
+#include <linux/spinlock.h>
+#include <net/mac80211.h>
+#include <linux/wait.h>
+
+#include "iwl-trans.h" /* for IWL_MAX_TID_COUNT */
+#include "fw-api.h" /* IWL_MVM_STATION_COUNT */
+#include "rs.h"
+
+struct iwl_mvm;
+
+/**
+ * DOC: station table - introduction
+ *
+ * The station table is a list of data structure that reprensent the stations.
+ * In STA/P2P client mode, the driver will hold one station for the AP/ GO.
+ * In GO/AP mode, the driver will have as many stations as associated clients.
+ * All these stations are reflected in the fw's station table. The driver
+ * keeps the fw's station table up to date with the ADD_STA command. Stations
+ * can be removed by the REMOVE_STA command.
+ *
+ * All the data related to a station is held in the structure %iwl_mvm_sta
+ * which is embed in the mac80211's %ieee80211_sta (in the drv_priv) area.
+ * This data includes the index of the station in the fw, per tid information
+ * (sequence numbers, Block-ack state machine, etc...). The stations are
+ * created and deleted by the %sta_state callback from %ieee80211_ops.
+ *
+ * The driver holds a map: %fw_id_to_mac_id that allows to fetch a
+ * %ieee80211_sta (and the %iwl_mvm_sta embedded into it) based on a fw
+ * station index. That way, the driver is able to get the tid related data in
+ * O(1) in time sensitive paths (Tx / Tx response / BA notification). These
+ * paths are triggered by the fw, and the driver needs to get a pointer to the
+ * %ieee80211 structure. This map helps to get that pointer quickly.
+ */
+
+/**
+ * DOC: station table - locking
+ *
+ * As stated before, the station is created / deleted by mac80211's %sta_state
+ * callback from %ieee80211_ops which can sleep. The next paragraph explains
+ * the locking of a single stations, the next ones relates to the station
+ * table.
+ *
+ * The station holds the sequence number per tid. So this data needs to be
+ * accessed in the Tx path (which is softIRQ). It also holds the Block-Ack
+ * information (the state machine / and the logic that checks if the queues
+ * were drained), so it also needs to be accessible from the Tx response flow.
+ * In short, the station needs to be access from sleepable context as well as
+ * from tasklets, so the station itself needs a spinlock.
+ *
+ * The writers of %fw_id_to_mac_id map are serialized by the global mutex of
+ * the mvm op_mode. This is possible since %sta_state can sleep.
+ * The pointers in this map are RCU protected, hence we won't replace the
+ * station while we have Tx / Tx response / BA notification running.
+ *
+ * If a station is deleted while it still has packets in its A-MPDU queues,
+ * then the reclaim flow will notice that there is no station in the map for
+ * sta_id and it will dump the responses.
+ */
+
+/**
+ * DOC: station table - internal stations
+ *
+ * The FW needs a few internal stations that are not reflected in
+ * mac80211, such as broadcast station in AP / GO mode, or AUX sta for
+ * scanning and P2P device (during the GO negotiation).
+ * For these kind of stations we have %iwl_mvm_int_sta struct which holds the
+ * data relevant for them from both %iwl_mvm_sta and %ieee80211_sta.
+ * Usually the data for these stations is static, so no locking is required,
+ * and no TID data as this is also not needed.
+ * One thing to note, is that these stations have an ID in the fw, but not
+ * in mac80211. In order to "reserve" them a sta_id in %fw_id_to_mac_id
+ * we fill ERR_PTR(EINVAL) in this mapping and all other dereferencing of
+ * pointers from this mapping need to check that the value is not error
+ * or NULL.
+ *
+ * Currently there is only one auxiliary station for scanning, initialized
+ * on init.
+ */
+
+/**
+ * DOC: station table - AP Station in STA mode
+ *
+ * %iwl_mvm_vif includes the index of the AP station in the fw's STA table:
+ * %ap_sta_id. To get the point to the coresponsding %ieee80211_sta,
+ * &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove
+ * the AP station from the fw before setting the MAC context as unassociated.
+ * Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is
+ * removed by mac80211, but the station won't be removed in the fw until the
+ * VIF is set as unassociated. Then, %ap_sta_id will be invalidated.
+ */
+
+/**
+ * DOC: station table - Drain vs. Flush
+ *
+ * Flush means that all the frames in the SCD queue are dumped regardless the
+ * station to which they were sent. We do that when we disassociate and before
+ * we remove the STA of the AP. The flush can be done synchronously against the
+ * fw.
+ * Drain means that the fw will drop all the frames sent to a specific station.
+ * This is useful when a client (if we are IBSS / GO or AP) disassociates. In
+ * that case, we need to drain all the frames for that client from the AC queues
+ * that are shared with the other clients. Only then, we can remove the STA in
+ * the fw. In order to do so, we track the non-AMPDU packets for each station.
+ * If mac80211 removes a STA and if it still has non-AMPDU packets pending in
+ * the queues, we mark this station as %EBUSY in %fw_id_to_mac_id, and drop all
+ * the frames for this STA (%iwl_mvm_rm_sta). When the last frame is dropped
+ * (we know about it with its Tx response), we remove the station in fw and set
+ * it as %NULL in %fw_id_to_mac_id: this is the purpose of
+ * %iwl_mvm_sta_drained_wk.
+ */
+
+/**
+ * DOC: station table - fw restart
+ *
+ * When the fw asserts, or we have any other issue that requires to reset the
+ * driver, we require mac80211 to reconfigure the driver. Since the private
+ * data of the stations is embed in mac80211's %ieee80211_sta, that data will
+ * not be zeroed and needs to be reinitialized manually.
+ * %IWL_MVM_STATUS_IN_HW_RESTART is set during restart and that will hint us
+ * that we must not allocate a new sta_id but reuse the previous one. This
+ * means that the stations being re-added after the reset will have the same
+ * place in the fw as before the reset. We do need to zero the %fw_id_to_mac_id
+ * map, since the stations aren't in the fw any more. Internal stations that
+ * are not added by mac80211 will be re-added in the init flow that is called
+ * after the restart: mac80211 call's %iwl_mvm_mac_start which calls to
+ * %iwl_mvm_up.
+ */
+
+/**
+ * DOC: AP mode - PS
+ *
+ * When a station is asleep, the fw will set it as "asleep". All the
+ * non-aggregation frames to that station will be dropped by the fw
+ * (%TX_STATUS_FAIL_DEST_PS failure code).
+ * AMPDUs are in a separate queue that is stopped by the fw. We just need to
+ * let mac80211 know how many frames we have in these queues so that it can
+ * properly handle trigger frames.
+ * When the a trigger frame is received, mac80211 tells the driver to send
+ * frames from the AMPDU queues or AC queue depending on which queue are
+ * delivery-enabled and what TID has frames to transmit (Note that mac80211 has
+ * all the knowledege since all the non-agg frames are buffered / filtered, and
+ * the driver tells mac80211 about agg frames). The driver needs to tell the fw
+ * to let frames out even if the station is asleep. This is done by
+ * %iwl_mvm_sta_modify_sleep_tx_count.
+ * When we receive a frame from that station with PM bit unset, the
+ * driver needs to let the fw know that this station isn't alseep any more.
+ * This is done by %iwl_mvm_sta_modify_ps_wake.
+ *
+ * TODO - EOSP handling
+ */
+
+/**
+ * enum iwl_mvm_agg_state
+ *
+ * The state machine of the BA agreement establishment / tear down.
+ * These states relate to a specific RA / TID.
+ *
+ * @IWL_AGG_OFF: aggregation is not used
+ * @IWL_AGG_STARTING: aggregation are starting (between start and oper)
+ * @IWL_AGG_ON: aggregation session is up
+ * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the
+ *     HW queue to be empty from packets for this RA /TID.
+ * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the
+ *     HW queue to be empty from packets for this RA /TID.
+ */
+enum iwl_mvm_agg_state {
+       IWL_AGG_OFF = 0,
+       IWL_AGG_STARTING,
+       IWL_AGG_ON,
+       IWL_EMPTYING_HW_QUEUE_ADDBA,
+       IWL_EMPTYING_HW_QUEUE_DELBA,
+};
+
+/**
+ * struct iwl_mvm_tid_data - holds the states for each RA / TID
+ * @seq_number: the next WiFi sequence number to use
+ * @next_reclaimed: the WiFi sequence number of the next packet to be acked.
+ *     This is basically (last acked packet++).
+ * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the
+ *     Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
+ * @state: state of the BA agreement establishment / tear down.
+ * @txq_id: Tx queue used by the BA session
+ * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or
+ *     the first packet to be sent in legacy HW queue in Tx AGG stop flow.
+ *     Basically when next_reclaimed reaches ssn, we can tell mac80211 that
+ *     we are ready to finish the Tx AGG stop / start flow.
+ * @wait_for_ba: Expect block-ack before next Tx reply
+ */
+struct iwl_mvm_tid_data {
+       u16 seq_number;
+       u16 next_reclaimed;
+       /* The rest is Tx AGG related */
+       u32 rate_n_flags;
+       enum iwl_mvm_agg_state state;
+       u16 txq_id;
+       u16 ssn;
+       bool wait_for_ba;
+};
+
+/**
+ * struct iwl_mvm_sta - representation of a station in the driver
+ * @sta_id: the index of the station in the fw (will be replaced by id_n_color)
+ * @tfd_queue_msk: the tfd queues used by the station
+ * @mac_id_n_color: the MAC context this station is linked to
+ * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
+ *     tid.
+ * @max_agg_bufsize: the maximal size of the AGG buffer for this station
+ * @lock: lock to protect the whole struct. Since %tid_data is access from Tx
+ * and from Tx response flow, it needs a spinlock.
+ * @pending_frames: number of frames for this STA on the shared Tx queues.
+ * @tid_data: per tid data. Look at %iwl_mvm_tid_data.
+ *
+ * When mac80211 creates a station it reserves some space (hw->sta_data_size)
+ * in the structure for use by driver. This structure is placed in that
+ * space.
+ *
+ */
+struct iwl_mvm_sta {
+       u32 sta_id;
+       u32 tfd_queue_msk;
+       u32 mac_id_n_color;
+       u16 tid_disable_agg;
+       u8 max_agg_bufsize;
+       spinlock_t lock;
+       atomic_t pending_frames;
+       struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT];
+       struct iwl_lq_sta lq_sta;
+       struct ieee80211_vif *vif;
+
+#ifdef CONFIG_PM_SLEEP
+       u16 last_seq_ctl;
+#endif
+};
+
+/**
+ * struct iwl_mvm_int_sta - representation of an internal station (auxiliary or
+ * broadcast)
+ * @sta_id: the index of the station in the fw (will be replaced by id_n_color)
+ * @tfd_queue_msk: the tfd queues used by the station
+ */
+struct iwl_mvm_int_sta {
+       u32 sta_id;
+       u32 tfd_queue_msk;
+};
+
+int iwl_mvm_sta_add_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta);
+int iwl_mvm_add_sta(struct iwl_mvm *mvm,
+                   struct ieee80211_vif *vif,
+                   struct ieee80211_sta *sta);
+int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
+                  struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta);
+int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
+                     struct ieee80211_vif *vif,
+                     u8 sta_id);
+int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
+                       struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta,
+                       struct ieee80211_key_conf *key,
+                       bool have_key_offset);
+int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
+                          struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta,
+                          struct ieee80211_key_conf *keyconf);
+
+void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_key_conf *keyconf,
+                            struct ieee80211_sta *sta, u32 iv32,
+                            u16 *phase1key);
+
+/* AMPDU */
+int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                      int tid, u16 ssn, bool start);
+int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta, u16 tid, u8 buf_size);
+int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta, u16 tid);
+
+int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm);
+int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
+                            u32 qmask);
+void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm,
+                            struct iwl_mvm_int_sta *sta);
+int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                          struct iwl_mvm_int_sta *bsta);
+int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm,
+                             struct iwl_mvm_int_sta *bsta);
+int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         struct iwl_mvm_int_sta *bsta);
+int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *bsta);
+void iwl_mvm_sta_drained_wk(struct work_struct *wk);
+void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, int sta_id);
+void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, int sta_id,
+                                      enum ieee80211_frame_release_type reason,
+                                      u16 cnt);
+int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+                     bool drain);
+
+#endif /* __sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
new file mode 100644 (file)
index 0000000..b9f076f
--- /dev/null
@@ -0,0 +1,569 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/jiffies.h>
+#include <net/mac80211.h>
+
+#include "iwl-notif-wait.h"
+#include "iwl-trans.h"
+#include "fw-api.h"
+#include "time-event.h"
+#include "mvm.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
+
+/* A TimeUnit is 1024 microsecond */
+#define TU_TO_JIFFIES(_tu)     (usecs_to_jiffies((_tu) * 1024))
+#define MSEC_TO_TU(_msec)      (_msec*1000/1024)
+
+void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
+                          struct iwl_mvm_time_event_data *te_data)
+{
+       lockdep_assert_held(&mvm->time_event_lock);
+
+       if (te_data->id == TE_MAX)
+               return;
+
+       list_del(&te_data->list);
+       te_data->running = false;
+       te_data->uid = 0;
+       te_data->id = TE_MAX;
+       te_data->vif = NULL;
+}
+
+void iwl_mvm_roc_done_wk(struct work_struct *wk)
+{
+       struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
+
+       synchronize_net();
+
+       /*
+        * Flush the offchannel queue -- this is called when the time
+        * event finishes or is cancelled, so that frames queued for it
+        * won't get stuck on the queue and be transmitted in the next
+        * time event.
+        * We have to send the command asynchronously since this cannot
+        * be under the mutex for locking reasons, but that's not an
+        * issue as it will have to complete before the next command is
+        * executed, and a new time event means a new command.
+        */
+       iwl_mvm_flush_tx_path(mvm, BIT(IWL_OFFCHANNEL_QUEUE), false);
+}
+
+static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
+{
+       /*
+        * First, clear the ROC_RUNNING status bit. This will cause the TX
+        * path to drop offchannel transmissions. That would also be done
+        * by mac80211, but it is racy, in particular in the case that the
+        * time event actually completed in the firmware (which is handled
+        * in iwl_mvm_te_handle_notif).
+        */
+       clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
+
+       /*
+        * Of course, our status bit is just as racy as mac80211, so in
+        * addition, fire off the work struct which will drop all frames
+        * from the hardware queues that made it through the race. First
+        * it will of course synchronize the TX path to make sure that
+        * any *new* TX will be rejected.
+        */
+       schedule_work(&mvm->roc_done_wk);
+}
+
+/*
+ * Handles a FW notification for an event that is known to the driver.
+ *
+ * @mvm: the mvm component
+ * @te_data: the time event data
+ * @notif: the notification data corresponding the time event data.
+ */
+static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
+                                   struct iwl_mvm_time_event_data *te_data,
+                                   struct iwl_time_event_notif *notif)
+{
+       lockdep_assert_held(&mvm->time_event_lock);
+
+       IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n",
+                    le32_to_cpu(notif->unique_id),
+                    le32_to_cpu(notif->action));
+
+       /*
+        * The FW sends the start/end time event notifications even for events
+        * that it fails to schedule. This is indicated in the status field of
+        * the notification. This happens in cases that the scheduler cannot
+        * find a schedule that can handle the event (for example requesting a
+        * P2P Device discoveribility, while there are other higher priority
+        * events in the system).
+        */
+       WARN_ONCE(!le32_to_cpu(notif->status),
+                 "Failed to schedule time event\n");
+
+       if (le32_to_cpu(notif->action) == TE_NOTIF_HOST_END) {
+               IWL_DEBUG_TE(mvm,
+                            "TE ended - current time %lu, estimated end %lu\n",
+                            jiffies, te_data->end_jiffies);
+
+               if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+                       ieee80211_remain_on_channel_expired(mvm->hw);
+                       iwl_mvm_roc_finished(mvm);
+               }
+
+               /*
+                * By now, we should have finished association
+                * and know the dtim period.
+                */
+               if (te_data->vif->type == NL80211_IFTYPE_STATION &&
+                   (!te_data->vif->bss_conf.assoc ||
+                    !te_data->vif->bss_conf.dtim_period))
+                       IWL_ERR(mvm,
+                               "No assocation and the time event is over already...\n");
+
+               iwl_mvm_te_clear_data(mvm, te_data);
+       } else if (le32_to_cpu(notif->action) == TE_NOTIF_HOST_START) {
+               te_data->running = true;
+               te_data->end_jiffies = jiffies +
+                       TU_TO_JIFFIES(te_data->duration);
+
+               if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+                       set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
+                       ieee80211_ready_on_channel(mvm->hw);
+               }
+       } else {
+               IWL_WARN(mvm, "Got TE with unknown action\n");
+       }
+}
+
+/*
+ * The Rx handler for time event notifications
+ */
+int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
+                               struct iwl_rx_cmd_buffer *rxb,
+                               struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_time_event_notif *notif = (void *)pkt->data;
+       struct iwl_mvm_time_event_data *te_data, *tmp;
+
+       IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n",
+                    le32_to_cpu(notif->unique_id),
+                    le32_to_cpu(notif->action));
+
+       spin_lock_bh(&mvm->time_event_lock);
+       list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) {
+               if (le32_to_cpu(notif->unique_id) == te_data->uid)
+                       iwl_mvm_te_handle_notif(mvm, te_data, notif);
+       }
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       return 0;
+}
+
+static bool iwl_mvm_time_event_notif(struct iwl_notif_wait_data *notif_wait,
+                                    struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_mvm *mvm =
+               container_of(notif_wait, struct iwl_mvm, notif_wait);
+       struct iwl_mvm_time_event_data *te_data = data;
+       struct ieee80211_vif *vif = te_data->vif;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_time_event_notif *notif;
+       struct iwl_time_event_resp *resp;
+
+       u32 mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color);
+
+       /* until we do something else */
+       WARN_ON(te_data->id != TE_BSS_STA_AGGRESSIVE_ASSOC);
+
+       switch (pkt->hdr.cmd) {
+       case TIME_EVENT_CMD:
+               resp = (void *)pkt->data;
+               /* TODO: I can't check that since the fw is buggy - it doesn't
+                * put the right values when we remove a TE. We can be here
+                * when we remove a TE because the remove TE command is sent in
+                * ASYNC...
+                * WARN_ON(mac_id_n_color != le32_to_cpu(resp->id_and_color));
+                */
+               te_data->uid = le32_to_cpu(resp->unique_id);
+               IWL_DEBUG_TE(mvm, "Got response - UID = 0x%x\n", te_data->uid);
+               return false;
+
+       case TIME_EVENT_NOTIFICATION:
+               notif = (void *)pkt->data;
+               WARN_ON(le32_to_cpu(notif->status) != 1);
+               WARN_ON(mac_id_n_color != le32_to_cpu(notif->id_and_color));
+               /* check if this is our Time Event that is starting */
+               if (le32_to_cpu(notif->unique_id) != te_data->uid)
+                       return false;
+               IWL_DEBUG_TE(mvm, "Event %d is starting - time is %d\n",
+                            te_data->uid, le32_to_cpu(notif->timestamp));
+
+               WARN_ONCE(!le32_to_cpu(notif->status),
+                         "Failed to schedule protected session TE\n");
+
+               te_data->running = true;
+               te_data->end_jiffies = jiffies +
+                                      TU_TO_JIFFIES(te_data->duration);
+               return true;
+
+       default:
+               WARN_ON(1);
+               return false;
+       };
+}
+
+void iwl_mvm_protect_session(struct iwl_mvm *mvm,
+                            struct ieee80211_vif *vif,
+                            u32 duration, u32 min_duration)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+       static const u8 time_event_notif[] = { TIME_EVENT_CMD,
+                                              TIME_EVENT_NOTIFICATION };
+       struct iwl_notification_wait wait_time_event;
+       struct iwl_time_event_cmd time_cmd = {};
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (te_data->running &&
+           time_after(te_data->end_jiffies,
+                      jiffies + TU_TO_JIFFIES(min_duration))) {
+               IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
+                            jiffies_to_msecs(te_data->end_jiffies - jiffies));
+               return;
+       }
+
+       if (te_data->running) {
+               IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n",
+                            te_data->uid,
+                            jiffies_to_msecs(te_data->end_jiffies - jiffies));
+               /*
+                * we don't have enough time
+                * cancel the current TE and issue a new one
+                * Of course it would be better to remove the old one only
+                * when the new one is added, but we don't care if we are off
+                * channel for a bit. All we need to do, is not to return
+                * before we actually begin to be on the channel.
+                */
+               iwl_mvm_stop_session_protection(mvm, vif);
+       }
+
+       iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
+                                  time_event_notif,
+                                  ARRAY_SIZE(time_event_notif),
+                                  iwl_mvm_time_event_notif,
+                                  &mvmvif->time_event_data);
+
+       time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+       time_cmd.id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+       time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC);
+
+       time_cmd.apply_time =
+               cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG));
+       time_cmd.dep_policy = TE_INDEPENDENT;
+       time_cmd.is_present = cpu_to_le32(1);
+       time_cmd.max_frags = cpu_to_le32(TE_FRAG_NONE);
+       time_cmd.max_delay = cpu_to_le32(500);
+       /* TODO: why do we need to interval = bi if it is not periodic? */
+       time_cmd.interval = cpu_to_le32(1);
+       time_cmd.interval_reciprocal = cpu_to_le32(iwl_mvm_reciprocal(1));
+       time_cmd.duration = cpu_to_le32(duration);
+       time_cmd.repeat = cpu_to_le32(1);
+       time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_START | TE_NOTIF_HOST_END);
+
+       te_data->vif = vif;
+       te_data->duration = duration;
+
+       spin_lock_bh(&mvm->time_event_lock);
+       te_data->id = le32_to_cpu(time_cmd.id);
+       list_add_tail(&te_data->list, &mvm->time_event_list);
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
+                                  sizeof(time_cmd), &time_cmd);
+       if (ret) {
+               IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
+               goto out_remove_notif;
+       }
+
+       ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1 * HZ);
+       if (ret) {
+               IWL_ERR(mvm, "%s - failed on timeout\n", __func__);
+               spin_lock_bh(&mvm->time_event_lock);
+               iwl_mvm_te_clear_data(mvm, te_data);
+               spin_unlock_bh(&mvm->time_event_lock);
+       }
+
+       return;
+
+out_remove_notif:
+       iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
+}
+
+/*
+ * Explicit request to remove a time event. The removal of a time event needs to
+ * be synchronized with the flow of a time event's end notification, which also
+ * removes the time event from the op mode data structures.
+ */
+void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+                              struct iwl_mvm_vif *mvmvif,
+                              struct iwl_mvm_time_event_data *te_data)
+{
+       struct iwl_time_event_cmd time_cmd = {};
+       u32 id, uid;
+       int ret;
+
+       /*
+        * It is possible that by the time we got to this point the time
+        * event was already removed.
+        */
+       spin_lock_bh(&mvm->time_event_lock);
+
+       /* Save time event uid before clearing its data */
+       uid = te_data->uid;
+       id = te_data->id;
+
+       /*
+        * The clear_data function handles time events that were already removed
+        */
+       iwl_mvm_te_clear_data(mvm, te_data);
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       /*
+        * It is possible that by the time we try to remove it, the time event
+        * has already ended and removed. In such a case there is no need to
+        * send a removal command.
+        */
+       if (id == TE_MAX) {
+               IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", uid);
+               return;
+       }
+
+       /* When we remove a TE, the UID is to be set in the id field */
+       time_cmd.id = cpu_to_le32(uid);
+       time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
+       time_cmd.id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+
+       IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_ASYNC,
+                                  sizeof(time_cmd), &time_cmd);
+       if (WARN_ON(ret))
+               return;
+}
+
+void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+
+       lockdep_assert_held(&mvm->mutex);
+       iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+}
+
+static bool iwl_mvm_roc_te_notif(struct iwl_notif_wait_data *notif_wait,
+                                struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_mvm *mvm =
+               container_of(notif_wait, struct iwl_mvm, notif_wait);
+       struct iwl_mvm_time_event_data *te_data = data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+       struct iwl_time_event_resp *resp;
+
+       u32 mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color);
+
+       /* until we do something else */
+       WARN_ON(te_data->id != TE_P2P_DEVICE_DISCOVERABLE);
+
+       switch (pkt->hdr.cmd) {
+       case TIME_EVENT_CMD:
+               resp = (void *)pkt->data;
+               WARN_ON(mac_id_n_color != le32_to_cpu(resp->id_and_color));
+               te_data->uid = le32_to_cpu(resp->unique_id);
+               IWL_DEBUG_TE(mvm, "Got response - UID = 0x%x\n", te_data->uid);
+               return true;
+
+       default:
+               WARN_ON(1);
+               return false;
+       };
+}
+
+int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         int duration)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+       static const u8 roc_te_notif[] = { TIME_EVENT_CMD };
+       struct iwl_notification_wait wait_time_event;
+       struct iwl_time_event_cmd time_cmd = {};
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+       if (te_data->running) {
+               IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");
+               return -EBUSY;
+       }
+
+       /*
+        * Flush the done work, just in case it's still pending, so that
+        * the work it does can complete and we can accept new frames.
+        */
+       flush_work(&mvm->roc_done_wk);
+
+       iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
+                                  roc_te_notif,
+                                  ARRAY_SIZE(roc_te_notif),
+                                  iwl_mvm_roc_te_notif,
+                                  &mvmvif->time_event_data);
+
+       time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+       time_cmd.id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+       time_cmd.id = cpu_to_le32(TE_P2P_DEVICE_DISCOVERABLE);
+
+       time_cmd.apply_time = cpu_to_le32(0);
+       time_cmd.dep_policy = cpu_to_le32(TE_INDEPENDENT);
+       time_cmd.is_present = cpu_to_le32(1);
+
+       time_cmd.interval = cpu_to_le32(1);
+
+       /*
+        * TE_P2P_DEVICE_DISCOVERABLE can have lower priority than other events
+        * that are being scheduled by the driver/fw, and thus it might not be
+        * scheduled. To improve the chances of it being scheduled, allow it to
+        * be fragmented.
+        * In addition, for the same reasons, allow to delay the scheduling of
+        * the time event.
+        */
+       time_cmd.max_frags = cpu_to_le32(MSEC_TO_TU(duration)/20);
+       time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
+       time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
+       time_cmd.repeat = cpu_to_le32(1);
+       time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_START | TE_NOTIF_HOST_END);
+
+       /* Push the te data to the tracked te list */
+       te_data->vif = vif;
+       te_data->duration = MSEC_TO_TU(duration);
+
+       spin_lock_bh(&mvm->time_event_lock);
+       te_data->id = le32_to_cpu(time_cmd.id);
+       list_add_tail(&te_data->list, &mvm->time_event_list);
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
+                                  sizeof(time_cmd), &time_cmd);
+       if (ret) {
+               IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
+               goto out_remove_notif;
+       }
+
+       ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1 * HZ);
+       if (ret) {
+               IWL_ERR(mvm, "%s - failed on timeout\n", __func__);
+               iwl_mvm_te_clear_data(mvm, te_data);
+       }
+
+       return ret;
+
+out_remove_notif:
+       iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
+       return ret;
+}
+
+void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)
+{
+       struct iwl_mvm_vif *mvmvif;
+       struct iwl_mvm_time_event_data *te_data;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /*
+        * Iterate over the list of time events and find the time event that is
+        * associated with a P2P_DEVICE interface.
+        * This assumes that a P2P_DEVICE interface can have only a single time
+        * event at any given time and this time event coresponds to a ROC
+        * request
+        */
+       mvmvif = NULL;
+       spin_lock_bh(&mvm->time_event_lock);
+       list_for_each_entry(te_data, &mvm->time_event_list, list) {
+               if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+                       mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+                       break;
+               }
+       }
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       if (!mvmvif) {
+               IWL_WARN(mvm, "P2P_DEVICE no remain on channel event\n");
+               return;
+       }
+
+       iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+
+       iwl_mvm_roc_finished(mvm);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h
new file mode 100644 (file)
index 0000000..64fb57a
--- /dev/null
@@ -0,0 +1,214 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __time_event_h__
+#define __time_event_h__
+
+#include "fw-api.h"
+
+#include "mvm.h"
+
+/**
+ * DOC: Time Events - what is it?
+ *
+ * Time Events are a fw feature that allows the driver to control the presence
+ * of the device on the channel. Since the fw supports multiple channels
+ * concurrently, the fw may choose to jump to another channel at any time.
+ * In order to make sure that the fw is on a specific channel at a certain time
+ * and for a certain duration, the driver needs to issue a time event.
+ *
+ * The simplest example is for BSS association. The driver issues a time event,
+ * waits for it to start, and only then tells mac80211 that we can start the
+ * association. This way, we make sure that the association will be done
+ * smoothly and won't be interrupted by channel switch decided within the fw.
+ */
+
+ /**
+ * DOC: The flow against the fw
+ *
+ * When the driver needs to make sure we are in a certain channel, at a certain
+ * time and for a certain duration, it sends a Time Event. The flow against the
+ * fw goes like this:
+ *     1) Driver sends a TIME_EVENT_CMD to the fw
+ *     2) Driver gets the response for that command. This response contains the
+ *        Unique ID (UID) of the event.
+ *     3) The fw sends notification when the event starts.
+ *
+ * Of course the API provides various options that allow to cover parameters
+ * of the flow.
+ *     What is the duration of the event?
+ *     What is the start time of the event?
+ *     Is there an end-time for the event?
+ *     How much can the event be delayed?
+ *     Can the event be split?
+ *     If yes what is the maximal number of chunks?
+ *     etc...
+ */
+
+/**
+ * DOC: Abstraction to the driver
+ *
+ * In order to simplify the use of time events to the rest of the driver,
+ * we abstract the use of time events. This component provides the functions
+ * needed by the driver.
+ */
+
+#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500
+#define IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400
+
+/**
+ * iwl_mvm_protect_session - start / extend the session protection.
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the session is issued
+ * @duration: the duration of the session in TU.
+ * @min_duration: will start a new session if the current session will end
+ *     in less than min_duration.
+ *
+ * This function can be used to start a session protection which means that the
+ * fw will stay on the channel for %duration_ms milliseconds. This function
+ * will block (sleep) until the session starts. This function can also be used
+ * to extend a currently running session.
+ * This function is meant to be used for BSS association for example, where we
+ * want to make sure that the fw stays on the channel during the association.
+ */
+void iwl_mvm_protect_session(struct iwl_mvm *mvm,
+                            struct ieee80211_vif *vif,
+                            u32 duration, u32 min_duration);
+
+/**
+ * iwl_mvm_stop_session_protection - cancel the session protection.
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the session is issued
+ *
+ * This functions cancels the session protection which is an act of good
+ * citizenship. If it is not needed any more it should be cancelled because
+ * the other bindings wait for the medium during that time.
+ * This funtions doesn't sleep.
+ */
+void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
+                                     struct ieee80211_vif *vif);
+
+/*
+ * iwl_mvm_rx_time_event_notif - handles %TIME_EVENT_NOTIFICATION.
+ */
+int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
+                               struct iwl_rx_cmd_buffer *rxb,
+                               struct iwl_device_cmd *cmd);
+
+/**
+ * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionlity
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the roc is requested. It is assumed
+ * that the vif type is NL80211_IFTYPE_P2P_DEVICE
+ * @duration: the requested duration in millisecond for the fw to be on the
+ * channel that is bound to the vif.
+ *
+ * This function can be used to issue a remain on channel session,
+ * which means that the fw will stay in the channel for the request %duration
+ * milliseconds. The function is async, meaning that it only issues the ROC
+ * request but does not wait for it to start. Once the FW is ready to serve the
+ * ROC request, it will issue a notification to the driver that it is on the
+ * requested channel. Once the FW completes the ROC request it will issue
+ * another notification to the driver.
+ */
+int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         int duration);
+
+/**
+ * iwl_mvm_stop_p2p_roc - stop remain on channel for p2p device functionlity
+ * @mvm: the mvm component
+ *
+ * This function can be used to cancel an ongoing ROC session.
+ * The function is async, it will instruct the FW to stop serving the ROC
+ * session, but will not wait for the actual stopping of the session.
+ */
+void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm);
+
+/**
+ * iwl_mvm_remove_time_event - general function to clean up of time event
+ * @mvm: the mvm component
+ * @vif: the vif to which the time event belongs
+ * @te_data: the time event data that corresponds to that time event
+ *
+ * This function can be used to cancel a time event regardless its type.
+ * It is useful for cleaning up time events running before removing an
+ * interface.
+ */
+void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+                              struct iwl_mvm_vif *mvmvif,
+                              struct iwl_mvm_time_event_data *te_data);
+
+/**
+ * iwl_mvm_te_clear_data - remove time event from list
+ * @mvm: the mvm component
+ * @te_data: the time event data to remove
+ *
+ * This function is mostly internal, it is made available here only
+ * for firmware restart purposes.
+ */
+void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
+                          struct iwl_mvm_time_event_data *te_data);
+
+void iwl_mvm_roc_done_wk(struct work_struct *wk);
+
+#endif /* __time_event_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
new file mode 100644 (file)
index 0000000..cada8ef
--- /dev/null
@@ -0,0 +1,916 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/ieee80211.h>
+#include <linux/etherdevice.h>
+
+#include "iwl-trans.h"
+#include "iwl-eeprom-parse.h"
+#include "mvm.h"
+#include "sta.h"
+
+/*
+ * Sets most of the Tx cmd's fields
+ */
+static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
+                              struct iwl_tx_cmd *tx_cmd,
+                              struct ieee80211_tx_info *info, u8 sta_id)
+{
+       struct ieee80211_hdr *hdr = (void *)skb->data;
+       __le16 fc = hdr->frame_control;
+       u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags);
+       u32 len = skb->len + FCS_LEN;
+
+       if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
+               tx_flags |= TX_CMD_FLG_ACK;
+       else
+               tx_flags &= ~TX_CMD_FLG_ACK;
+
+       if (ieee80211_is_probe_resp(fc))
+               tx_flags |= TX_CMD_FLG_TSF;
+       else if (ieee80211_is_back_req(fc))
+               tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR;
+
+       /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */
+       if (info->band == IEEE80211_BAND_2GHZ        &&
+           (skb->protocol == cpu_to_be16(ETH_P_PAE)  ||
+            is_multicast_ether_addr(hdr->addr1)      ||
+            ieee80211_is_back_req(fc)                ||
+            ieee80211_is_mgmt(fc)))
+               tx_flags |= TX_CMD_FLG_BT_DIS;
+
+       if (ieee80211_has_morefrags(fc))
+               tx_flags |= TX_CMD_FLG_MORE_FRAG;
+
+       if (ieee80211_is_data_qos(fc)) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               tx_cmd->tid_tspec = qc[0] & 0xf;
+               tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
+       } else {
+               tx_cmd->tid_tspec = IWL_TID_NON_QOS;
+               if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
+                       tx_flags |= TX_CMD_FLG_SEQ_CTL;
+               else
+                       tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
+       }
+
+       if (ieee80211_is_mgmt(fc)) {
+               if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
+                       tx_cmd->pm_frame_timeout = cpu_to_le16(3);
+               else
+                       tx_cmd->pm_frame_timeout = cpu_to_le16(2);
+
+               /* The spec allows Action frames in A-MPDU, we don't support
+                * it
+                */
+               WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU);
+       } else {
+               tx_cmd->pm_frame_timeout = 0;
+       }
+
+       if (info->flags & IEEE80211_TX_CTL_AMPDU)
+               tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
+
+       if (ieee80211_is_data(fc) && len > mvm->rts_threshold &&
+           !is_multicast_ether_addr(ieee80211_get_DA(hdr)))
+               tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
+
+       tx_cmd->driver_txop = 0;
+       tx_cmd->tx_flags = cpu_to_le32(tx_flags);
+       /* Total # bytes to be transmitted */
+       tx_cmd->len = cpu_to_le16((u16)skb->len);
+       tx_cmd->next_frame_len = 0;
+       tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
+       tx_cmd->sta_id = sta_id;
+}
+
+/*
+ * Sets the fields in the Tx cmd that are rate related
+ */
+static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
+                                   struct iwl_tx_cmd *tx_cmd,
+                                   struct ieee80211_tx_info *info,
+                                   struct ieee80211_sta *sta,
+                                   __le16 fc)
+{
+       u32 rate_flags;
+       int rate_idx;
+       u8 rate_plcp;
+
+       /* Set retry limit on RTS packets */
+       tx_cmd->rts_retry_limit = IWL_RTS_DFAULT_RETRY_LIMIT;
+
+       /* Set retry limit on DATA packets and Probe Responses*/
+       if (ieee80211_is_probe_resp(fc)) {
+               tx_cmd->data_retry_limit = IWL_MGMT_DFAULT_RETRY_LIMIT;
+               tx_cmd->rts_retry_limit =
+                       min(tx_cmd->data_retry_limit, tx_cmd->rts_retry_limit);
+       } else if (ieee80211_is_back_req(fc)) {
+               tx_cmd->data_retry_limit = IWL_BAR_DFAULT_RETRY_LIMIT;
+       } else {
+               tx_cmd->data_retry_limit = IWL_DEFAULT_TX_RETRY;
+       }
+
+       /*
+        * for data packets, rate info comes from the table inside he fw. This
+        * table is controlled by LINK_QUALITY commands
+        */
+
+       if (ieee80211_is_data(fc)) {
+               tx_cmd->initial_rate_index = 0;
+               tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
+               return;
+       } else if (ieee80211_is_back_req(fc)) {
+               tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
+       }
+
+       /* HT rate doesn't make sense for a non data frame */
+       WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS,
+                 "Got an HT rate for a non data frame 0x%x\n",
+                 info->control.rates[0].flags);
+
+       rate_idx = info->control.rates[0].idx;
+       /* if the rate isn't a well known legacy rate, take the lowest one */
+       if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT_LEGACY)
+               rate_idx = rate_lowest_index(
+                               &mvm->nvm_data->bands[info->band], sta);
+
+       /* For 5 GHZ band, remap mac80211 rate indices into driver indices */
+       if (info->band == IEEE80211_BAND_5GHZ)
+               rate_idx += IWL_FIRST_OFDM_RATE;
+
+       /* For 2.4 GHZ band, check that there is no need to remap */
+       BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
+
+       /* Get PLCP rate for tx_cmd->rate_n_flags */
+       rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx);
+
+       mvm->mgmt_last_antenna_idx =
+               iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant,
+                                    mvm->mgmt_last_antenna_idx);
+       rate_flags = BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
+
+       /* Set CCK flag as needed */
+       if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
+               rate_flags |= RATE_MCS_CCK_MSK;
+
+       /* Set the rate in the TX cmd */
+       tx_cmd->rate_n_flags = cpu_to_le32((u32)rate_plcp | rate_flags);
+}
+
+/*
+ * Sets the fields in the Tx cmd that are crypto related
+ */
+static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
+                                     struct ieee80211_tx_info *info,
+                                     struct iwl_tx_cmd *tx_cmd,
+                                     struct sk_buff *skb_frag)
+{
+       struct ieee80211_key_conf *keyconf = info->control.hw_key;
+
+       switch (keyconf->cipher) {
+       case WLAN_CIPHER_SUITE_CCMP:
+               tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
+               memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
+               if (info->flags & IEEE80211_TX_CTL_AMPDU)
+                       tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_CCMP_AGG);
+               break;
+
+       case WLAN_CIPHER_SUITE_TKIP:
+               tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
+               ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
+               break;
+
+       case WLAN_CIPHER_SUITE_WEP104:
+               tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
+               /* fall through */
+       case WLAN_CIPHER_SUITE_WEP40:
+               tx_cmd->sec_ctl |= TX_CMD_SEC_WEP |
+                       ((keyconf->keyidx << TX_CMD_SEC_WEP_KEY_IDX_POS) &
+                         TX_CMD_SEC_WEP_KEY_IDX_MSK);
+
+               memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
+               break;
+       default:
+               IWL_ERR(mvm, "Unknown encode cipher %x\n", keyconf->cipher);
+               break;
+       }
+}
+
+/*
+ * Allocates and sets the Tx cmd the driver data pointers in the skb
+ */
+static struct iwl_device_cmd *
+iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
+                     struct ieee80211_sta *sta, u8 sta_id)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct iwl_device_cmd *dev_cmd;
+       struct iwl_tx_cmd *tx_cmd;
+
+       dev_cmd = iwl_trans_alloc_tx_cmd(mvm->trans);
+
+       if (unlikely(!dev_cmd))
+               return NULL;
+
+       memset(dev_cmd, 0, sizeof(*dev_cmd));
+       tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+
+       if (info->control.hw_key)
+               iwl_mvm_set_tx_cmd_crypto(mvm, info, tx_cmd, skb);
+
+       iwl_mvm_set_tx_cmd(mvm, skb, tx_cmd, info, sta_id);
+
+       iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control);
+
+       memset(&info->status, 0, sizeof(info->status));
+
+       info->driver_data[0] = NULL;
+       info->driver_data[1] = dev_cmd;
+
+       return dev_cmd;
+}
+
+int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct iwl_device_cmd *dev_cmd;
+       struct iwl_tx_cmd *tx_cmd;
+       u8 sta_id;
+
+       if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU))
+               return -1;
+
+       if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM &&
+                        (!info->control.vif ||
+                         info->hw_queue != info->control.vif->cab_queue)))
+               return -1;
+
+       /*
+        * If the interface on which frame is sent is the P2P_DEVICE
+        * or an AP/GO interface use the broadcast station associated
+        * with it; otherwise use the AUX station.
+        */
+       if (info->control.vif &&
+           (info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE ||
+            info->control.vif->type == NL80211_IFTYPE_AP)) {
+               struct iwl_mvm_vif *mvmvif =
+                       iwl_mvm_vif_from_mac80211(info->control.vif);
+               sta_id = mvmvif->bcast_sta.sta_id;
+       } else {
+               sta_id = mvm->aux_sta.sta_id;
+       }
+
+       IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue);
+
+       dev_cmd = iwl_mvm_set_tx_params(mvm, skb, NULL, sta_id);
+       if (!dev_cmd)
+               return -1;
+
+       /* From now on, we cannot access info->control */
+       tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+
+       /* Copy MAC header from skb into command buffer */
+       memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(hdr->frame_control));
+
+       if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) {
+               iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * Sets the fields in the Tx cmd that are crypto related
+ */
+int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
+                  struct ieee80211_sta *sta)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct iwl_mvm_sta *mvmsta;
+       struct iwl_device_cmd *dev_cmd;
+       struct iwl_tx_cmd *tx_cmd;
+       __le16 fc;
+       u16 seq_number = 0;
+       u8 tid = IWL_MAX_TID_COUNT;
+       u8 txq_id = info->hw_queue;
+       bool is_data_qos = false, is_ampdu = false;
+
+       mvmsta = (void *)sta->drv_priv;
+       fc = hdr->frame_control;
+
+       if (WARN_ON_ONCE(!mvmsta))
+               return -1;
+
+       if (WARN_ON_ONCE(mvmsta->sta_id == IWL_INVALID_STATION))
+               return -1;
+
+       dev_cmd = iwl_mvm_set_tx_params(mvm, skb, sta, mvmsta->sta_id);
+       if (!dev_cmd)
+               goto drop;
+
+       tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+       /* From now on, we cannot access info->control */
+
+       spin_lock(&mvmsta->lock);
+
+       if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
+               u8 *qc = NULL;
+               qc = ieee80211_get_qos_ctl(hdr);
+               tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+               if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+                       goto drop_unlock_sta;
+
+               seq_number = mvmsta->tid_data[tid].seq_number;
+               seq_number &= IEEE80211_SCTL_SEQ;
+               hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+               hdr->seq_ctrl |= cpu_to_le16(seq_number);
+               seq_number += 0x10;
+               is_data_qos = true;
+               is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU;
+       }
+
+       /* Copy MAC header from skb into command buffer */
+       memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(fc));
+
+       WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
+
+       if (is_ampdu) {
+               if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON))
+                       goto drop_unlock_sta;
+               txq_id = mvmsta->tid_data[tid].txq_id;
+       }
+
+       IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id,
+                    tid, txq_id, seq_number);
+
+       /* NOTE: aggregation will need changes here (for txq id) */
+       if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id))
+               goto drop_unlock_sta;
+
+       if (is_data_qos && !ieee80211_has_morefrags(fc))
+               mvmsta->tid_data[tid].seq_number = seq_number;
+
+       spin_unlock(&mvmsta->lock);
+
+       if (mvmsta->vif->type == NL80211_IFTYPE_AP &&
+           txq_id < IWL_FIRST_AMPDU_QUEUE)
+               atomic_inc(&mvmsta->pending_frames);
+
+       return 0;
+
+drop_unlock_sta:
+       iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
+       spin_unlock(&mvmsta->lock);
+drop:
+       return -1;
+}
+
+static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
+                                     struct ieee80211_sta *sta, u8 tid)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+       struct ieee80211_vif *vif = mvmsta->vif;
+
+       lockdep_assert_held(&mvmsta->lock);
+
+       if (tid_data->ssn != tid_data->next_reclaimed)
+               return;
+
+       switch (tid_data->state) {
+       case IWL_EMPTYING_HW_QUEUE_ADDBA:
+               IWL_DEBUG_TX_QUEUES(mvm,
+                                   "Can continue addBA flow ssn = next_recl = %d\n",
+                                   tid_data->next_reclaimed);
+               tid_data->state = IWL_AGG_STARTING;
+               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+
+       case IWL_EMPTYING_HW_QUEUE_DELBA:
+               IWL_DEBUG_TX_QUEUES(mvm,
+                                   "Can continue DELBA flow ssn = next_recl = %d\n",
+                                   tid_data->next_reclaimed);
+               iwl_trans_txq_disable(mvm->trans, tid_data->txq_id);
+               tid_data->state = IWL_AGG_OFF;
+               /*
+                * we can't hold the mutex - but since we are after a sequence
+                * point (call to iwl_trans_txq_disable), so we don't even need
+                * a memory barrier.
+                */
+               mvm->queue_to_mac80211[tid_data->txq_id] =
+                                       IWL_INVALID_MAC80211_QUEUE;
+               ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+
+       default:
+               break;
+       }
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_mvm_get_tx_fail_reason(u32 status)
+{
+#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
+#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
+
+       switch (status & TX_STATUS_MSK) {
+       case TX_STATUS_SUCCESS:
+               return "SUCCESS";
+       TX_STATUS_POSTPONE(DELAY);
+       TX_STATUS_POSTPONE(FEW_BYTES);
+       TX_STATUS_POSTPONE(BT_PRIO);
+       TX_STATUS_POSTPONE(QUIET_PERIOD);
+       TX_STATUS_POSTPONE(CALC_TTAK);
+       TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
+       TX_STATUS_FAIL(SHORT_LIMIT);
+       TX_STATUS_FAIL(LONG_LIMIT);
+       TX_STATUS_FAIL(UNDERRUN);
+       TX_STATUS_FAIL(DRAIN_FLOW);
+       TX_STATUS_FAIL(RFKILL_FLUSH);
+       TX_STATUS_FAIL(LIFE_EXPIRE);
+       TX_STATUS_FAIL(DEST_PS);
+       TX_STATUS_FAIL(HOST_ABORTED);
+       TX_STATUS_FAIL(BT_RETRY);
+       TX_STATUS_FAIL(STA_INVALID);
+       TX_STATUS_FAIL(FRAG_DROPPED);
+       TX_STATUS_FAIL(TID_DISABLE);
+       TX_STATUS_FAIL(FIFO_FLUSHED);
+       TX_STATUS_FAIL(SMALL_CF_POLL);
+       TX_STATUS_FAIL(FW_DROP);
+       TX_STATUS_FAIL(STA_COLOR_MISMATCH);
+       }
+
+       return "UNKNOWN";
+
+#undef TX_STATUS_FAIL
+#undef TX_STATUS_POSTPONE
+}
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
+/**
+ * translate ucode response to mac80211 tx status control values
+ */
+static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags,
+                                        struct ieee80211_tx_info *info)
+{
+       struct ieee80211_tx_rate *r = &info->status.rates[0];
+
+       info->status.antenna =
+               ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
+       if (rate_n_flags & RATE_HT_MCS_GF_MSK)
+               r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
+       switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
+       case RATE_MCS_CHAN_WIDTH_20:
+               break;
+       case RATE_MCS_CHAN_WIDTH_40:
+               r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+               break;
+       case RATE_MCS_CHAN_WIDTH_80:
+               r->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
+               break;
+       case RATE_MCS_CHAN_WIDTH_160:
+               r->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH;
+               break;
+       }
+       if (rate_n_flags & RATE_MCS_SGI_MSK)
+               r->flags |= IEEE80211_TX_RC_SHORT_GI;
+       if (rate_n_flags & RATE_MCS_HT_MSK) {
+               r->flags |= IEEE80211_TX_RC_MCS;
+               r->idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+       } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+               ieee80211_rate_set_vht(
+                       r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK,
+                       ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
+                                               RATE_VHT_MCS_NSS_POS) + 1);
+               r->flags |= IEEE80211_TX_RC_VHT_MCS;
+       } else {
+               r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
+                                                            info->band);
+       }
+}
+
+static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
+                                    struct iwl_rx_packet *pkt)
+{
+       struct ieee80211_sta *sta;
+       u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+       int txq_id = SEQ_TO_QUEUE(sequence);
+       struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+       int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid);
+       int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid);
+       u32 status = le16_to_cpu(tx_resp->status.status);
+       u16 ssn = iwl_mvm_get_scd_ssn(tx_resp);
+       struct iwl_mvm_sta *mvmsta;
+       struct sk_buff_head skbs;
+       u8 skb_freed = 0;
+       u16 next_reclaimed, seq_ctl;
+
+       __skb_queue_head_init(&skbs);
+
+       seq_ctl = le16_to_cpu(tx_resp->seq_ctl);
+
+       /* we can free until ssn % q.n_bd not inclusive */
+       iwl_trans_reclaim(mvm->trans, txq_id, ssn, &skbs);
+
+       while (!skb_queue_empty(&skbs)) {
+               struct sk_buff *skb = __skb_dequeue(&skbs);
+               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+               skb_freed++;
+
+               iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
+
+               memset(&info->status, 0, sizeof(info->status));
+
+               info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+
+               /* inform mac80211 about what happened with the frame */
+               switch (status & TX_STATUS_MSK) {
+               case TX_STATUS_SUCCESS:
+               case TX_STATUS_DIRECT_DONE:
+                       info->flags |= IEEE80211_TX_STAT_ACK;
+                       break;
+               case TX_STATUS_FAIL_DEST_PS:
+                       info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+                       break;
+               default:
+                       break;
+               }
+
+               info->status.rates[0].count = tx_resp->failure_frame + 1;
+               iwl_mvm_hwrate_to_tx_control(le32_to_cpu(tx_resp->initial_rate),
+                                            info);
+
+               /* Single frame failure in an AMPDU queue => send BAR */
+               if (txq_id >= IWL_FIRST_AMPDU_QUEUE &&
+                   !(info->flags & IEEE80211_TX_STAT_ACK)) {
+                       /* there must be only one skb in the skb_list */
+                       WARN_ON_ONCE(skb_freed > 1 ||
+                                    !skb_queue_empty(&skbs));
+                       info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
+               }
+
+               /* W/A FW bug: seq_ctl is wrong when the queue is flushed */
+               if (status == TX_STATUS_FAIL_FIFO_FLUSHED) {
+                       struct ieee80211_hdr *hdr = (void *)skb->data;
+                       seq_ctl = le16_to_cpu(hdr->seq_ctrl);
+               }
+
+               ieee80211_tx_status(mvm->hw, skb);
+       }
+
+       if (txq_id >= IWL_FIRST_AMPDU_QUEUE) {
+               /* If this is an aggregation queue, we use the ssn since:
+                * ssn = wifi seq_num % 256.
+                * The seq_ctl is the sequence control of the packet to which
+                * this Tx response relates. But if there is a hole in the
+                * bitmap of the BA we received, this Tx response may allow to
+                * reclaim the hole and all the subsequent packets that were
+                * already acked. In that case, seq_ctl != ssn, and the next
+                * packet to be reclaimed will be ssn and not seq_ctl. In that
+                * case, several packets will be reclaimed even if
+                * frame_count = 1.
+                *
+                * The ssn is the index (% 256) of the latest packet that has
+                * treated (acked / dropped) + 1.
+                */
+               next_reclaimed = ssn;
+       } else {
+               /* The next packet to be reclaimed is the one after this one */
+               next_reclaimed = SEQ_TO_SN(seq_ctl + 0x10);
+       }
+
+       IWL_DEBUG_TX_REPLY(mvm,
+                          "TXQ %d status %s (0x%08x)\n\t\t\t\tinitial_rate 0x%x "
+                           "retries %d, idx=%d ssn=%d next_reclaimed=0x%x seq_ctl=0x%x\n",
+                          txq_id, iwl_mvm_get_tx_fail_reason(status),
+                          status, le32_to_cpu(tx_resp->initial_rate),
+                          tx_resp->failure_frame, SEQ_TO_INDEX(sequence),
+                          ssn, next_reclaimed, seq_ctl);
+
+       rcu_read_lock();
+
+       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+       if (!IS_ERR_OR_NULL(sta)) {
+               mvmsta = (void *)sta->drv_priv;
+
+               if (tid != IWL_TID_NON_QOS) {
+                       struct iwl_mvm_tid_data *tid_data =
+                               &mvmsta->tid_data[tid];
+
+                       spin_lock(&mvmsta->lock);
+                       tid_data->next_reclaimed = next_reclaimed;
+                       IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n",
+                                          next_reclaimed);
+                       iwl_mvm_check_ratid_empty(mvm, sta, tid);
+                       spin_unlock(&mvmsta->lock);
+               }
+
+#ifdef CONFIG_PM_SLEEP
+               mvmsta->last_seq_ctl = seq_ctl;
+#endif
+       } else {
+               sta = NULL;
+               mvmsta = NULL;
+       }
+
+       /*
+        * If the txq is not an AMPDU queue, there is no chance we freed
+        * several skbs. Check that out...
+        * If there are no pending frames for this STA, notify mac80211 that
+        * this station can go to sleep in its STA table.
+        */
+       if (txq_id < IWL_FIRST_AMPDU_QUEUE && mvmsta &&
+           !WARN_ON(skb_freed > 1) &&
+           mvmsta->vif->type == NL80211_IFTYPE_AP &&
+           atomic_sub_and_test(skb_freed, &mvmsta->pending_frames)) {
+               ieee80211_sta_block_awake(mvm->hw, sta, false);
+               set_bit(sta_id, mvm->sta_drained);
+               schedule_work(&mvm->sta_drained_wk);
+       }
+
+       rcu_read_unlock();
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+#define AGG_TX_STATE_(x) case AGG_TX_STATE_ ## x: return #x
+static const char *iwl_get_agg_tx_status(u16 status)
+{
+       switch (status & AGG_TX_STATE_STATUS_MSK) {
+       AGG_TX_STATE_(TRANSMITTED);
+       AGG_TX_STATE_(UNDERRUN);
+       AGG_TX_STATE_(BT_PRIO);
+       AGG_TX_STATE_(FEW_BYTES);
+       AGG_TX_STATE_(ABORT);
+       AGG_TX_STATE_(LAST_SENT_TTL);
+       AGG_TX_STATE_(LAST_SENT_TRY_CNT);
+       AGG_TX_STATE_(LAST_SENT_BT_KILL);
+       AGG_TX_STATE_(SCD_QUERY);
+       AGG_TX_STATE_(TEST_BAD_CRC32);
+       AGG_TX_STATE_(RESPONSE);
+       AGG_TX_STATE_(DUMP_TX);
+       AGG_TX_STATE_(DELAY_TX);
+       }
+
+       return "UNKNOWN";
+}
+
+static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm,
+                                     struct iwl_rx_packet *pkt)
+{
+       struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+       struct agg_tx_status *frame_status = &tx_resp->status;
+       int i;
+
+       for (i = 0; i < tx_resp->frame_count; i++) {
+               u16 fstatus = le16_to_cpu(frame_status[i].status);
+
+               IWL_DEBUG_TX_REPLY(mvm,
+                                  "status %s (0x%04x), try-count (%d) seq (0x%x)\n",
+                                  iwl_get_agg_tx_status(fstatus),
+                                  fstatus & AGG_TX_STATE_STATUS_MSK,
+                                  (fstatus & AGG_TX_STATE_TRY_CNT_MSK) >>
+                                       AGG_TX_STATE_TRY_CNT_POS,
+                                  le16_to_cpu(frame_status[i].sequence));
+       }
+}
+#else
+static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm,
+                                     struct iwl_rx_packet *pkt)
+{}
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
+static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
+                                 struct iwl_rx_packet *pkt)
+{
+       struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+       int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid);
+       int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid);
+       u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+       struct ieee80211_sta *sta;
+
+       if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_FIRST_AMPDU_QUEUE))
+               return;
+
+       if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS))
+               return;
+
+       iwl_mvm_rx_tx_cmd_agg_dbg(mvm, pkt);
+
+       rcu_read_lock();
+
+       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+       if (!WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
+               struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+               mvmsta->tid_data[tid].rate_n_flags =
+                       le32_to_cpu(tx_resp->initial_rate);
+       }
+
+       rcu_read_unlock();
+}
+
+int iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                     struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+
+       if (tx_resp->frame_count == 1)
+               iwl_mvm_rx_tx_cmd_single(mvm, pkt);
+       else
+               iwl_mvm_rx_tx_cmd_agg(mvm, pkt);
+
+       return 0;
+}
+
+int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                       struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_mvm_ba_notif *ba_notif = (void *)pkt->data;
+       struct sk_buff_head reclaimed_skbs;
+       struct iwl_mvm_tid_data *tid_data;
+       struct ieee80211_tx_info *info;
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb;
+       int sta_id, tid, freed;
+
+       /* "flow" corresponds to Tx queue */
+       u16 scd_flow = le16_to_cpu(ba_notif->scd_flow);
+
+       /* "ssn" is start of block-ack Tx window, corresponds to index
+        * (in Tx queue's circular buffer) of first TFD/frame in window */
+       u16 ba_resp_scd_ssn = le16_to_cpu(ba_notif->scd_ssn);
+
+       sta_id = ba_notif->sta_id;
+       tid = ba_notif->tid;
+
+       rcu_read_lock();
+
+       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+       /* Reclaiming frames for a station that has been deleted ? */
+       if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
+               rcu_read_unlock();
+               return 0;
+       }
+
+       mvmsta = (void *)sta->drv_priv;
+       tid_data = &mvmsta->tid_data[tid];
+
+       if (WARN_ONCE(tid_data->txq_id != scd_flow, "Q %d, tid %d, flow %d",
+                     tid_data->txq_id, tid, scd_flow)) {
+               rcu_read_unlock();
+               return 0;
+       }
+
+       spin_lock(&mvmsta->lock);
+
+       __skb_queue_head_init(&reclaimed_skbs);
+
+       /*
+        * Release all TFDs before the SSN, i.e. all TFDs in front of
+        * block-ack window (we assume that they've been successfully
+        * transmitted ... if not, it's too late anyway).
+        */
+       iwl_trans_reclaim(mvm->trans, scd_flow, ba_resp_scd_ssn,
+                         &reclaimed_skbs);
+
+       IWL_DEBUG_TX_REPLY(mvm,
+                          "BA_NOTIFICATION Received from %pM, sta_id = %d\n",
+                          (u8 *)&ba_notif->sta_addr_lo32,
+                          ba_notif->sta_id);
+       IWL_DEBUG_TX_REPLY(mvm,
+                          "TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n",
+                          ba_notif->tid, le16_to_cpu(ba_notif->seq_ctl),
+                          (unsigned long long)le64_to_cpu(ba_notif->bitmap),
+                          scd_flow, ba_resp_scd_ssn, ba_notif->txed,
+                          ba_notif->txed_2_done);
+
+       tid_data->next_reclaimed = ba_resp_scd_ssn;
+
+       iwl_mvm_check_ratid_empty(mvm, sta, tid);
+
+       freed = 0;
+
+       skb_queue_walk(&reclaimed_skbs, skb) {
+               hdr = (struct ieee80211_hdr *)skb->data;
+
+               if (ieee80211_is_data_qos(hdr->frame_control))
+                       freed++;
+               else
+                       WARN_ON_ONCE(1);
+
+               info = IEEE80211_SKB_CB(skb);
+               iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
+
+               if (freed == 1) {
+                       /* this is the first skb we deliver in this batch */
+                       /* put the rate scaling data there */
+                       info = IEEE80211_SKB_CB(skb);
+                       memset(&info->status, 0, sizeof(info->status));
+                       info->flags |= IEEE80211_TX_STAT_ACK;
+                       info->flags |= IEEE80211_TX_STAT_AMPDU;
+                       info->status.ampdu_ack_len = ba_notif->txed_2_done;
+                       info->status.ampdu_len = ba_notif->txed;
+                       iwl_mvm_hwrate_to_tx_control(tid_data->rate_n_flags,
+                                                    info);
+               }
+       }
+
+       spin_unlock(&mvmsta->lock);
+
+       rcu_read_unlock();
+
+       while (!skb_queue_empty(&reclaimed_skbs)) {
+               skb = __skb_dequeue(&reclaimed_skbs);
+               ieee80211_tx_status(mvm->hw, skb);
+       }
+
+       return 0;
+}
+
+int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync)
+{
+       int ret;
+       struct iwl_tx_path_flush_cmd flush_cmd = {
+               .queues_ctl = cpu_to_le32(tfd_msk),
+               .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH),
+       };
+
+       u32 flags = sync ? CMD_SYNC : CMD_ASYNC;
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags,
+                                  sizeof(flush_cmd), &flush_cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret);
+       return ret;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
new file mode 100644 (file)
index 0000000..000e842
--- /dev/null
@@ -0,0 +1,472 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <net/mac80211.h>
+
+#include "iwl-debug.h"
+#include "iwl-io.h"
+
+#include "mvm.h"
+#include "fw-api-rs.h"
+
+/*
+ * Will return 0 even if the cmd failed when RFKILL is asserted unless
+ * CMD_WANT_SKB is set in cmd->flags.
+ */
+int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
+{
+       int ret;
+
+       /*
+        * Synchronous commands from this op-mode must hold
+        * the mutex, this ensures we don't try to send two
+        * (or more) synchronous commands at a time.
+        */
+       if (!(cmd->flags & CMD_ASYNC))
+               lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_trans_send_cmd(mvm->trans, cmd);
+
+       /*
+        * If the caller wants the SKB, then don't hide any problems, the
+        * caller might access the response buffer which will be NULL if
+        * the command failed.
+        */
+       if (cmd->flags & CMD_WANT_SKB)
+               return ret;
+
+       /* Silently ignore failures if RFKILL is asserted */
+       if (!ret || ret == -ERFKILL)
+               return 0;
+       return ret;
+}
+
+int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u8 id,
+                        u32 flags, u16 len, const void *data)
+{
+       struct iwl_host_cmd cmd = {
+               .id = id,
+               .len = { len, },
+               .data = { data, },
+               .flags = flags,
+       };
+
+       return iwl_mvm_send_cmd(mvm, &cmd);
+}
+
+/*
+ * We assume that the caller set the status to the sucess value
+ */
+int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
+                           u32 *status)
+{
+       struct iwl_rx_packet *pkt;
+       struct iwl_cmd_response *resp;
+       int ret, resp_len;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /*
+        * Only synchronous commands can wait for status,
+        * we use WANT_SKB so the caller can't.
+        */
+       if (WARN_ONCE(cmd->flags & (CMD_ASYNC | CMD_WANT_SKB),
+                     "cmd flags %x", cmd->flags))
+               return -EINVAL;
+
+       cmd->flags |= CMD_SYNC | CMD_WANT_SKB;
+
+       ret = iwl_trans_send_cmd(mvm->trans, cmd);
+       if (ret == -ERFKILL) {
+               /*
+                * The command failed because of RFKILL, don't update
+                * the status, leave it as success and return 0.
+                */
+               return 0;
+       } else if (ret) {
+               return ret;
+       }
+
+       pkt = cmd->resp_pkt;
+       /* Can happen if RFKILL is asserted */
+       if (!pkt) {
+               ret = 0;
+               goto out_free_resp;
+       }
+
+       if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+               ret = -EIO;
+               goto out_free_resp;
+       }
+
+       resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+       if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) {
+               ret = -EIO;
+               goto out_free_resp;
+       }
+
+       resp = (void *)pkt->data;
+       *status = le32_to_cpu(resp->status);
+ out_free_resp:
+       iwl_free_resp(cmd);
+       return ret;
+}
+
+/*
+ * We assume that the caller set the status to the sucess value
+ */
+int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u8 id, u16 len,
+                               const void *data, u32 *status)
+{
+       struct iwl_host_cmd cmd = {
+               .id = id,
+               .len = { len, },
+               .data = { data, },
+       };
+
+       return iwl_mvm_send_cmd_status(mvm, &cmd, status);
+}
+
+#define IWL_DECLARE_RATE_INFO(r) \
+       [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP
+
+/*
+ * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP
+ */
+static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = {
+       IWL_DECLARE_RATE_INFO(1),
+       IWL_DECLARE_RATE_INFO(2),
+       IWL_DECLARE_RATE_INFO(5),
+       IWL_DECLARE_RATE_INFO(11),
+       IWL_DECLARE_RATE_INFO(6),
+       IWL_DECLARE_RATE_INFO(9),
+       IWL_DECLARE_RATE_INFO(12),
+       IWL_DECLARE_RATE_INFO(18),
+       IWL_DECLARE_RATE_INFO(24),
+       IWL_DECLARE_RATE_INFO(36),
+       IWL_DECLARE_RATE_INFO(48),
+       IWL_DECLARE_RATE_INFO(54),
+};
+
+int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
+                                       enum ieee80211_band band)
+{
+       int rate = rate_n_flags & RATE_LEGACY_RATE_MSK;
+       int idx;
+       int band_offset = 0;
+
+       /* Legacy rate format, search for match in table */
+       if (band == IEEE80211_BAND_5GHZ)
+               band_offset = IWL_FIRST_OFDM_RATE;
+       for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++)
+               if (fw_rate_idx_to_plcp[idx] == rate)
+                       return idx - band_offset;
+
+       return -1;
+}
+
+u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx)
+{
+       /* Get PLCP rate for tx_cmd->rate_n_flags */
+       return fw_rate_idx_to_plcp[rate_idx];
+}
+
+int iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_error_resp *err_resp = (void *)pkt->data;
+
+       IWL_ERR(mvm, "FW Error notification: type 0x%08X cmd_id 0x%02X\n",
+               le32_to_cpu(err_resp->error_type), err_resp->cmd_id);
+       IWL_ERR(mvm, "FW Error notification: seq 0x%04X service 0x%08X\n",
+               le16_to_cpu(err_resp->bad_cmd_seq_num),
+               le32_to_cpu(err_resp->error_service));
+       IWL_ERR(mvm, "FW Error notification: timestamp 0x%16llX\n",
+               le64_to_cpu(err_resp->timestamp));
+       return 0;
+}
+
+/*
+ * Returns the first antenna as ANT_[ABC], as defined in iwl-config.h.
+ * The parameter should also be a combination of ANT_[ABC].
+ */
+u8 first_antenna(u8 mask)
+{
+       BUILD_BUG_ON(ANT_A != BIT(0)); /* using ffs is wrong if not */
+       WARN_ON_ONCE(!mask); /* ffs will return 0 if mask is zeroed */
+       return (u8)(BIT(ffs(mask)));
+}
+
+/*
+ * Toggles between TX antennas to send the probe request on.
+ * Receives the bitmask of valid TX antennas and the *index* used
+ * for the last TX, and returns the next valid *index* to use.
+ * In order to set it in the tx_cmd, must do BIT(idx).
+ */
+u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
+{
+       u8 ind = last_idx;
+       int i;
+
+       for (i = 0; i < RATE_MCS_ANT_NUM; i++) {
+               ind = (ind + 1) % RATE_MCS_ANT_NUM;
+               if (valid & BIT(ind))
+                       return ind;
+       }
+
+       WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid);
+       return last_idx;
+}
+
+static struct {
+       char *name;
+       u8 num;
+} advanced_lookup[] = {
+       { "NMI_INTERRUPT_WDG", 0x34 },
+       { "SYSASSERT", 0x35 },
+       { "UCODE_VERSION_MISMATCH", 0x37 },
+       { "BAD_COMMAND", 0x38 },
+       { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
+       { "FATAL_ERROR", 0x3D },
+       { "NMI_TRM_HW_ERR", 0x46 },
+       { "NMI_INTERRUPT_TRM", 0x4C },
+       { "NMI_INTERRUPT_BREAK_POINT", 0x54 },
+       { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
+       { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
+       { "NMI_INTERRUPT_HOST", 0x66 },
+       { "NMI_INTERRUPT_ACTION_PT", 0x7C },
+       { "NMI_INTERRUPT_UNKNOWN", 0x84 },
+       { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
+       { "ADVANCED_SYSASSERT", 0 },
+};
+
+static const char *desc_lookup(u32 num)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(advanced_lookup) - 1; i++)
+               if (advanced_lookup[i].num == num)
+                       return advanced_lookup[i].name;
+
+       /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
+       return advanced_lookup[i].name;
+}
+
+/*
+ * Note: This structure is read from the device with IO accesses,
+ * and the reading already does the endian conversion. As it is
+ * read with u32-sized accesses, any members with a different size
+ * need to be ordered correctly though!
+ */
+struct iwl_error_event_table {
+       u32 valid;              /* (nonzero) valid, (0) log is empty */
+       u32 error_id;           /* type of error */
+       u32 pc;                 /* program counter */
+       u32 blink1;             /* branch link */
+       u32 blink2;             /* branch link */
+       u32 ilink1;             /* interrupt link */
+       u32 ilink2;             /* interrupt link */
+       u32 data1;              /* error-specific data */
+       u32 data2;              /* error-specific data */
+       u32 data3;              /* error-specific data */
+       u32 bcon_time;          /* beacon timer */
+       u32 tsf_low;            /* network timestamp function timer */
+       u32 tsf_hi;             /* network timestamp function timer */
+       u32 gp1;                /* GP1 timer register */
+       u32 gp2;                /* GP2 timer register */
+       u32 gp3;                /* GP3 timer register */
+       u32 ucode_ver;          /* uCode version */
+       u32 hw_ver;             /* HW Silicon version */
+       u32 brd_ver;            /* HW board version */
+       u32 log_pc;             /* log program counter */
+       u32 frame_ptr;          /* frame pointer */
+       u32 stack_ptr;          /* stack pointer */
+       u32 hcmd;               /* last host command header */
+       u32 isr0;               /* isr status register LMPM_NIC_ISR0:
+                                * rxtx_flag */
+       u32 isr1;               /* isr status register LMPM_NIC_ISR1:
+                                * host_flag */
+       u32 isr2;               /* isr status register LMPM_NIC_ISR2:
+                                * enc_flag */
+       u32 isr3;               /* isr status register LMPM_NIC_ISR3:
+                                * time_flag */
+       u32 isr4;               /* isr status register LMPM_NIC_ISR4:
+                                * wico interrupt */
+       u32 isr_pref;           /* isr status register LMPM_NIC_PREF_STAT */
+       u32 wait_event;         /* wait event() caller address */
+       u32 l2p_control;        /* L2pControlField */
+       u32 l2p_duration;       /* L2pDurationField */
+       u32 l2p_mhvalid;        /* L2pMhValidBits */
+       u32 l2p_addr_match;     /* L2pAddrMatchStat */
+       u32 lmpm_pmg_sel;       /* indicate which clocks are turned on
+                                * (LMPM_PMG_SEL) */
+       u32 u_timestamp;        /* indicate when the date and time of the
+                                * compilation */
+       u32 flow_handler;       /* FH read/write pointers, RX credit */
+} __packed;
+
+#define ERROR_START_OFFSET  (1 * sizeof(u32))
+#define ERROR_ELEM_SIZE     (7 * sizeof(u32))
+
+void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
+{
+       struct iwl_trans *trans = mvm->trans;
+       struct iwl_error_event_table table;
+       u32 base;
+
+       base = mvm->error_event_table;
+       if (mvm->cur_ucode == IWL_UCODE_INIT) {
+               if (!base)
+                       base = mvm->fw->init_errlog_ptr;
+       } else {
+               if (!base)
+                       base = mvm->fw->inst_errlog_ptr;
+       }
+
+       if (base < 0x800000 || base >= 0x80C000) {
+               IWL_ERR(mvm,
+                       "Not valid error log pointer 0x%08X for %s uCode\n",
+                       base,
+                       (mvm->cur_ucode == IWL_UCODE_INIT)
+                                       ? "Init" : "RT");
+               return;
+       }
+
+       iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
+
+       if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
+               IWL_ERR(trans, "Start IWL Error Log Dump:\n");
+               IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
+                       mvm->status, table.valid);
+       }
+
+       trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
+                                     table.data1, table.data2, table.data3,
+                                     table.blink1, table.blink2, table.ilink1,
+                                     table.ilink2, table.bcon_time, table.gp1,
+                                     table.gp2, table.gp3, table.ucode_ver,
+                                     table.hw_ver, table.brd_ver);
+       IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
+               desc_lookup(table.error_id));
+       IWL_ERR(mvm, "0x%08X | uPc\n", table.pc);
+       IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1);
+       IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2);
+       IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1);
+       IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2);
+       IWL_ERR(mvm, "0x%08X | data1\n", table.data1);
+       IWL_ERR(mvm, "0x%08X | data2\n", table.data2);
+       IWL_ERR(mvm, "0x%08X | data3\n", table.data3);
+       IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time);
+       IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low);
+       IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi);
+       IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1);
+       IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2);
+       IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3);
+       IWL_ERR(mvm, "0x%08X | uCode version\n", table.ucode_ver);
+       IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver);
+       IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver);
+       IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd);
+       IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0);
+       IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1);
+       IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2);
+       IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3);
+       IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4);
+       IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref);
+       IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event);
+       IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control);
+       IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration);
+       IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
+       IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
+       IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
+       IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
+       IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
+}
+
+/**
+ * iwl_mvm_send_lq_cmd() - Send link quality command
+ * @init: This command is sent as part of station initialization right
+ *        after station has been added.
+ *
+ * The link quality command is sent as the last step of station creation.
+ * This is the special case in which init is set and we call a callback in
+ * this case to clear the state indicating that station creation is in
+ * progress.
+ */
+int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
+                       u8 flags, bool init)
+{
+       struct iwl_host_cmd cmd = {
+               .id = LQ_CMD,
+               .len = { sizeof(struct iwl_lq_cmd), },
+               .flags = flags,
+               .data = { lq, },
+       };
+
+       if (WARN_ON(lq->sta_id == IWL_INVALID_STATION))
+               return -EINVAL;
+
+       if (WARN_ON(init && (cmd.flags & CMD_ASYNC)))
+               return -EINVAL;
+
+       return iwl_mvm_send_cmd(mvm, &cmd);
+}
index f8620ecae6b41dc1a4d05c756c6dbe00b3ec3148..ff3389757281b868e541be82bab3ebb55eeb84d3 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 244019cec3e1e8ca22a4c10ff0d0b17836d54cfd..e7de33128b16203e30d570a3b7f6dc2c45b938b3 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 83ca40321ff1e789b9aca2959b1d2b602b0beef6..5096f7c96ab6ce79e331e0200fd10fb9b399c79c 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index d4df976d470904da3be3f60d0a01f5c4abfbb40f..801ff49796dd03bd0e38037641a64d607b10927c 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
diff --git a/drivers/net/wireless/iwlwifi/pcie/7000.c b/drivers/net/wireless/iwlwifi/pcie/7000.c
new file mode 100644 (file)
index 0000000..6e35b2b
--- /dev/null
@@ -0,0 +1,111 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include "iwl-config.h"
+#include "iwl-agn-hw.h"
+#include "cfg.h"
+
+/* Highest firmware API version supported */
+#define IWL7260_UCODE_API_MAX  6
+#define IWL3160_UCODE_API_MAX  6
+
+/* Oldest version we won't warn about */
+#define IWL7260_UCODE_API_OK   6
+#define IWL3160_UCODE_API_OK   6
+
+/* Lowest firmware API version supported */
+#define IWL7260_UCODE_API_MIN  6
+#define IWL3160_UCODE_API_MIN  6
+
+/* NVM versions */
+#define IWL7260_NVM_VERSION            0x0a1d
+#define IWL7260_TX_POWER_VERSION       0xffff /* meaningless */
+#define IWL3160_NVM_VERSION            0x709
+#define IWL3160_TX_POWER_VERSION       0xffff /* meaningless */
+
+#define IWL7260_FW_PRE "iwlwifi-7260-"
+#define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode"
+
+#define IWL3160_FW_PRE "iwlwifi-3160-"
+#define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode"
+
+static const struct iwl_base_params iwl7000_base_params = {
+       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .num_of_queues = IWLAGN_NUM_QUEUES,
+       .pll_cfg_val = 0,
+       .shadow_ram_support = true,
+       .led_compensation = 57,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .wd_timeout = IWL_LONG_WD_TIMEOUT,
+       .max_event_log_size = 512,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+};
+
+static const struct iwl_ht_params iwl7000_ht_params = {
+       .ht_greenfield_support = true,
+       .use_rts_for_aggregation = true, /* use rts/cts protection */
+       .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+#define IWL_DEVICE_7000                                                \
+       .ucode_api_max = IWL7260_UCODE_API_MAX,                 \
+       .ucode_api_ok = IWL7260_UCODE_API_OK,                   \
+       .ucode_api_min = IWL7260_UCODE_API_MIN,                 \
+       .device_family = IWL_DEVICE_FAMILY_7000,                \
+       .max_inst_size = IWL60_RTC_INST_SIZE,                   \
+       .max_data_size = IWL60_RTC_DATA_SIZE,                   \
+       .base_params = &iwl7000_base_params,                    \
+       /* TODO: .bt_params? */                                 \
+       .need_temp_offset_calib = true,                         \
+       .led_mode = IWL_LED_RF_STATE,                           \
+       .adv_pm = true                                          \
+
+
+const struct iwl_cfg iwl7260_2ac_cfg = {
+       .name = "Intel(R) Dual Band Wireless AC7260",
+       .fw_name_pre = IWL7260_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL7260_NVM_VERSION,
+       .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl3160_ac_cfg = {
+       .name = "Intel(R) Dual Band Wireless AC3160",
+       .fw_name_pre = IWL3160_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL3160_NVM_VERSION,
+       .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
+};
+
+MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
+MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
index 82152311d73b3ebe5a89d3cdfac1732892327696..c6f8e83c3551e81a82670f4c1a3f61e502b1dc18 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -109,5 +109,7 @@ extern const struct iwl_cfg iwl6035_2agn_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;
+extern const struct iwl_cfg iwl7260_2ac_cfg;
+extern const struct iwl_cfg iwl3160_ac_cfg;
 
 #endif /* __iwl_pci_h__ */
index c2e141af353c657c86dc0a8b86dff9a4515a9186..7bc0fb9128ddc86f7cddfe63001a500f7430271d 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -255,6 +255,12 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)},
        {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)},
 
+/* 7000 Series */
+       {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_ac_cfg)},
+
        {0}
 };
 MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
index bc5e9ecfd1bb97d62509b1f23f5385a4da908936..cf478ec04cf74e81246b7717e5eaecc512955e33 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -231,8 +231,6 @@ struct iwl_txq {
  * @rx_replenish: work that will be called when buffers need to be allocated
  * @drv - pointer to iwl_drv
  * @trans: pointer to the generic transport area
- * @irq - the irq number for the device
- * @irq_requested: true when the irq has been requested
  * @scd_base_addr: scheduler sram base address in SRAM
  * @scd_bc_tbls: pointer to the byte count table of the scheduler
  * @kw: keep warm address
@@ -243,8 +241,10 @@ struct iwl_txq {
  * @status - transport specific status flags
  * @cmd_queue - command queue number
  * @rx_buf_size_8k: 8 kB RX buffer size
+ * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes)
  * @rx_page_order: page order for receive buffer size
  * @wd_timeout: queue watchdog timeout (jiffies)
+ * @reg_lock: protect hw register access
  */
 struct iwl_trans_pcie {
        struct iwl_rxq rxq;
@@ -258,11 +258,9 @@ struct iwl_trans_pcie {
        int ict_index;
        u32 inta;
        bool use_ict;
-       bool irq_requested;
        struct tasklet_struct irq_tasklet;
        struct isr_statistics isr_stats;
 
-       unsigned int irq;
        spinlock_t irq_lock;
        u32 inta_mask;
        u32 scd_base_addr;
@@ -288,12 +286,16 @@ struct iwl_trans_pcie {
        u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS];
 
        bool rx_buf_size_8k;
+       bool bc_table_dword;
        u32 rx_page_order;
 
        const char **command_names;
 
        /* queue watchdog */
        unsigned long wd_timeout;
+
+       /*protect hw register */
+       spinlock_t reg_lock;
 };
 
 /**
@@ -368,6 +370,8 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
                            struct iwl_rx_cmd_buffer *rxb, int handler_status);
 void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
                            struct sk_buff_head *skbs);
+void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
+
 /*****************************************************
 * Error handling
 ******************************************************/
index 8389cd38338ba70766561d13c35591e1073faca6..a9ca1d35fa9382831c2eae6e8587954b0cc5872d 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -436,7 +436,7 @@ static int iwl_pcie_rx_alloc(struct iwl_trans *trans)
 err_rb_stts:
        dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE,
                          rxq->bd, rxq->bd_dma);
-       memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma));
+       rxq->bd_dma = 0;
        rxq->bd = NULL;
 err_bd:
        return -ENOMEM;
@@ -455,6 +455,10 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq)
 
        /* Stop Rx DMA */
        iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
+       /* reset and flush pointers */
+       iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0);
+       iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0);
+       iwl_write_direct32(trans, FH_RSCSR_CHNL0_RDPTR, 0);
 
        /* Reset driver's Rx queue write index */
        iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
@@ -491,7 +495,6 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_rxq *rxq = &trans_pcie->rxq;
-
        int i, err;
        unsigned long flags;
 
@@ -518,6 +521,7 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
        rxq->read = rxq->write = 0;
        rxq->write_actual = 0;
        rxq->free_count = 0;
+       memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts));
        spin_unlock_irqrestore(&rxq->lock, flags);
 
        iwl_pcie_rx_replenish(trans);
@@ -545,13 +549,15 @@ void iwl_pcie_rx_free(struct iwl_trans *trans)
                return;
        }
 
+       cancel_work_sync(&trans_pcie->rx_replenish);
+
        spin_lock_irqsave(&rxq->lock, flags);
        iwl_pcie_rxq_free_rbs(trans);
        spin_unlock_irqrestore(&rxq->lock, flags);
 
        dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE,
                          rxq->bd, rxq->bd_dma);
-       memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma));
+       rxq->bd_dma = 0;
        rxq->bd = NULL;
 
        if (rxq->rb_stts)
@@ -560,7 +566,7 @@ void iwl_pcie_rx_free(struct iwl_trans *trans)
                                  rxq->rb_stts, rxq->rb_stts_dma);
        else
                IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n");
-       memset(&rxq->rb_stts_dma, 0, sizeof(rxq->rb_stts_dma));
+       rxq->rb_stts_dma = 0;
        rxq->rb_stts = NULL;
 }
 
@@ -588,6 +594,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
                int index, cmd_index, err, len;
                struct iwl_rx_cmd_buffer rxcb = {
                        ._offset = offset,
+                       ._rx_page_order = trans_pcie->rx_page_order,
                        ._page = rxb->page,
                        ._page_stolen = false,
                        .truesize = max_len,
index 35708b959ad6e563a38d29f1bd75c51e87c9c2c8..56d4f72500bcbcf9dd6712edf67b10951d1869d8 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "iwl-agn-hw.h"
 #include "internal.h"
 
-static void iwl_pcie_set_pwr_vmain(struct iwl_trans *trans)
+static void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans,
+                                                 u32 reg, u32 mask, u32 value)
 {
-/*
- * (for documentation purposes)
- * to set power to V_AUX, do:
+       u32 v;
 
-               if (pci_pme_capable(priv->pci_dev, PCI_D3cold))
-                       iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
-                                              APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
-                                              ~APMG_PS_CTRL_MSK_PWR_SRC);
- */
+#ifdef CONFIG_IWLWIFI_DEBUG
+       WARN_ON_ONCE(value & ~mask);
+#endif
 
-       iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
-                              APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
-                              ~APMG_PS_CTRL_MSK_PWR_SRC);
+       v = iwl_read32(trans, reg);
+       v &= ~mask;
+       v |= value;
+       iwl_write32(trans, reg, v);
+}
+
+static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans,
+                                             u32 reg, u32 mask)
+{
+       __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0);
+}
+
+static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
+                                           u32 reg, u32 mask)
+{
+       __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask);
+}
+
+static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
+{
+       if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold))
+               iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
+                                      APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
+                                      ~APMG_PS_CTRL_MSK_PWR_SRC);
+       else
+               iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
+                                      APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
+                                      ~APMG_PS_CTRL_MSK_PWR_SRC);
 }
 
 /* PCI registers */
@@ -259,7 +281,7 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans)
 
        spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
 
-       iwl_pcie_set_pwr_vmain(trans);
+       iwl_pcie_set_pwr(trans, false);
 
        iwl_op_mode_nic_config(trans->op_mode);
 
@@ -435,7 +457,7 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
 }
 
 static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
-                                  const struct fw_img *fw)
+                                  const struct fw_img *fw, bool run_in_rfkill)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret;
@@ -454,7 +476,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
        /* If platform's RF_KILL switch is NOT set to KILL */
        hw_rfkill = iwl_is_rfkill_set(trans);
        iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
-       if (hw_rfkill)
+       if (hw_rfkill && !run_in_rfkill)
                return -ERFKILL;
 
        iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
@@ -534,12 +556,6 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
 
        iwl_enable_rfkill_int(trans);
 
-       /* wait to make sure we flush pending tasklet*/
-       synchronize_irq(trans_pcie->irq);
-       tasklet_kill(&trans_pcie->irq_tasklet);
-
-       cancel_work_sync(&trans_pcie->rx_replenish);
-
        /* stop and reset the on-board processor */
        iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
 
@@ -551,46 +567,87 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
        clear_bit(STATUS_RFKILL, &trans_pcie->status);
 }
 
-static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans)
+static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans)
 {
        /* let the ucode operate on its own */
        iwl_write32(trans, CSR_UCODE_DRV_GP1_SET,
                    CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
 
        iwl_disable_interrupts(trans);
+       iwl_pcie_disable_ict(trans);
+
        iwl_clear_bit(trans, CSR_GP_CNTRL,
                      CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+       iwl_clear_bit(trans, CSR_GP_CNTRL,
+                     CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+       /*
+        * reset TX queues -- some of their registers reset during S3
+        * so if we don't reset everything here the D3 image would try
+        * to execute some invalid memory upon resume
+        */
+       iwl_trans_pcie_tx_reset(trans);
+
+       iwl_pcie_set_pwr(trans, true);
 }
 
-static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
+static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
+                                   enum iwl_d3_status *status)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       int err;
-       bool hw_rfkill;
+       u32 val;
+       int ret;
 
-       trans_pcie->inta_mask = CSR_INI_SET_MASK;
+       iwl_pcie_set_pwr(trans, false);
+
+       val = iwl_read32(trans, CSR_RESET);
+       if (val & CSR_RESET_REG_FLAG_NEVO_RESET) {
+               *status = IWL_D3_STATUS_RESET;
+               return 0;
+       }
 
-       if (!trans_pcie->irq_requested) {
-               tasklet_init(&trans_pcie->irq_tasklet, (void (*)(unsigned long))
-                       iwl_pcie_tasklet, (unsigned long)trans);
+       /*
+        * Also enables interrupts - none will happen as the device doesn't
+        * know we're waking it up, only when the opmode actually tells it
+        * after this call.
+        */
+       iwl_pcie_reset_ict(trans);
 
-               iwl_pcie_alloc_ict(trans);
+       iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+       iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
 
-               err = request_irq(trans_pcie->irq, iwl_pcie_isr_ict,
-                                 IRQF_SHARED, DRV_NAME, trans);
-               if (err) {
-                       IWL_ERR(trans, "Error allocating IRQ %d\n",
-                               trans_pcie->irq);
-                       goto error;
-               }
+       ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+                          25000);
+       if (ret) {
+               IWL_ERR(trans, "Failed to resume the device (mac ready)\n");
+               return ret;
+       }
+
+       iwl_trans_pcie_tx_reset(trans);
 
-               trans_pcie->irq_requested = true;
+       ret = iwl_pcie_rx_init(trans);
+       if (ret) {
+               IWL_ERR(trans, "Failed to resume the device (RX reset)\n");
+               return ret;
        }
 
+       iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
+                   CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
+
+       *status = IWL_D3_STATUS_ALIVE;
+       return 0;
+}
+
+static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
+{
+       bool hw_rfkill;
+       int err;
+
        err = iwl_pcie_prepare_card_hw(trans);
        if (err) {
                IWL_ERR(trans, "Error while preparing HW: %d\n", err);
-               goto err_free_irq;
+               return err;
        }
 
        iwl_pcie_apm_init(trans);
@@ -601,15 +658,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
        hw_rfkill = iwl_is_rfkill_set(trans);
        iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
 
-       return err;
-
-err_free_irq:
-       trans_pcie->irq_requested = false;
-       free_irq(trans_pcie->irq, trans);
-error:
-       iwl_pcie_free_ict(trans);
-       tasklet_kill(&trans_pcie->irq_tasklet);
-       return err;
+       return 0;
 }
 
 static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans,
@@ -703,19 +752,21 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
                msecs_to_jiffies(trans_cfg->queue_watchdog_timeout);
 
        trans_pcie->command_names = trans_cfg->command_names;
+       trans_pcie->bc_table_dword = trans_cfg->bc_table_dword;
 }
 
 void iwl_trans_pcie_free(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
+       synchronize_irq(trans_pcie->pci_dev->irq);
+       tasklet_kill(&trans_pcie->irq_tasklet);
+
        iwl_pcie_tx_free(trans);
        iwl_pcie_rx_free(trans);
 
-       if (trans_pcie->irq_requested == true) {
-               free_irq(trans_pcie->irq, trans);
-               iwl_pcie_free_ict(trans);
-       }
+       free_irq(trans_pcie->pci_dev->irq, trans);
+       iwl_pcie_free_ict(trans);
 
        pci_disable_msi(trans_pcie->pci_dev);
        iounmap(trans_pcie->hw_base);
@@ -751,13 +802,126 @@ static int iwl_trans_pcie_resume(struct iwl_trans *trans)
        hw_rfkill = iwl_is_rfkill_set(trans);
        iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
 
-       if (!hw_rfkill)
-               iwl_enable_interrupts(trans);
-
        return 0;
 }
 #endif /* CONFIG_PM_SLEEP */
 
+static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
+                                               unsigned long *flags)
+{
+       int ret;
+       struct iwl_trans_pcie *pcie_trans = IWL_TRANS_GET_PCIE_TRANS(trans);
+       spin_lock_irqsave(&pcie_trans->reg_lock, *flags);
+
+       /* this bit wakes up the NIC */
+       __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
+                                CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+
+       /*
+        * These bits say the device is running, and should keep running for
+        * at least a short while (at least as long as MAC_ACCESS_REQ stays 1),
+        * but they do not indicate that embedded SRAM is restored yet;
+        * 3945 and 4965 have volatile SRAM, and must save/restore contents
+        * to/from host DRAM when sleeping/waking for power-saving.
+        * Each direction takes approximately 1/4 millisecond; with this
+        * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a
+        * series of register accesses are expected (e.g. reading Event Log),
+        * to keep device from sleeping.
+        *
+        * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that
+        * SRAM is okay/restored.  We don't check that here because this call
+        * is just for hardware register access; but GP1 MAC_SLEEP check is a
+        * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log).
+        *
+        * 5000 series and later (including 1000 series) have non-volatile SRAM,
+        * and do not save/restore SRAM when power cycling.
+        */
+       ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+                          CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
+                          (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
+                           CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
+       if (unlikely(ret < 0)) {
+               iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
+               if (!silent) {
+                       u32 val = iwl_read32(trans, CSR_GP_CNTRL);
+                       WARN_ONCE(1,
+                                 "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n",
+                                 val);
+                       spin_unlock_irqrestore(&pcie_trans->reg_lock, *flags);
+                       return false;
+               }
+       }
+
+       /*
+        * Fool sparse by faking we release the lock - sparse will
+        * track nic_access anyway.
+        */
+       __release(&pcie_trans->reg_lock);
+       return true;
+}
+
+static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
+                                             unsigned long *flags)
+{
+       struct iwl_trans_pcie *pcie_trans = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       lockdep_assert_held(&pcie_trans->reg_lock);
+
+       /*
+        * Fool sparse by faking we acquiring the lock - sparse will
+        * track nic_access anyway.
+        */
+       __acquire(&pcie_trans->reg_lock);
+
+       __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+                                  CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+       /*
+        * Above we read the CSR_GP_CNTRL register, which will flush
+        * any previous writes, but we need the write that clears the
+        * MAC_ACCESS_REQ bit to be performed before any other writes
+        * scheduled on different CPUs (after we drop reg_lock).
+        */
+       mmiowb();
+       spin_unlock_irqrestore(&pcie_trans->reg_lock, *flags);
+}
+
+static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
+                                  void *buf, int dwords)
+{
+       unsigned long flags;
+       int offs, ret = 0;
+       u32 *vals = buf;
+
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
+               iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr);
+               for (offs = 0; offs < dwords; offs++)
+                       vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
+               iwl_trans_release_nic_access(trans, &flags);
+       } else {
+               ret = -EBUSY;
+       }
+       return ret;
+}
+
+static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr,
+                                   void *buf, int dwords)
+{
+       unsigned long flags;
+       int offs, ret = 0;
+       u32 *vals = buf;
+
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
+               iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr);
+               for (offs = 0; offs < dwords; offs++)
+                       iwl_write32(trans, HBUS_TARG_MEM_WDAT,
+                                   vals ? vals[offs] : 0);
+               iwl_trans_release_nic_access(trans, &flags);
+       } else {
+               ret = -EBUSY;
+       }
+       return ret;
+}
+
 #define IWL_FLUSH_WAIT_MS      2000
 
 static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
@@ -767,6 +931,8 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
        struct iwl_queue *q;
        int cnt;
        unsigned long now = jiffies;
+       u32 scd_sram_addr;
+       u8 buf[16];
        int ret = 0;
 
        /* waiting for all the tx frames complete might take a while */
@@ -780,14 +946,64 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
                        msleep(1);
 
                if (q->read_ptr != q->write_ptr) {
-                       IWL_ERR(trans, "fail to flush all tx fifo queues\n");
+                       IWL_ERR(trans,
+                               "fail to flush all tx fifo queues Q %d\n", cnt);
                        ret = -ETIMEDOUT;
                        break;
                }
        }
+
+       if (!ret)
+               return 0;
+
+       IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n",
+               txq->q.read_ptr, txq->q.write_ptr);
+
+       scd_sram_addr = trans_pcie->scd_base_addr +
+                       SCD_TX_STTS_QUEUE_OFFSET(txq->q.id);
+       iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf));
+
+       iwl_print_hex_error(trans, buf, sizeof(buf));
+
+       for (cnt = 0; cnt < FH_TCSR_CHNL_NUM; cnt++)
+               IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", cnt,
+                       iwl_read_direct32(trans, FH_TX_TRB_REG(cnt)));
+
+       for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) {
+               u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(cnt));
+               u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7;
+               bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE));
+               u32 tbl_dw =
+                       iwl_trans_read_mem32(trans, trans_pcie->scd_base_addr +
+                                            SCD_TRANS_TBL_OFFSET_QUEUE(cnt));
+
+               if (cnt & 0x1)
+                       tbl_dw = (tbl_dw & 0xFFFF0000) >> 16;
+               else
+                       tbl_dw = tbl_dw & 0x0000FFFF;
+
+               IWL_ERR(trans,
+                       "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
+                       cnt, active ? "" : "in", fifo, tbl_dw,
+                       iwl_read_prph(trans,
+                                     SCD_QUEUE_RDPTR(cnt)) & (txq->q.n_bd - 1),
+                       iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt)));
+       }
+
        return ret;
 }
 
+static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
+                                        u32 mask, u32 value)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       unsigned long flags;
+
+       spin_lock_irqsave(&trans_pcie->reg_lock, flags);
+       __iwl_trans_pcie_set_bits_mask(trans, reg, mask, value);
+       spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+}
+
 static const char *get_fh_string(int cmd)
 {
 #define IWL_CMD(x) case x: return #x
@@ -1212,7 +1428,8 @@ static const struct iwl_trans_ops trans_ops_pcie = {
        .start_fw = iwl_trans_pcie_start_fw,
        .stop_device = iwl_trans_pcie_stop_device,
 
-       .wowlan_suspend = iwl_trans_pcie_wowlan_suspend,
+       .d3_suspend = iwl_trans_pcie_d3_suspend,
+       .d3_resume = iwl_trans_pcie_d3_resume,
 
        .send_cmd = iwl_trans_pcie_send_hcmd,
 
@@ -1235,8 +1452,13 @@ static const struct iwl_trans_ops trans_ops_pcie = {
        .read32 = iwl_trans_pcie_read32,
        .read_prph = iwl_trans_pcie_read_prph,
        .write_prph = iwl_trans_pcie_write_prph,
+       .read_mem = iwl_trans_pcie_read_mem,
+       .write_mem = iwl_trans_pcie_write_mem,
        .configure = iwl_trans_pcie_configure,
        .set_pmi = iwl_trans_pcie_set_pmi,
+       .grab_nic_access = iwl_trans_pcie_grab_nic_access,
+       .release_nic_access = iwl_trans_pcie_release_nic_access,
+       .set_bits_mask = iwl_trans_pcie_set_bits_mask,
 };
 
 struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
@@ -1260,6 +1482,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        trans->cfg = cfg;
        trans_pcie->trans = trans;
        spin_lock_init(&trans_pcie->irq_lock);
+       spin_lock_init(&trans_pcie->reg_lock);
        init_waitqueue_head(&trans_pcie->ucode_write_waitq);
 
        /* W/A - seems to solve weird behavior. We need to remove this if we
@@ -1318,7 +1541,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        }
 
        trans->dev = &pdev->dev;
-       trans_pcie->irq = pdev->irq;
        trans_pcie->pci_dev = pdev;
        trans->hw_rev = iwl_read32(trans, CSR_HW_REV);
        trans->hw_id = (pdev->device << 16) + pdev->subsystem_device;
@@ -1327,7 +1549,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
 
        /* Initialize the wait queue for commands */
        init_waitqueue_head(&trans_pcie->wait_command_queue);
-       spin_lock_init(&trans->reg_lock);
 
        snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
                 "iwl_cmd_pool:%s", dev_name(trans->dev));
@@ -1344,8 +1565,27 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        if (!trans->dev_cmd_pool)
                goto out_pci_disable_msi;
 
+       trans_pcie->inta_mask = CSR_INI_SET_MASK;
+
+       tasklet_init(&trans_pcie->irq_tasklet, (void (*)(unsigned long))
+                    iwl_pcie_tasklet, (unsigned long)trans);
+
+       if (iwl_pcie_alloc_ict(trans))
+               goto out_free_cmd_pool;
+
+       err = request_irq(pdev->irq, iwl_pcie_isr_ict,
+                         IRQF_SHARED, DRV_NAME, trans);
+       if (err) {
+               IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq);
+               goto out_free_ict;
+       }
+
        return trans;
 
+out_free_ict:
+       iwl_pcie_free_ict(trans);
+out_free_cmd_pool:
+       kmem_cache_destroy(trans->dev_cmd_pool);
 out_pci_disable_msi:
        pci_disable_msi(pdev);
 out_pci_release_regions:
index c6cd9221e71aeed9fe4aac61d0217759c9034f0f..62b57be55c3f1bf4279f71e9bedb619310db591a 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -160,7 +160,7 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data)
        IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n",
                txq->q.read_ptr, txq->q.write_ptr);
 
-       iwl_read_targ_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf));
+       iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf));
 
        iwl_print_hex_error(trans, buf, sizeof(buf));
 
@@ -173,9 +173,9 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data)
                u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7;
                bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE));
                u32 tbl_dw =
-                       iwl_read_targ_mem(trans,
-                                         trans_pcie->scd_base_addr +
-                                         SCD_TRANS_TBL_OFFSET_QUEUE(i));
+                       iwl_trans_read_mem32(trans,
+                                            trans_pcie->scd_base_addr +
+                                            SCD_TRANS_TBL_OFFSET_QUEUE(i));
 
                if (i & 0x1)
                        tbl_dw = (tbl_dw & 0xFFFF0000) >> 16;
@@ -237,7 +237,10 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
                break;
        }
 
-       bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12));
+       if (trans_pcie->bc_table_dword)
+               len = DIV_ROUND_UP(len, 4);
+
+       bc_ent = cpu_to_le16(len | (sta_id << 12));
 
        scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
 
@@ -306,6 +309,9 @@ void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
                                return;
                        }
 
+                       IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id,
+                                    txq->q.write_ptr);
+
                        iwl_write_direct32(trans, HBUS_TARG_WRPTR,
                                     txq->q.write_ptr | (txq_id << 8));
 
@@ -612,7 +618,7 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)
        if (txq->q.n_bd) {
                dma_free_coherent(dev, sizeof(struct iwl_tfd) *
                                  txq->q.n_bd, txq->tfds, txq->q.dma_addr);
-               memset(&txq->q.dma_addr, 0, sizeof(txq->q.dma_addr));
+               txq->q.dma_addr = 0;
        }
 
        kfree(txq->entries);
@@ -638,9 +644,11 @@ static void iwl_pcie_txq_set_sched(struct iwl_trans *trans, u32 mask)
 void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       u32 a;
+       int nq = trans->cfg->base_params->num_of_queues;
        int chan;
        u32 reg_val;
+       int clear_dwords = (SCD_TRANS_TBL_OFFSET_QUEUE(nq) -
+                               SCD_CONTEXT_MEM_LOWER_BOUND) / sizeof(u32);
 
        /* make sure all queue are not stopped/used */
        memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped));
@@ -652,20 +660,10 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
        WARN_ON(scd_base_addr != 0 &&
                scd_base_addr != trans_pcie->scd_base_addr);
 
-       a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND;
-       /* reset conext data memory */
-       for (; a < trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_UPPER_BOUND;
-               a += 4)
-               iwl_write_targ_mem(trans, a, 0);
-       /* reset tx status memory */
-       for (; a < trans_pcie->scd_base_addr + SCD_TX_STTS_MEM_UPPER_BOUND;
-               a += 4)
-               iwl_write_targ_mem(trans, a, 0);
-       for (; a < trans_pcie->scd_base_addr +
-              SCD_TRANS_TBL_OFFSET_QUEUE(
-                               trans->cfg->base_params->num_of_queues);
-              a += 4)
-               iwl_write_targ_mem(trans, a, 0);
+       /* reset context data, TX status and translation data */
+       iwl_trans_write_mem(trans, trans_pcie->scd_base_addr +
+                                  SCD_CONTEXT_MEM_LOWER_BOUND,
+                           NULL, clear_dwords);
 
        iwl_write_prph(trans, SCD_DRAM_BASE_ADDR,
                       trans_pcie->scd_bc_tbls.dma >> 10);
@@ -697,6 +695,29 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
                            APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
 }
 
+void iwl_trans_pcie_tx_reset(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int txq_id;
+
+       for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
+            txq_id++) {
+               struct iwl_txq *txq = &trans_pcie->txq[txq_id];
+
+               iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id),
+                                  txq->q.dma_addr >> 8);
+               iwl_pcie_txq_unmap(trans, txq_id);
+               txq->q.read_ptr = 0;
+               txq->q.write_ptr = 0;
+       }
+
+       /* Tell NIC where to find the "keep warm" buffer */
+       iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG,
+                          trans_pcie->kw.dma >> 4);
+
+       iwl_pcie_tx_start(trans, trans_pcie->scd_base_addr);
+}
+
 /*
  * iwl_pcie_tx_stop - Stop all Tx DMA channels
  */
@@ -1002,14 +1023,14 @@ static int iwl_pcie_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid,
        tbl_dw_addr = trans_pcie->scd_base_addr +
                        SCD_TRANS_TBL_OFFSET_QUEUE(txq_id);
 
-       tbl_dw = iwl_read_targ_mem(trans, tbl_dw_addr);
+       tbl_dw = iwl_trans_read_mem32(trans, tbl_dw_addr);
 
        if (txq_id & 0x1)
                tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
        else
                tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
 
-       iwl_write_targ_mem(trans, tbl_dw_addr, tbl_dw);
+       iwl_trans_write_mem32(trans, tbl_dw_addr, tbl_dw);
 
        return 0;
 }
@@ -1068,9 +1089,9 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
        iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn);
 
        /* Set up Tx window size and frame limit for this queue */
-       iwl_write_targ_mem(trans, trans_pcie->scd_base_addr +
+       iwl_trans_write_mem32(trans, trans_pcie->scd_base_addr +
                        SCD_CONTEXT_QUEUE_OFFSET(txq_id), 0);
-       iwl_write_targ_mem(trans, trans_pcie->scd_base_addr +
+       iwl_trans_write_mem32(trans, trans_pcie->scd_base_addr +
                        SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32),
                        ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
                                SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
@@ -1101,8 +1122,8 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id)
 
        iwl_pcie_txq_set_inactive(trans, txq_id);
 
-       _iwl_write_targ_mem_dwords(trans, stts_addr,
-                                  zero_val, ARRAY_SIZE(zero_val));
+       iwl_trans_write_mem(trans, stts_addr, (void *)zero_val,
+                           ARRAY_SIZE(zero_val));
 
        iwl_pcie_txq_unmap(trans, txq_id);
 
@@ -1683,10 +1704,6 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
        tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys);
 
-       IWL_DEBUG_TX(trans, "sequence nr = 0X%x\n",
-                    le16_to_cpu(dev_cmd->hdr.sequence));
-       IWL_DEBUG_TX(trans, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags));
-
        /* Set up entry for this TFD in Tx byte-count array */
        iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len));
 
index ec6d5d6b452e7aebf8b8d782460e7b0263c121a0..230f8ebbe28921f6786e250b4f88cb421c013385 100644 (file)
@@ -2132,6 +2132,21 @@ static void lbs_cfg_set_regulatory_hint(struct lbs_private *priv)
        lbs_deb_leave(LBS_DEB_CFG80211);
 }
 
+static void lbs_reg_notifier(struct wiphy *wiphy,
+                            struct regulatory_request *request)
+{
+       struct lbs_private *priv = wiphy_priv(wiphy);
+
+       lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain "
+                       "callback for domain %c%c\n", request->alpha2[0],
+                       request->alpha2[1]);
+
+       memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2));
+       if (lbs_iface_active(priv))
+               lbs_set_11d_domain_info(priv);
+
+       lbs_deb_leave(LBS_DEB_CFG80211);
+}
 
 /*
  * This function get's called after lbs_setup_firmware() determined the
@@ -2184,24 +2199,6 @@ int lbs_cfg_register(struct lbs_private *priv)
        return ret;
 }
 
-int lbs_reg_notifier(struct wiphy *wiphy,
-               struct regulatory_request *request)
-{
-       struct lbs_private *priv = wiphy_priv(wiphy);
-       int ret = 0;
-
-       lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain "
-                       "callback for domain %c%c\n", request->alpha2[0],
-                       request->alpha2[1]);
-
-       memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2));
-       if (lbs_iface_active(priv))
-               ret = lbs_set_11d_domain_info(priv);
-
-       lbs_deb_leave(LBS_DEB_CFG80211);
-       return ret;
-}
-
 void lbs_scan_deinit(struct lbs_private *priv)
 {
        lbs_deb_enter(LBS_DEB_CFG80211);
index 558168ce634d519d67201d92c1992163550f4b4b..10995f59fe34a1796db45e914196fc7a03248c07 100644 (file)
@@ -10,9 +10,6 @@ struct wireless_dev *lbs_cfg_alloc(struct device *dev);
 int lbs_cfg_register(struct lbs_private *priv);
 void lbs_cfg_free(struct lbs_private *priv);
 
-int lbs_reg_notifier(struct wiphy *wiphy,
-               struct regulatory_request *request);
-
 void lbs_send_disconnect_notification(struct lbs_private *priv);
 void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event);
 
index ff9085502beacba1f66990b93e225fea6d3d2fa0..b73e497fe77049b59d13f9c822492c2dceb2aba1 100644 (file)
@@ -48,6 +48,10 @@ static int channels = 1;
 module_param(channels, int, 0444);
 MODULE_PARM_DESC(channels, "Number of concurrent channels");
 
+static bool paged_rx = false;
+module_param(paged_rx, bool, 0644);
+MODULE_PARM_DESC(paged_rx, "Use paged SKBs for RX instead of linear ones");
+
 /**
  * enum hwsim_regtest - the type of regulatory tests we offer
  *
@@ -333,11 +337,11 @@ struct mac80211_hwsim_data {
        int scan_chan_idx;
 
        struct ieee80211_channel *channel;
-       unsigned long beacon_int; /* in jiffies unit */
+       u64 beacon_int  /* beacon interval in us */;
        unsigned int rx_filter;
        bool started, idle, scanning;
        struct mutex mutex;
-       struct timer_list beacon_timer;
+       struct tasklet_hrtimer beacon_timer;
        enum ps_mode {
                PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL
        } ps;
@@ -357,7 +361,10 @@ struct mac80211_hwsim_data {
        int power_level;
 
        /* difference between this hw's clock and the real clock, in usecs */
-       u64 tsf_offset;
+       s64 tsf_offset;
+       s64 bcn_delta;
+       /* absolute beacon transmission time. Used to cover up "tx" delay. */
+       u64 abs_bcn_ts;
 };
 
 
@@ -405,15 +412,19 @@ static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb,
        return NETDEV_TX_OK;
 }
 
+static inline u64 mac80211_hwsim_get_tsf_raw(void)
+{
+       return ktime_to_us(ktime_get_real());
+}
+
 static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data)
 {
-       struct timeval tv = ktime_to_timeval(ktime_get_real());
-       u64 now = tv.tv_sec * USEC_PER_SEC + tv.tv_usec;
+       u64 now = mac80211_hwsim_get_tsf_raw();
        return cpu_to_le64(now + data->tsf_offset);
 }
 
 static u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
-               struct ieee80211_vif *vif)
+                                 struct ieee80211_vif *vif)
 {
        struct mac80211_hwsim_data *data = hw->priv;
        return le64_to_cpu(__mac80211_hwsim_get_tsf(data));
@@ -423,9 +434,13 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
                struct ieee80211_vif *vif, u64 tsf)
 {
        struct mac80211_hwsim_data *data = hw->priv;
-       struct timeval tv = ktime_to_timeval(ktime_get_real());
-       u64 now = tv.tv_sec * USEC_PER_SEC + tv.tv_usec;
-       data->tsf_offset = tsf - now;
+       u64 now = mac80211_hwsim_get_tsf(hw, vif);
+       u32 bcn_int = data->beacon_int;
+       s64 delta = tsf - now;
+
+       data->tsf_offset += delta;
+       /* adjust after beaconing with new timestamp at old TBTT */
+       data->bcn_delta = do_div(delta, bcn_int);
 }
 
 static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
@@ -696,7 +711,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_rx_status rx_status;
-       struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info);
+       u64 now;
 
        memset(&rx_status, 0, sizeof(rx_status));
        rx_status.flag |= RX_FLAG_MACTIME_START;
@@ -722,11 +737,23 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
        secpath_reset(skb);
        nf_reset(skb);
 
+       /*
+        * Get absolute mactime here so all HWs RX at the "same time", and
+        * absolute TX time for beacon mactime so the timestamp matches.
+        * Giving beacons a different mactime than non-beacons looks messy, but
+        * it helps the Toffset be exact and a ~10us mactime discrepancy
+        * probably doesn't really matter.
+        */
+       if (ieee80211_is_beacon(hdr->frame_control) ||
+           ieee80211_is_probe_resp(hdr->frame_control))
+               now = data->abs_bcn_ts;
+       else
+               now = mac80211_hwsim_get_tsf_raw();
+
        /* Copy skb to all enabled radios that are on the current frequency */
        spin_lock(&hwsim_radio_lock);
        list_for_each_entry(data2, &hwsim_radios, list) {
                struct sk_buff *nskb;
-               struct ieee80211_mgmt *mgmt;
                struct tx_iter_data tx_iter_data = {
                        .receive = false,
                        .channel = chan,
@@ -755,24 +782,30 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
                 * reserve some space for our vendor and the normal
                 * radiotap header, since we're copying anyway
                 */
-               nskb = skb_copy_expand(skb, 64, 0, GFP_ATOMIC);
-               if (nskb == NULL)
-                       continue;
+               if (skb->len < PAGE_SIZE && paged_rx) {
+                       struct page *page = alloc_page(GFP_ATOMIC);
+
+                       if (!page)
+                               continue;
+
+                       nskb = dev_alloc_skb(128);
+                       if (!nskb) {
+                               __free_page(page);
+                               continue;
+                       }
+
+                       memcpy(page_address(page), skb->data, skb->len);
+                       skb_add_rx_frag(nskb, 0, page, 0, skb->len, skb->len);
+               } else {
+                       nskb = skb_copy(skb, GFP_ATOMIC);
+                       if (!nskb)
+                               continue;
+               }
 
                if (mac80211_hwsim_addr_match(data2, hdr->addr1))
                        ack = true;
 
-               /* set bcn timestamp relative to receiver mactime */
-               rx_status.mactime =
-                               le64_to_cpu(__mac80211_hwsim_get_tsf(data2));
-               mgmt = (struct ieee80211_mgmt *) nskb->data;
-               if (ieee80211_is_beacon(mgmt->frame_control) ||
-                   ieee80211_is_probe_resp(mgmt->frame_control))
-                       mgmt->u.beacon.timestamp = cpu_to_le64(
-                               rx_status.mactime +
-                               (data->tsf_offset - data2->tsf_offset) +
-                               24 * 8 * 10 / txrate->bitrate);
-
+               rx_status.mactime = now + data2->tsf_offset;
 #if 0
                /*
                 * Don't enable this code by default as the OUI 00:00:00
@@ -896,7 +929,7 @@ static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
 {
        struct mac80211_hwsim_data *data = hw->priv;
        data->started = false;
-       del_timer(&data->beacon_timer);
+       tasklet_hrtimer_cancel(&data->beacon_timer);
        wiphy_debug(hw->wiphy, "%s\n", __func__);
 }
 
@@ -962,7 +995,11 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
 static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
                                     struct ieee80211_vif *vif)
 {
-       struct ieee80211_hw *hw = arg;
+       struct mac80211_hwsim_data *data = arg;
+       struct ieee80211_hw *hw = data->hw;
+       struct ieee80211_tx_info *info;
+       struct ieee80211_rate *txrate;
+       struct ieee80211_mgmt *mgmt;
        struct sk_buff *skb;
 
        hwsim_check_magic(vif);
@@ -975,26 +1012,48 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
        skb = ieee80211_beacon_get(hw, vif);
        if (skb == NULL)
                return;
+       info = IEEE80211_SKB_CB(skb);
+       txrate = ieee80211_get_tx_rate(hw, info);
+
+       mgmt = (struct ieee80211_mgmt *) skb->data;
+       /* fake header transmission time */
+       data->abs_bcn_ts = mac80211_hwsim_get_tsf_raw();
+       mgmt->u.beacon.timestamp = cpu_to_le64(data->abs_bcn_ts +
+                                              data->tsf_offset +
+                                              24 * 8 * 10 / txrate->bitrate);
 
        mac80211_hwsim_tx_frame(hw, skb,
                                rcu_dereference(vif->chanctx_conf)->def.chan);
 }
 
-
-static void mac80211_hwsim_beacon(unsigned long arg)
+static enum hrtimer_restart
+mac80211_hwsim_beacon(struct hrtimer *timer)
 {
-       struct ieee80211_hw *hw = (struct ieee80211_hw *) arg;
-       struct mac80211_hwsim_data *data = hw->priv;
+       struct mac80211_hwsim_data *data =
+               container_of(timer, struct mac80211_hwsim_data,
+                            beacon_timer.timer);
+       struct ieee80211_hw *hw = data->hw;
+       u64 bcn_int = data->beacon_int;
+       ktime_t next_bcn;
 
        if (!data->started)
-               return;
+               goto out;
 
        ieee80211_iterate_active_interfaces_atomic(
                hw, IEEE80211_IFACE_ITER_NORMAL,
-               mac80211_hwsim_beacon_tx, hw);
+               mac80211_hwsim_beacon_tx, data);
+
+       /* beacon at new TBTT + beacon interval */
+       if (data->bcn_delta) {
+               bcn_int -= data->bcn_delta;
+               data->bcn_delta = 0;
+       }
 
-       data->beacon_timer.expires = jiffies + data->beacon_int;
-       add_timer(&data->beacon_timer);
+       next_bcn = ktime_add(hrtimer_get_expires(timer),
+                            ns_to_ktime(bcn_int * 1000));
+       tasklet_hrtimer_start(&data->beacon_timer, next_bcn, HRTIMER_MODE_ABS);
+out:
+       return HRTIMER_NORESTART;
 }
 
 static const char *hwsim_chantypes[] = {
@@ -1032,9 +1091,16 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
 
        data->power_level = conf->power_level;
        if (!data->started || !data->beacon_int)
-               del_timer(&data->beacon_timer);
-       else
-               mod_timer(&data->beacon_timer, jiffies + data->beacon_int);
+               tasklet_hrtimer_cancel(&data->beacon_timer);
+       else if (!hrtimer_is_queued(&data->beacon_timer.timer)) {
+               u64 tsf = mac80211_hwsim_get_tsf(hw, NULL);
+               u32 bcn_int = data->beacon_int;
+               u64 until_tbtt = bcn_int - do_div(tsf, bcn_int);
+
+               tasklet_hrtimer_start(&data->beacon_timer,
+                                     ns_to_ktime(until_tbtt * 1000),
+                                     HRTIMER_MODE_REL);
+       }
 
        return 0;
 }
@@ -1084,12 +1150,26 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_BEACON_INT) {
                wiphy_debug(hw->wiphy, "  BCNINT: %d\n", info->beacon_int);
-               data->beacon_int = 1024 * info->beacon_int / 1000 * HZ / 1000;
-               if (WARN_ON(!data->beacon_int))
-                       data->beacon_int = 1;
-               if (data->started)
-                       mod_timer(&data->beacon_timer,
-                                 jiffies + data->beacon_int);
+               data->beacon_int = info->beacon_int * 1024;
+       }
+
+       if (changed & BSS_CHANGED_BEACON_ENABLED) {
+               wiphy_debug(hw->wiphy, "  BCN EN: %d\n", info->enable_beacon);
+               if (data->started &&
+                   !hrtimer_is_queued(&data->beacon_timer.timer) &&
+                   info->enable_beacon) {
+                       u64 tsf, until_tbtt;
+                       u32 bcn_int;
+                       if (WARN_ON(!data->beacon_int))
+                               data->beacon_int = 1000 * 1024;
+                       tsf = mac80211_hwsim_get_tsf(hw, vif);
+                       bcn_int = data->beacon_int;
+                       until_tbtt = bcn_int - do_div(tsf, bcn_int);
+                       tasklet_hrtimer_start(&data->beacon_timer,
+                                             ns_to_ktime(until_tbtt * 1000),
+                                             HRTIMER_MODE_REL);
+               } else if (!info->enable_beacon)
+                       tasklet_hrtimer_cancel(&data->beacon_timer);
        }
 
        if (changed & BSS_CHANGED_ERP_CTS_PROT) {
@@ -1292,7 +1372,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
        case IEEE80211_AMPDU_TX_START:
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                break;
-       case IEEE80211_AMPDU_TX_STOP:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                break;
        case IEEE80211_AMPDU_TX_OPERATIONAL:
@@ -2370,8 +2452,9 @@ static int __init init_mac80211_hwsim(void)
                                                        data->debugfs, data,
                                                        &hwsim_fops_group);
 
-               setup_timer(&data->beacon_timer, mac80211_hwsim_beacon,
-                           (unsigned long) hw);
+               tasklet_hrtimer_init(&data->beacon_timer,
+                                    mac80211_hwsim_beacon,
+                                    CLOCK_REALTIME, HRTIMER_MODE_ABS);
 
                list_add_tail(&data->list, &hwsim_radios);
        }
index 245a371f1a43a4746bda49912a4a2d656d5cf75e..48cc46bc152f001e1ca8eddc22068e3c67afab85 100644 (file)
@@ -53,7 +53,9 @@ mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type,
               sizeof(sband->ht_cap.mcs));
 
        if (priv->bss_mode == NL80211_IFTYPE_STATION ||
-           sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
+           (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
+            (priv->adapter->sec_chan_offset !=
+                                       IEEE80211_HT_PARAM_CHA_SEC_NONE)))
                /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
                SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask);
 
@@ -397,45 +399,6 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
        return ret_len;
 }
 
-/*
- * This function reconfigures the Tx buffer size in firmware.
- *
- * This function prepares a firmware command and issues it, if
- * the current Tx buffer size is different from the one requested.
- * Maximum configurable Tx buffer size is limited by the HT capability
- * field value.
- */
-void
-mwifiex_cfg_tx_buf(struct mwifiex_private *priv,
-                  struct mwifiex_bssdescriptor *bss_desc)
-{
-       u16 max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_2K;
-       u16 tx_buf, curr_tx_buf_size = 0;
-
-       if (bss_desc->bcn_ht_cap) {
-               if (le16_to_cpu(bss_desc->bcn_ht_cap->cap_info) &
-                               IEEE80211_HT_CAP_MAX_AMSDU)
-                       max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_8K;
-               else
-                       max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_4K;
-       }
-
-       tx_buf = min(priv->adapter->max_tx_buf_size, max_amsdu);
-
-       dev_dbg(priv->adapter->dev, "info: max_amsdu=%d, max_tx_buf=%d\n",
-               max_amsdu, priv->adapter->max_tx_buf_size);
-
-       if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_2K)
-               curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
-       else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_4K)
-               curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K;
-       else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_8K)
-               curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_8K;
-       if (curr_tx_buf_size != tx_buf)
-               mwifiex_send_cmd_async(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
-                                      HostCmd_ACT_GEN_SET, 0, &tx_buf);
-}
-
 /*
  * This function checks if the given pointer is valid entry of
  * Tx BA Stream table.
index 46006a54a6566ee1c744220926bc198159aac0dd..29a4c02479d62b19893cd8d7bdaa46fd3e1771e8 100644 (file)
@@ -34,8 +34,6 @@ int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, u16 cmd_action,
 int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
                               struct mwifiex_bssdescriptor *bss_desc,
                               u8 **buffer);
-void mwifiex_cfg_tx_buf(struct mwifiex_private *priv,
-                       struct mwifiex_bssdescriptor *bss_desc);
 void mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type,
                           struct mwifiex_ie_types_htcap *);
 int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv,
index 68d52cfc1ebd250a55a43c8b641344d7d6999461..af8fe6352eed143baca5fbeb5fee807c2964dc99 100644 (file)
@@ -278,14 +278,16 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
                dev_dbg(adapter->dev, "data: -EBUSY is returned\n");
                break;
        case -1:
-               adapter->data_sent = false;
+               if (adapter->iface_type != MWIFIEX_PCIE)
+                       adapter->data_sent = false;
                dev_err(adapter->dev, "%s: host_to_card failed: %#x\n",
                        __func__, ret);
                adapter->dbg.num_tx_host_to_card_failure++;
                mwifiex_write_data_complete(adapter, skb_aggr, 1, ret);
                return 0;
        case -EINPROGRESS:
-               adapter->data_sent = false;
+               if (adapter->iface_type != MWIFIEX_PCIE)
+                       adapter->data_sent = false;
                break;
        case 0:
                mwifiex_write_data_complete(adapter, skb_aggr, 1, ret);
index b55badef4660431fff9c18139e3ec29368d6bb42..3d64613ebb2979426d9fd1652b9fc17325cf8676 100644 (file)
@@ -121,7 +121,6 @@ info
        wmm_ac_vi = <number of packets sent to device from WMM AcVi queue>
        wmm_ac_be = <number of packets sent to device from WMM AcBE queue>
        wmm_ac_bk = <number of packets sent to device from WMM AcBK queue>
-       max_tx_buf_size = <maximum Tx buffer size>
        tx_buf_size = <current Tx buffer size>
        curr_tx_buf_size = <current Tx buffer size>
        ps_mode = <0/1, CAM mode/PS mode>
index cdb11b3964e27dd68de20ac68ab8442772a070a1..3a004b85b99fd91cc973762942026b838f641545 100644 (file)
@@ -519,8 +519,8 @@ static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy)
  *      - Set by user
  *      - Set bt Country IE
  */
-static int mwifiex_reg_notifier(struct wiphy *wiphy,
-                               struct regulatory_request *request)
+static void mwifiex_reg_notifier(struct wiphy *wiphy,
+                                struct regulatory_request *request)
 {
        struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
 
@@ -540,8 +540,6 @@ static int mwifiex_reg_notifier(struct wiphy *wiphy,
                break;
        }
        mwifiex_send_domain_info_cmd_fw(wiphy);
-
-       return 0;
 }
 
 /*
@@ -1327,6 +1325,7 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
        }
 
        mwifiex_set_ht_params(priv, bss_cfg, params);
+       mwifiex_set_wmm_params(priv, bss_cfg, params);
 
        if (params->inactivity_timeout > 0) {
                /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */
@@ -2248,6 +2247,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
        wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
        wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
                        WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
+                       WIPHY_FLAG_AP_UAPSD |
                        WIPHY_FLAG_CUSTOM_REGULATORY |
                        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 
index 46e34aa65d1c2dec171e99c5156c57ecd5905a11..753b5682d53fe9277dbc3dd92413250509f72ea4 100644 (file)
@@ -58,8 +58,6 @@ static struct mwifiex_debug_data items[] = {
         item_addr(packets_out[WMM_AC_BE]), 1},
        {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]),
         item_addr(packets_out[WMM_AC_BK]), 1},
-       {"max_tx_buf_size", item_size(max_tx_buf_size),
-        item_addr(max_tx_buf_size), 1},
        {"tx_buf_size", item_size(tx_buf_size),
         item_addr(tx_buf_size), 1},
        {"curr_tx_buf_size", item_size(curr_tx_buf_size),
index e9357d87d3279f7ba1d19c8246347f44ca46a269..e8a569aaa2e8ca24298e8e1e25161bd5a9379943 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/wait.h>
 #include <linux/timer.h>
 #include <linux/ieee80211.h>
+#include <net/mac80211.h>
 
 
 #define MWIFIEX_MAX_BSS_NUM         (3)
@@ -58,6 +59,8 @@
 #define MWIFIEX_RTS_MAX_VALUE              (2347)
 #define MWIFIEX_FRAG_MIN_VALUE             (256)
 #define MWIFIEX_FRAG_MAX_VALUE             (2346)
+#define MWIFIEX_WMM_VERSION                0x01
+#define MWIFIEX_WMM_SUBTYPE                0x01
 
 #define MWIFIEX_RETRY_LIMIT                14
 #define MWIFIEX_SDIO_BLOCK_SIZE            256
@@ -126,4 +129,19 @@ enum mwifiex_wmm_ac_e {
        WMM_AC_VI,
        WMM_AC_VO
 } __packed;
+
+struct ieee_types_wmm_ac_parameters {
+       u8 aci_aifsn_bitmap;
+       u8 ecw_bitmap;
+       __le16 tx_op_limit;
+} __packed;
+
+struct mwifiex_types_wmm_info {
+       u8 oui[4];
+       u8 subtype;
+       u8 version;
+       u8 qos_info;
+       u8 reserved;
+       struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
+} __packed;
 #endif /* !_MWIFIEX_DECL_H_ */
index 4dc8e2e9a889c06b3e8d41b352bc45c8e20efc88..ebe2f6a7984ce927ab75c689c52d50ba7842bf22 100644 (file)
@@ -330,6 +330,9 @@ enum P2P_MODES {
 #define HOST_SLEEP_CFG_GPIO_DEF                0xff
 #define HOST_SLEEP_CFG_GAP_DEF         0
 
+#define MWIFIEX_TIMEOUT_FOR_AP_RESP            0xfffc
+#define MWIFIEX_STATUS_CODE_AUTH_TIMEOUT       2
+
 #define CMD_F_HOSTCMD           (1 << 0)
 #define CMD_F_CANCELED          (1 << 1)
 
@@ -1131,12 +1134,6 @@ struct ieee_types_vendor_header {
        u8 version;
 } __packed;
 
-struct ieee_types_wmm_ac_parameters {
-       u8 aci_aifsn_bitmap;
-       u8 ecw_bitmap;
-       __le16 tx_op_limit;
-} __packed;
-
 struct ieee_types_wmm_parameter {
        /*
         * WMM Parameter IE - Vendor Specific Header:
@@ -1186,6 +1183,11 @@ struct mwifiex_ie_types_htcap {
        struct ieee80211_ht_cap ht_cap;
 } __packed;
 
+struct mwifiex_ie_types_wmmcap {
+       struct mwifiex_ie_types_header header;
+       struct mwifiex_types_wmm_info wmm_info;
+} __packed;
+
 struct mwifiex_ie_types_htinfo {
        struct mwifiex_ie_types_header header;
        struct ieee80211_ht_operation ht_oper;
index 39f03ce5a5b1cf50bfde4ec291438bc828a2dc22..820a19cfa562f5f4f5100fc71e148aa1ca63cc0d 100644 (file)
@@ -317,7 +317,6 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
 
        adapter->pm_wakeup_fw_try = false;
 
-       adapter->max_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
        adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
        adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
 
@@ -591,6 +590,12 @@ int mwifiex_init_fw(struct mwifiex_adapter *adapter)
                                return -1;
                }
        }
+
+       if (adapter->if_ops.init_fw_port) {
+               if (adapter->if_ops.init_fw_port(adapter))
+                       return -1;
+       }
+
        for (i = 0; i < adapter->priv_num; i++) {
                if (adapter->priv[i]) {
                        ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta);
index 4e31c6013ebe5d73db79e427ee3296c5daa940c5..f3d9d0445529d6c888c27177c53f463649292239 100644 (file)
@@ -20,7 +20,6 @@
 #ifndef _MWIFIEX_IOCTL_H_
 #define _MWIFIEX_IOCTL_H_
 
-#include <net/mac80211.h>
 #include <net/lib80211.h>
 
 enum {
@@ -107,6 +106,8 @@ struct mwifiex_uap_bss_param {
        u8 rates[MWIFIEX_SUPPORTED_RATES];
        u32 sta_ao_timer;
        u32 ps_sta_ao_timer;
+       u8 qos_info;
+       struct mwifiex_types_wmm_info wmm_info;
 };
 
 enum {
@@ -177,7 +178,6 @@ struct mwifiex_ds_tx_ba_stream_tbl {
 struct mwifiex_debug_info {
        u32 int_counter;
        u32 packets_out[MAX_NUM_TID];
-       u32 max_tx_buf_size;
        u32 tx_buf_size;
        u32 curr_tx_buf_size;
        u32 tx_tbl_num;
index 34738762196c3f6b28c922978331de921a26d0e7..56884d7af22c7f95c6c53f872022485f342a983e 100644 (file)
@@ -157,8 +157,8 @@ static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1,
 
        memset(rate1, 0, rate1_size);
 
-       for (i = 0; rate2[i] && i < rate2_size; i++) {
-               for (j = 0; tmp[j] && j < rate1_size; j++) {
+       for (i = 0; i < rate2_size && rate2[i]; i++) {
+               for (j = 0; j < rate1_size && tmp[j]; j++) {
                        /* Check common rate, excluding the bit for
                           basic rate */
                        if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) {
@@ -398,8 +398,6 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
 
        pos = (u8 *) assoc;
 
-       mwifiex_cfg_tx_buf(priv, bss_desc);
-
        cmd->command = cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE);
 
        /* Save so we know which BSS Desc to use in the response handler */
@@ -615,23 +613,33 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
        struct ieee_types_assoc_rsp *assoc_rsp;
        struct mwifiex_bssdescriptor *bss_desc;
        u8 enable_data = true;
+       u16 cap_info, status_code;
 
        assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params;
 
+       cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap);
+       status_code = le16_to_cpu(assoc_rsp->status_code);
+
        priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN,
                                   sizeof(priv->assoc_rsp_buf));
 
        memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size);
 
-       if (le16_to_cpu(assoc_rsp->status_code)) {
+       if (status_code) {
                priv->adapter->dbg.num_cmd_assoc_failure++;
                dev_err(priv->adapter->dev,
                        "ASSOC_RESP: failed, status code=%d err=%#x a_id=%#x\n",
-                       le16_to_cpu(assoc_rsp->status_code),
-                       le16_to_cpu(assoc_rsp->cap_info_bitmap),
-                       le16_to_cpu(assoc_rsp->a_id));
+                       status_code, cap_info, le16_to_cpu(assoc_rsp->a_id));
+
+               if (cap_info == MWIFIEX_TIMEOUT_FOR_AP_RESP) {
+                       if (status_code == MWIFIEX_STATUS_CODE_AUTH_TIMEOUT)
+                               ret = WLAN_STATUS_AUTH_TIMEOUT;
+                       else
+                               ret = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               } else {
+                       ret = status_code;
+               }
 
-               ret = le16_to_cpu(assoc_rsp->status_code);
                goto done;
        }
 
@@ -969,6 +977,16 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
                                        priv->adapter->config_bands);
                mwifiex_fill_cap_info(priv, radio_type, ht_cap);
 
+               if (adapter->sec_chan_offset ==
+                                       IEEE80211_HT_PARAM_CHA_SEC_NONE) {
+                       u16 tmp_ht_cap;
+
+                       tmp_ht_cap = le16_to_cpu(ht_cap->ht_cap.cap_info);
+                       tmp_ht_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+                       tmp_ht_cap &= ~IEEE80211_HT_CAP_SGI_40;
+                       ht_cap->ht_cap.cap_info = cpu_to_le16(tmp_ht_cap);
+               }
+
                pos += sizeof(struct mwifiex_ie_types_htcap);
                cmd_append_size += sizeof(struct mwifiex_ie_types_htcap);
 
index 1b3cfc82194081606e21988f7dbc03a53c854df7..ac799a046eb7e4f6a1d2970b7cb256a7ecd4e67a 100644 (file)
@@ -599,8 +599,10 @@ struct mwifiex_if_ops {
        int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *);
        int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *);
        int (*data_complete) (struct mwifiex_adapter *, struct sk_buff *);
+       int (*init_fw_port) (struct mwifiex_adapter *);
        int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
        void (*card_reset) (struct mwifiex_adapter *);
+       int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
 };
 
 struct mwifiex_adapter {
@@ -629,7 +631,6 @@ struct mwifiex_adapter {
        /* spin lock for main process */
        spinlock_t main_proc_lock;
        u32 mwifiex_processing;
-       u16 max_tx_buf_size;
        u16 tx_buf_size;
        u16 curr_tx_buf_size;
        u32 ioport;
@@ -890,6 +891,10 @@ void mwifiex_set_ht_params(struct mwifiex_private *priv,
                           struct cfg80211_ap_settings *params);
 void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg,
                           struct cfg80211_ap_settings *params);
+void
+mwifiex_set_wmm_params(struct mwifiex_private *priv,
+                      struct mwifiex_uap_bss_param *bss_cfg,
+                      struct cfg80211_ap_settings *params);
 
 /*
  * This function checks if the queuing is RA based or not.
index 0bbea88b35f9afffa7a7f34708ed4b3f68e0f1b7..fb07aa361bc1ae33299d0aaef2ba3992cd644d66 100644 (file)
@@ -39,17 +39,20 @@ static struct semaphore add_remove_card_sem;
 static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter);
 static int mwifiex_pcie_resume(struct pci_dev *pdev);
 
-/*
- * This function is called after skb allocation to update
- * "skb->cb" with physical address of data pointer.
- */
-static phys_addr_t *mwifiex_update_sk_buff_pa(struct sk_buff *skb)
+static int
+mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb,
+                      int size, int flags)
 {
-       phys_addr_t *buf_pa = MWIFIEX_SKB_PACB(skb);
-
-       *buf_pa = (phys_addr_t)virt_to_phys(skb->data);
+       struct pcie_service_card *card = adapter->card;
+       dma_addr_t buf_pa;
 
-       return buf_pa;
+       buf_pa = pci_map_single(card->dev, skb->data, size, flags);
+       if (pci_dma_mapping_error(card->dev, buf_pa)) {
+               dev_err(adapter->dev, "failed to map pci memory!\n");
+               return -1;
+       }
+       memcpy(skb->cb, &buf_pa, sizeof(dma_addr_t));
+       return 0;
 }
 
 /*
@@ -60,8 +63,8 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter)
        u32 *cookie_addr;
        struct pcie_service_card *card = adapter->card;
 
-       if (card->sleep_cookie) {
-               cookie_addr = (u32 *)card->sleep_cookie->data;
+       if (card->sleep_cookie_vbase) {
+               cookie_addr = (u32 *)card->sleep_cookie_vbase;
                dev_dbg(adapter->dev, "info: ACCESS_HW: sleep cookie=0x%x\n",
                        *cookie_addr);
                if (*cookie_addr == FW_AWAKE_COOKIE)
@@ -366,9 +369,7 @@ static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter)
 static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter)
 {
        struct pcie_service_card *card = adapter->card;
-       struct sk_buff *skb;
        int i;
-       phys_addr_t *buf_pa;
 
        /*
         * driver maintaines the write pointer and firmware maintaines the read
@@ -384,16 +385,18 @@ static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter)
                                                        MWIFIEX_MAX_TXRX_BD;
        dev_dbg(adapter->dev, "info: txbd_ring: Allocating %d bytes\n",
                card->txbd_ring_size);
-       card->txbd_ring_vbase = kzalloc(card->txbd_ring_size, GFP_KERNEL);
+       card->txbd_ring_vbase = pci_alloc_consistent(card->dev,
+                                                    card->txbd_ring_size,
+                                                    &card->txbd_ring_pbase);
        if (!card->txbd_ring_vbase) {
-               dev_err(adapter->dev, "Unable to alloc buffer for txbd ring\n");
+               dev_err(adapter->dev,
+                       "allocate consistent memory (%d bytes) failed!\n",
+                       card->txbd_ring_size);
                return -ENOMEM;
        }
-       card->txbd_ring_pbase = virt_to_phys(card->txbd_ring_vbase);
-
        dev_dbg(adapter->dev,
                "info: txbd_ring - base: %p, pbase: %#x:%x, len: %x\n",
-               card->txbd_ring_vbase, (u32)card->txbd_ring_pbase,
+               card->txbd_ring_vbase, (unsigned int)card->txbd_ring_pbase,
                (u32)((u64)card->txbd_ring_pbase >> 32), card->txbd_ring_size);
 
        for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
@@ -402,24 +405,9 @@ static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter)
                                      (sizeof(struct mwifiex_pcie_buf_desc)
                                       * i));
 
-               /* Allocate buffer here so that firmware can DMA data from it */
-               skb = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE);
-               if (!skb) {
-                       dev_err(adapter->dev, "Unable to allocate skb for TX ring.\n");
-                       kfree(card->txbd_ring_vbase);
-                       return -ENOMEM;
-               }
-               buf_pa = mwifiex_update_sk_buff_pa(skb);
-
-               skb_put(skb, MWIFIEX_RX_DATA_BUF_SIZE);
-               dev_dbg(adapter->dev, "info: TX ring: add new skb base: %p, "
-                       "buf_base: %p, buf_pbase: %#x:%x, buf_len: %#x\n",
-                       skb, skb->data, (u32)*buf_pa,
-                       (u32)(((u64)*buf_pa >> 32)), skb->len);
-
-               card->tx_buf_list[i] = skb;
-               card->txbd_ring[i]->paddr = *buf_pa;
-               card->txbd_ring[i]->len = (u16)skb->len;
+               card->tx_buf_list[i] = NULL;
+               card->txbd_ring[i]->paddr = 0;
+               card->txbd_ring[i]->len = 0;
                card->txbd_ring[i]->flags = 0;
        }
 
@@ -429,11 +417,16 @@ static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter)
 static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter)
 {
        struct pcie_service_card *card = adapter->card;
+       struct sk_buff *skb;
        int i;
 
        for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
-               if (card->tx_buf_list[i])
-                       dev_kfree_skb_any(card->tx_buf_list[i]);
+               if (card->tx_buf_list[i]) {
+                       skb = card->tx_buf_list[i];
+                       pci_unmap_single(card->dev, card->txbd_ring[i]->paddr,
+                                        skb->len, PCI_DMA_TODEVICE);
+                       dev_kfree_skb_any(skb);
+               }
                card->tx_buf_list[i] = NULL;
                card->txbd_ring[i]->paddr = 0;
                card->txbd_ring[i]->len = 0;
@@ -441,11 +434,15 @@ static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter)
                card->txbd_ring[i] = NULL;
        }
 
-       kfree(card->txbd_ring_vbase);
+       if (card->txbd_ring_vbase)
+               pci_free_consistent(card->dev, card->txbd_ring_size,
+                                   card->txbd_ring_vbase,
+                                   card->txbd_ring_pbase);
        card->txbd_ring_size = 0;
        card->txbd_wrptr = 0;
        card->txbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND;
        card->txbd_ring_vbase = NULL;
+       card->txbd_ring_pbase = 0;
 
        return 0;
 }
@@ -458,7 +455,7 @@ static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter)
        struct pcie_service_card *card = adapter->card;
        struct sk_buff *skb;
        int i;
-       phys_addr_t *buf_pa;
+       dma_addr_t buf_pa;
 
        /*
         * driver maintaines the read pointer and firmware maintaines the write
@@ -472,13 +469,15 @@ static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter)
                                                        MWIFIEX_MAX_TXRX_BD;
        dev_dbg(adapter->dev, "info: rxbd_ring: Allocating %d bytes\n",
                card->rxbd_ring_size);
-       card->rxbd_ring_vbase = kzalloc(card->rxbd_ring_size, GFP_KERNEL);
+       card->rxbd_ring_vbase = pci_alloc_consistent(card->dev,
+                                                    card->rxbd_ring_size,
+                                                    &card->rxbd_ring_pbase);
        if (!card->rxbd_ring_vbase) {
-               dev_err(adapter->dev, "Unable to allocate buffer for "
-                               "rxbd_ring.\n");
+               dev_err(adapter->dev,
+                       "allocate consistent memory (%d bytes) failed!\n",
+                       card->rxbd_ring_size);
                return -ENOMEM;
        }
-       card->rxbd_ring_pbase = virt_to_phys(card->rxbd_ring_vbase);
 
        dev_dbg(adapter->dev,
                "info: rxbd_ring - base: %p, pbase: %#x:%x, len: %#x\n",
@@ -500,16 +499,20 @@ static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter)
                        kfree(card->rxbd_ring_vbase);
                        return -ENOMEM;
                }
-               buf_pa = mwifiex_update_sk_buff_pa(skb);
-               skb_put(skb, MWIFIEX_RX_DATA_BUF_SIZE);
+               if (mwifiex_map_pci_memory(adapter, skb,
+                                          MWIFIEX_RX_DATA_BUF_SIZE,
+                                          PCI_DMA_FROMDEVICE))
+                       return -1;
+
+               MWIFIEX_SKB_PACB(skb, &buf_pa);
 
                dev_dbg(adapter->dev, "info: RX ring: add new skb base: %p, "
                        "buf_base: %p, buf_pbase: %#x:%x, buf_len: %#x\n",
-                       skb, skb->data, (u32)*buf_pa, (u32)((u64)*buf_pa >> 32),
+                       skb, skb->data, (u32)buf_pa, (u32)((u64)buf_pa >> 32),
                        skb->len);
 
                card->rx_buf_list[i] = skb;
-               card->rxbd_ring[i]->paddr = *buf_pa;
+               card->rxbd_ring[i]->paddr = buf_pa;
                card->rxbd_ring[i]->len = (u16)skb->len;
                card->rxbd_ring[i]->flags = 0;
        }
@@ -523,11 +526,17 @@ static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter)
 static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter)
 {
        struct pcie_service_card *card = adapter->card;
+       struct sk_buff *skb;
        int i;
 
        for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
-               if (card->rx_buf_list[i])
-                       dev_kfree_skb_any(card->rx_buf_list[i]);
+               if (card->rx_buf_list[i]) {
+                       skb = card->rx_buf_list[i];
+                       pci_unmap_single(card->dev, card->rxbd_ring[i]->paddr ,
+                                        MWIFIEX_RX_DATA_BUF_SIZE,
+                                        PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb_any(skb);
+               }
                card->rx_buf_list[i] = NULL;
                card->rxbd_ring[i]->paddr = 0;
                card->rxbd_ring[i]->len = 0;
@@ -535,11 +544,15 @@ static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter)
                card->rxbd_ring[i] = NULL;
        }
 
-       kfree(card->rxbd_ring_vbase);
+       if (card->rxbd_ring_vbase)
+               pci_free_consistent(card->dev, card->rxbd_ring_size,
+                                   card->rxbd_ring_vbase,
+                                   card->rxbd_ring_pbase);
        card->rxbd_ring_size = 0;
        card->rxbd_wrptr = 0;
        card->rxbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND;
        card->rxbd_ring_vbase = NULL;
+       card->rxbd_ring_pbase = 0;
 
        return 0;
 }
@@ -552,7 +565,7 @@ static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter)
        struct pcie_service_card *card = adapter->card;
        struct sk_buff *skb;
        int i;
-       phys_addr_t *buf_pa;
+       dma_addr_t buf_pa;
 
        /*
         * driver maintaines the read pointer and firmware maintaines the write
@@ -566,13 +579,15 @@ static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter)
                                                        MWIFIEX_MAX_EVT_BD;
        dev_dbg(adapter->dev, "info: evtbd_ring: Allocating %d bytes\n",
                card->evtbd_ring_size);
-       card->evtbd_ring_vbase = kzalloc(card->evtbd_ring_size, GFP_KERNEL);
+       card->evtbd_ring_vbase = pci_alloc_consistent(card->dev,
+                                                     card->evtbd_ring_size,
+                                                     &card->evtbd_ring_pbase);
        if (!card->evtbd_ring_vbase) {
                dev_err(adapter->dev,
-                       "Unable to allocate buffer. Terminating download\n");
+                       "allocate consistent memory (%d bytes) failed!\n",
+                       card->evtbd_ring_size);
                return -ENOMEM;
        }
-       card->evtbd_ring_pbase = virt_to_phys(card->evtbd_ring_vbase);
 
        dev_dbg(adapter->dev,
                "info: CMDRSP/EVT bd_ring - base: %p pbase: %#x:%x len: %#x\n",
@@ -594,16 +609,20 @@ static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter)
                        kfree(card->evtbd_ring_vbase);
                        return -ENOMEM;
                }
-               buf_pa = mwifiex_update_sk_buff_pa(skb);
                skb_put(skb, MAX_EVENT_SIZE);
 
+               if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE,
+                                          PCI_DMA_FROMDEVICE))
+                       return -1;
+
+               MWIFIEX_SKB_PACB(skb, &buf_pa);
                dev_dbg(adapter->dev, "info: Evt ring: add new skb. base: %p, "
                        "buf_base: %p, buf_pbase: %#x:%x, buf_len: %#x\n",
-                       skb, skb->data, (u32)*buf_pa, (u32)((u64)*buf_pa >> 32),
+                       skb, skb->data, (u32)buf_pa, (u32)((u64)buf_pa >> 32),
                        skb->len);
 
                card->evt_buf_list[i] = skb;
-               card->evtbd_ring[i]->paddr = *buf_pa;
+               card->evtbd_ring[i]->paddr = buf_pa;
                card->evtbd_ring[i]->len = (u16)skb->len;
                card->evtbd_ring[i]->flags = 0;
        }
@@ -617,11 +636,16 @@ static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter)
 static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter)
 {
        struct pcie_service_card *card = adapter->card;
+       struct sk_buff *skb;
        int i;
 
        for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) {
-               if (card->evt_buf_list[i])
-                       dev_kfree_skb_any(card->evt_buf_list[i]);
+               if (card->evt_buf_list[i]) {
+                       skb = card->evt_buf_list[i];
+                       pci_unmap_single(card->dev, card->evtbd_ring[i]->paddr,
+                                        MAX_EVENT_SIZE, PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb_any(skb);
+               }
                card->evt_buf_list[i] = NULL;
                card->evtbd_ring[i]->paddr = 0;
                card->evtbd_ring[i]->len = 0;
@@ -629,11 +653,15 @@ static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter)
                card->evtbd_ring[i] = NULL;
        }
 
-       kfree(card->evtbd_ring_vbase);
+       if (card->evtbd_ring_vbase)
+               pci_free_consistent(card->dev, card->evtbd_ring_size,
+                                   card->evtbd_ring_vbase,
+                                   card->evtbd_ring_pbase);
        card->evtbd_wrptr = 0;
        card->evtbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND;
        card->evtbd_ring_size = 0;
        card->evtbd_ring_vbase = NULL;
+       card->evtbd_ring_pbase = 0;
 
        return 0;
 }
@@ -653,21 +681,12 @@ static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter)
                        "Unable to allocate skb for command response data.\n");
                return -ENOMEM;
        }
-       mwifiex_update_sk_buff_pa(skb);
        skb_put(skb, MWIFIEX_UPLD_SIZE);
-       card->cmdrsp_buf = skb;
+       if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
+                                  PCI_DMA_FROMDEVICE))
+               return -1;
 
-       skb = NULL;
-       /* Allocate memory for sending command to firmware */
-       skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER);
-       if (!skb) {
-               dev_err(adapter->dev,
-                       "Unable to allocate skb for command data.\n");
-               return -ENOMEM;
-       }
-       mwifiex_update_sk_buff_pa(skb);
-       skb_put(skb, MWIFIEX_SIZE_OF_CMD_BUFFER);
-       card->cmd_buf = skb;
+       card->cmdrsp_buf = skb;
 
        return 0;
 }
@@ -678,18 +697,26 @@ static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter)
 static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter)
 {
        struct pcie_service_card *card;
+       dma_addr_t buf_pa;
 
        if (!adapter)
                return 0;
 
        card = adapter->card;
 
-       if (card && card->cmdrsp_buf)
+       if (card && card->cmdrsp_buf) {
+               MWIFIEX_SKB_PACB(card->cmdrsp_buf, &buf_pa);
+               pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
+                                PCI_DMA_FROMDEVICE);
                dev_kfree_skb_any(card->cmdrsp_buf);
+       }
 
-       if (card && card->cmd_buf)
+       if (card && card->cmd_buf) {
+               MWIFIEX_SKB_PACB(card->cmd_buf, &buf_pa);
+               pci_unmap_single(card->dev, buf_pa, MWIFIEX_SIZE_OF_CMD_BUFFER,
+                                PCI_DMA_TODEVICE);
                dev_kfree_skb_any(card->cmd_buf);
-
+       }
        return 0;
 }
 
@@ -698,27 +725,19 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter)
  */
 static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter)
 {
-       struct sk_buff *skb;
        struct pcie_service_card *card = adapter->card;
 
-       /* Allocate memory for sleep cookie */
-       skb = dev_alloc_skb(sizeof(u32));
-       if (!skb) {
-               dev_err(adapter->dev,
-                       "Unable to allocate skb for sleep cookie!\n");
+       card->sleep_cookie_vbase = pci_alloc_consistent(card->dev, sizeof(u32),
+                                                    &card->sleep_cookie_pbase);
+       if (!card->sleep_cookie_vbase) {
+               dev_err(adapter->dev, "pci_alloc_consistent failed!\n");
                return -ENOMEM;
        }
-       mwifiex_update_sk_buff_pa(skb);
-       skb_put(skb, sizeof(u32));
-
        /* Init val of Sleep Cookie */
-       *(u32 *)skb->data = FW_AWAKE_COOKIE;
+       *(u32 *)card->sleep_cookie_vbase = FW_AWAKE_COOKIE;
 
        dev_dbg(adapter->dev, "alloc_scook: sleep cookie=0x%x\n",
-               *((u32 *)skb->data));
-
-       /* Save the sleep cookie */
-       card->sleep_cookie = skb;
+               *((u32 *)card->sleep_cookie_vbase));
 
        return 0;
 }
@@ -735,24 +754,57 @@ static int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter)
 
        card = adapter->card;
 
-       if (card && card->sleep_cookie) {
-               dev_kfree_skb_any(card->sleep_cookie);
-               card->sleep_cookie = NULL;
+       if (card && card->sleep_cookie_vbase) {
+               pci_free_consistent(card->dev, sizeof(u32),
+                                   card->sleep_cookie_vbase,
+                                   card->sleep_cookie_pbase);
+               card->sleep_cookie_vbase = NULL;
        }
 
        return 0;
 }
 
+/* This function flushes the TX buffer descriptor ring
+ * This function defined as handler is also called while cleaning TXRX
+ * during disconnect/ bss stop.
+ */
+static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter)
+{
+       struct pcie_service_card *card = adapter->card;
+       u32 rdptr;
+
+       /* Read the TX ring read pointer set by firmware */
+       if (mwifiex_read_reg(adapter, REG_TXBD_RDPTR, &rdptr)) {
+               dev_err(adapter->dev,
+                       "Flush TXBD: failed to read REG_TXBD_RDPTR\n");
+               return -1;
+       }
+
+       if (!mwifiex_pcie_txbd_empty(card, rdptr)) {
+               card->txbd_flush = 1;
+               /* write pointer already set at last send
+                * send dnld-rdy intr again, wait for completion.
+                */
+               if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+                                     CPU_INTR_DNLD_RDY)) {
+                       dev_err(adapter->dev,
+                               "failed to assert dnld-rdy interrupt.\n");
+                       return -1;
+               }
+       }
+       return 0;
+}
+
 /*
- * This function sends data buffer to device
+ * This function unmaps and frees downloaded data buffer
  */
-static int
-mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb)
+static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
 {
+       const u32 num_tx_buffs = MWIFIEX_MAX_TXRX_BD;
+       struct sk_buff *skb;
+       dma_addr_t buf_pa;
+       u32 wrdoneidx, rdptr, unmap_count = 0;
        struct pcie_service_card *card = adapter->card;
-       u32 wrindx, rdptr;
-       phys_addr_t *buf_pa;
-       __le16 *tmp;
 
        if (!mwifiex_pcie_ok_to_access_hw(adapter))
                mwifiex_pm_wakeup_card(adapter);
@@ -760,34 +812,112 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb)
        /* Read the TX ring read pointer set by firmware */
        if (mwifiex_read_reg(adapter, REG_TXBD_RDPTR, &rdptr)) {
                dev_err(adapter->dev,
-                       "SEND DATA: failed to read REG_TXBD_RDPTR\n");
+                       "SEND COMP: failed to read REG_TXBD_RDPTR\n");
                return -1;
        }
 
-       wrindx = card->txbd_wrptr & MWIFIEX_TXBD_MASK;
+       dev_dbg(adapter->dev, "SEND COMP: rdptr_prev=0x%x, rdptr=0x%x\n",
+               card->txbd_rdptr, rdptr);
 
-       dev_dbg(adapter->dev, "info: SEND DATA: <Rd: %#x, Wr: %#x>\n", rdptr,
-               card->txbd_wrptr);
-       if (((card->txbd_wrptr & MWIFIEX_TXBD_MASK) !=
-                       (rdptr & MWIFIEX_TXBD_MASK)) ||
-           ((card->txbd_wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) !=
-                       (rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) {
-               struct sk_buff *skb_data;
+       /* free from previous txbd_rdptr to current txbd_rdptr */
+       while (((card->txbd_rdptr & MWIFIEX_TXBD_MASK) !=
+               (rdptr & MWIFIEX_TXBD_MASK)) ||
+              ((card->txbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) !=
+               (rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) {
+               wrdoneidx = card->txbd_rdptr & MWIFIEX_TXBD_MASK;
+
+               skb = card->tx_buf_list[wrdoneidx];
+               if (skb) {
+                       dev_dbg(adapter->dev,
+                               "SEND COMP: Detach skb %p at txbd_rdidx=%d\n",
+                               skb, wrdoneidx);
+                       MWIFIEX_SKB_PACB(skb, &buf_pa);
+                       pci_unmap_single(card->dev, buf_pa, skb->len,
+                                        PCI_DMA_TODEVICE);
+
+                       unmap_count++;
+
+                       if (card->txbd_flush)
+                               mwifiex_write_data_complete(adapter, skb, 0,
+                                                           -1);
+                       else
+                               mwifiex_write_data_complete(adapter, skb, 0, 0);
+               }
+
+               card->tx_buf_list[wrdoneidx] = NULL;
+               card->txbd_ring[wrdoneidx]->paddr = 0;
+               card->txbd_ring[wrdoneidx]->len = 0;
+               card->txbd_ring[wrdoneidx]->flags = 0;
+               card->txbd_rdptr++;
+
+               if ((card->txbd_rdptr & MWIFIEX_TXBD_MASK) == num_tx_buffs)
+                       card->txbd_rdptr = ((card->txbd_rdptr &
+                                           MWIFIEX_BD_FLAG_ROLLOVER_IND) ^
+                                           MWIFIEX_BD_FLAG_ROLLOVER_IND);
+       }
+
+       if (unmap_count)
+               adapter->data_sent = false;
+
+       if (card->txbd_flush) {
+               if (((card->txbd_wrptr & MWIFIEX_TXBD_MASK) ==
+                    (card->txbd_rdptr & MWIFIEX_TXBD_MASK)) &&
+                   ((card->txbd_wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) !=
+                    (card->txbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND)))
+                       card->txbd_flush = 0;
+               else
+                       mwifiex_clean_pcie_ring_buf(adapter);
+       }
+
+       return 0;
+}
+
+/* This function sends data buffer to device. First 4 bytes of payload
+ * are filled with payload length and payload type. Then this payload
+ * is mapped to PCI device memory. Tx ring pointers are advanced accordingly.
+ * Download ready interrupt to FW is deffered if Tx ring is not full and
+ * additional payload can be accomodated.
+ */
+static int
+mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
+                      struct mwifiex_tx_param *tx_param)
+{
+       struct pcie_service_card *card = adapter->card;
+       u32 wrindx;
+       int ret;
+       dma_addr_t buf_pa;
+       __le16 *tmp;
+
+       if (!(skb->data && skb->len)) {
+               dev_err(adapter->dev, "%s(): invalid parameter <%p, %#x>\n",
+                       __func__, skb->data, skb->len);
+               return -1;
+       }
+
+       if (!mwifiex_pcie_ok_to_access_hw(adapter))
+               mwifiex_pm_wakeup_card(adapter);
+
+       dev_dbg(adapter->dev, "info: SEND DATA: <Rd: %#x, Wr: %#x>\n",
+               card->txbd_rdptr, card->txbd_wrptr);
+       if (mwifiex_pcie_txbd_not_full(card)) {
                u8 *payload;
 
                adapter->data_sent = true;
-               skb_data = card->tx_buf_list[wrindx];
-               memcpy(skb_data->data, skb->data, skb->len);
-               payload = skb_data->data;
+               payload = skb->data;
                tmp = (__le16 *)&payload[0];
                *tmp = cpu_to_le16((u16)skb->len);
                tmp = (__le16 *)&payload[2];
                *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA);
-               skb_put(skb_data, MWIFIEX_RX_DATA_BUF_SIZE - skb_data->len);
-               skb_trim(skb_data, skb->len);
-               buf_pa = MWIFIEX_SKB_PACB(skb_data);
-               card->txbd_ring[wrindx]->paddr = *buf_pa;
-               card->txbd_ring[wrindx]->len = (u16)skb_data->len;
+
+               if (mwifiex_map_pci_memory(adapter, skb, skb->len ,
+                                          PCI_DMA_TODEVICE))
+                       return -1;
+
+               wrindx = card->txbd_wrptr & MWIFIEX_TXBD_MASK;
+               MWIFIEX_SKB_PACB(skb, &buf_pa);
+               card->tx_buf_list[wrindx] = skb;
+               card->txbd_ring[wrindx]->paddr = buf_pa;
+               card->txbd_ring[wrindx]->len = (u16)skb->len;
                card->txbd_ring[wrindx]->flags = MWIFIEX_BD_FLAG_FIRST_DESC |
                                                MWIFIEX_BD_FLAG_LAST_DESC;
 
@@ -802,19 +932,28 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                                      card->txbd_wrptr)) {
                        dev_err(adapter->dev,
                                "SEND DATA: failed to write REG_TXBD_WRPTR\n");
-                       return 0;
+                       ret = -1;
+                       goto done_unmap;
                }
-
-               /* Send the TX ready interrupt */
-               if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
-                                     CPU_INTR_DNLD_RDY)) {
-                       dev_err(adapter->dev,
-                               "SEND DATA: failed to assert door-bell intr\n");
-                       return -1;
+               if ((mwifiex_pcie_txbd_not_full(card)) &&
+                   tx_param->next_pkt_len) {
+                       /* have more packets and TxBD still can hold more */
+                       dev_dbg(adapter->dev,
+                               "SEND DATA: delay dnld-rdy interrupt.\n");
+                       adapter->data_sent = false;
+               } else {
+                       /* Send the TX ready interrupt */
+                       if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
+                                             CPU_INTR_DNLD_RDY)) {
+                               dev_err(adapter->dev,
+                                       "SEND DATA: failed to assert dnld-rdy interrupt.\n");
+                               ret = -1;
+                               goto done_unmap;
+                       }
                }
                dev_dbg(adapter->dev, "info: SEND DATA: Updated <Rd: %#x, Wr: "
                        "%#x> and sent packet to firmware successfully\n",
-                       rdptr, card->txbd_wrptr);
+                       card->txbd_rdptr, card->txbd_wrptr);
        } else {
                dev_dbg(adapter->dev,
                        "info: TX Ring full, can't send packets to fw\n");
@@ -827,7 +966,15 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                return -EBUSY;
        }
 
-       return 0;
+       return -EINPROGRESS;
+done_unmap:
+       MWIFIEX_SKB_PACB(skb, &buf_pa);
+       pci_unmap_single(card->dev, buf_pa, skb->len, PCI_DMA_TODEVICE);
+       card->tx_buf_list[wrindx] = NULL;
+       card->txbd_ring[wrindx]->paddr = 0;
+       card->txbd_ring[wrindx]->len = 0;
+       card->txbd_ring[wrindx]->flags = 0;
+       return ret;
 }
 
 /*
@@ -838,9 +985,13 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
 {
        struct pcie_service_card *card = adapter->card;
        u32 wrptr, rd_index;
+       dma_addr_t buf_pa;
        int ret = 0;
        struct sk_buff *skb_tmp = NULL;
 
+       if (!mwifiex_pcie_ok_to_access_hw(adapter))
+               mwifiex_pm_wakeup_card(adapter);
+
        /* Read the RX ring Write pointer set by firmware */
        if (mwifiex_read_reg(adapter, REG_RXBD_WRPTR, &wrptr)) {
                dev_err(adapter->dev,
@@ -848,6 +999,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
                ret = -1;
                goto done;
        }
+       card->rxbd_wrptr = wrptr;
 
        while (((wrptr & MWIFIEX_RXBD_MASK) !=
                (card->rxbd_rdptr & MWIFIEX_RXBD_MASK)) ||
@@ -855,27 +1007,50 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
                (card->rxbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) {
                struct sk_buff *skb_data;
                u16 rx_len;
+               __le16 pkt_len;
 
                rd_index = card->rxbd_rdptr & MWIFIEX_RXBD_MASK;
                skb_data = card->rx_buf_list[rd_index];
 
+               MWIFIEX_SKB_PACB(skb_data, &buf_pa);
+               pci_unmap_single(card->dev, buf_pa, MWIFIEX_RX_DATA_BUF_SIZE,
+                                PCI_DMA_FROMDEVICE);
+               card->rx_buf_list[rd_index] = NULL;
+
                /* Get data length from interface header -
-                  first byte is len, second byte is type */
-               rx_len = *((u16 *)skb_data->data);
+                * first 2 bytes for len, next 2 bytes is for type
+                */
+               pkt_len = *((__le16 *)skb_data->data);
+               rx_len = le16_to_cpu(pkt_len);
+               skb_put(skb_data, rx_len);
                dev_dbg(adapter->dev,
                        "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n",
                        card->rxbd_rdptr, wrptr, rx_len);
-               skb_tmp = dev_alloc_skb(rx_len);
+               skb_pull(skb_data, INTF_HEADER_LEN);
+               mwifiex_handle_rx_packet(adapter, skb_data);
+
+               skb_tmp = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE);
                if (!skb_tmp) {
-                       dev_dbg(adapter->dev,
-                               "info: Failed to alloc skb for RX\n");
-                       ret = -EBUSY;
-                       goto done;
+                       dev_err(adapter->dev,
+                               "Unable to allocate skb.\n");
+                       return -ENOMEM;
                }
 
-               skb_put(skb_tmp, rx_len);
+               if (mwifiex_map_pci_memory(adapter, skb_tmp,
+                                          MWIFIEX_RX_DATA_BUF_SIZE,
+                                          PCI_DMA_FROMDEVICE))
+                       return -1;
+
+               MWIFIEX_SKB_PACB(skb_tmp, &buf_pa);
+
+               dev_dbg(adapter->dev,
+                       "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n",
+                       skb_tmp, rd_index);
+               card->rx_buf_list[rd_index] = skb_tmp;
+               card->rxbd_ring[rd_index]->paddr = buf_pa;
+               card->rxbd_ring[rd_index]->len = skb_tmp->len;
+               card->rxbd_ring[rd_index]->flags = 0;
 
-               memcpy(skb_tmp->data, skb_data->data + INTF_HEADER_LEN, rx_len);
                if ((++card->rxbd_rdptr & MWIFIEX_RXBD_MASK) ==
                                                        MWIFIEX_MAX_TXRX_BD) {
                        card->rxbd_rdptr = ((card->rxbd_rdptr &
@@ -903,12 +1078,10 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
                }
                dev_dbg(adapter->dev,
                        "info: RECV DATA: Rcvd packet from fw successfully\n");
-               mwifiex_handle_rx_packet(adapter, skb_tmp);
+               card->rxbd_wrptr = wrptr;
        }
 
 done:
-       if (ret && skb_tmp)
-               dev_kfree_skb_any(skb_tmp);
        return ret;
 }
 
@@ -918,32 +1091,41 @@ done:
 static int
 mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
 {
-       phys_addr_t *buf_pa = MWIFIEX_SKB_PACB(skb);
+       dma_addr_t buf_pa;
+       struct pcie_service_card *card = adapter->card;
 
-       if (!(skb->data && skb->len && *buf_pa)) {
+       if (!(skb->data && skb->len)) {
                dev_err(adapter->dev,
-                       "Invalid parameter in %s <%p, %#x:%x, %x>\n",
-                       __func__, skb->data, skb->len,
-                       (u32)*buf_pa, (u32)((u64)*buf_pa >> 32));
+                       "Invalid parameter in %s <%p. len %d>\n",
+                       __func__, skb->data, skb->len);
                return -1;
        }
 
+       if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE))
+               return -1;
+
+       MWIFIEX_SKB_PACB(skb, &buf_pa);
+
        /* Write the lower 32bits of the physical address to scratch
         * register 0 */
-       if (mwifiex_write_reg(adapter, PCIE_SCRATCH_0_REG, (u32)*buf_pa)) {
+       if (mwifiex_write_reg(adapter, PCIE_SCRATCH_0_REG, (u32)buf_pa)) {
                dev_err(adapter->dev,
                        "%s: failed to write download command to boot code.\n",
                        __func__);
+               pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
+                                PCI_DMA_TODEVICE);
                return -1;
        }
 
        /* Write the upper 32bits of the physical address to scratch
         * register 1 */
        if (mwifiex_write_reg(adapter, PCIE_SCRATCH_1_REG,
-                             (u32)((u64)*buf_pa >> 32))) {
+                             (u32)((u64)buf_pa >> 32))) {
                dev_err(adapter->dev,
                        "%s: failed to write download command to boot code.\n",
                        __func__);
+               pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
+                                PCI_DMA_TODEVICE);
                return -1;
        }
 
@@ -952,6 +1134,8 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                dev_err(adapter->dev,
                        "%s: failed to write command len to scratch reg 2\n",
                        __func__);
+               pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
+                                PCI_DMA_TODEVICE);
                return -1;
        }
 
@@ -960,22 +1144,39 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                              CPU_INTR_DOOR_BELL)) {
                dev_err(adapter->dev,
                        "%s: failed to assert door-bell intr\n", __func__);
+               pci_unmap_single(card->dev, buf_pa,
+                                MWIFIEX_UPLD_SIZE, PCI_DMA_TODEVICE);
                return -1;
        }
 
        return 0;
 }
 
-/*
- * This function downloads commands to the device
+/* This function init rx port in firmware which in turn enables to receive data
+ * from device before transmitting any packet.
+ */
+static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter)
+{
+       struct pcie_service_card *card = adapter->card;
+
+       /* Write the RX ring read pointer in to REG_RXBD_RDPTR */
+       if (mwifiex_write_reg(adapter, REG_RXBD_RDPTR, card->rxbd_rdptr | 0)) {
+               dev_err(adapter->dev,
+                       "RECV DATA: failed to write REG_RXBD_RDPTR\n");
+               return -1;
+       }
+       return 0;
+}
+
+/* This function downloads commands to the device
  */
 static int
 mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
 {
        struct pcie_service_card *card = adapter->card;
        int ret = 0;
-       phys_addr_t *cmd_buf_pa;
-       phys_addr_t *cmdrsp_buf_pa;
+       dma_addr_t cmd_buf_pa, cmdrsp_buf_pa;
+       u8 *payload = (u8 *)skb->data;
 
        if (!(skb->data && skb->len)) {
                dev_err(adapter->dev, "Invalid parameter in %s <%p, %#x>\n",
@@ -990,17 +1191,18 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                return -EBUSY;
        }
 
-       /* Make sure a command buffer is available */
-       if (!card->cmd_buf) {
-               dev_err(adapter->dev, "Command buffer not available\n");
-               return -EBUSY;
-       }
+       if (!mwifiex_pcie_ok_to_access_hw(adapter))
+               mwifiex_pm_wakeup_card(adapter);
 
        adapter->cmd_sent = true;
-       /* Copy the given skb in to DMA accessable shared buffer */
-       skb_put(card->cmd_buf, MWIFIEX_SIZE_OF_CMD_BUFFER - card->cmd_buf->len);
-       skb_trim(card->cmd_buf, skb->len);
-       memcpy(card->cmd_buf->data, skb->data, skb->len);
+
+       *(__le16 *)&payload[0] = cpu_to_le16((u16)skb->len);
+       *(__le16 *)&payload[2] = cpu_to_le16(MWIFIEX_TYPE_CMD);
+
+       if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE))
+               return -1;
+
+       card->cmd_buf = skb;
 
        /* To send a command, the driver will:
                1. Write the 64bit physical address of the data buffer to
@@ -1013,11 +1215,11 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
        */
 
        if (card->cmdrsp_buf) {
-               cmdrsp_buf_pa = MWIFIEX_SKB_PACB(card->cmdrsp_buf);
+               MWIFIEX_SKB_PACB(card->cmdrsp_buf, &cmdrsp_buf_pa);
                /* Write the lower 32bits of the cmdrsp buffer physical
                   address */
                if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_LO,
-                                     (u32)*cmdrsp_buf_pa)) {
+                                     (u32)cmdrsp_buf_pa)) {
                        dev_err(adapter->dev,
                                "Failed to write download cmd to boot code.\n");
                        ret = -1;
@@ -1026,7 +1228,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                /* Write the upper 32bits of the cmdrsp buffer physical
                   address */
                if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_HI,
-                                     (u32)((u64)*cmdrsp_buf_pa >> 32))) {
+                                     (u32)((u64)cmdrsp_buf_pa >> 32))) {
                        dev_err(adapter->dev,
                                "Failed to write download cmd to boot code.\n");
                        ret = -1;
@@ -1034,9 +1236,9 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                }
        }
 
-       cmd_buf_pa = MWIFIEX_SKB_PACB(card->cmd_buf);
+       MWIFIEX_SKB_PACB(card->cmd_buf, &cmd_buf_pa);
        /* Write the lower 32bits of the physical address to REG_CMD_ADDR_LO */
-       if (mwifiex_write_reg(adapter, REG_CMD_ADDR_LO, (u32)*cmd_buf_pa)) {
+       if (mwifiex_write_reg(adapter, REG_CMD_ADDR_LO, (u32)cmd_buf_pa)) {
                dev_err(adapter->dev,
                        "Failed to write download cmd to boot code.\n");
                ret = -1;
@@ -1044,7 +1246,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
        }
        /* Write the upper 32bits of the physical address to REG_CMD_ADDR_HI */
        if (mwifiex_write_reg(adapter, REG_CMD_ADDR_HI,
-                             (u32)((u64)*cmd_buf_pa >> 32))) {
+                             (u32)((u64)cmd_buf_pa >> 32))) {
                dev_err(adapter->dev,
                        "Failed to write download cmd to boot code.\n");
                ret = -1;
@@ -1083,11 +1285,22 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
        struct pcie_service_card *card = adapter->card;
        struct sk_buff *skb = card->cmdrsp_buf;
        int count = 0;
+       u16 rx_len;
+       __le16 pkt_len;
+       dma_addr_t buf_pa;
 
        dev_dbg(adapter->dev, "info: Rx CMD Response\n");
 
+       MWIFIEX_SKB_PACB(skb, &buf_pa);
+       pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
+                        PCI_DMA_FROMDEVICE);
+
+       pkt_len = *((__le16 *)skb->data);
+       rx_len = le16_to_cpu(pkt_len);
+       skb_trim(skb, rx_len);
+       skb_pull(skb, INTF_HEADER_LEN);
+
        if (!adapter->curr_cmd) {
-               skb_pull(skb, INTF_HEADER_LEN);
                if (adapter->ps_state == PS_STATE_SLEEP_CFM) {
                        mwifiex_process_sleep_confirm_resp(adapter, skb->data,
                                                           skb->len);
@@ -1100,9 +1313,12 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
                }
                memcpy(adapter->upld_buf, skb->data,
                       min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len));
-               skb_push(skb, INTF_HEADER_LEN);
+               if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
+                                          PCI_DMA_FROMDEVICE))
+                       return -1;
+
+               MWIFIEX_SKB_PACB(skb, &buf_pa);
        } else if (mwifiex_pcie_ok_to_access_hw(adapter)) {
-               skb_pull(skb, INTF_HEADER_LEN);
                adapter->curr_cmd->resp_skb = skb;
                adapter->cmd_resp_received = true;
                /* Take the pointer and set it to CMD node and will
@@ -1136,10 +1352,23 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter,
                                        struct sk_buff *skb)
 {
        struct pcie_service_card *card = adapter->card;
+       dma_addr_t buf_pa;
+       struct sk_buff *skb_tmp;
 
        if (skb) {
                card->cmdrsp_buf = skb;
                skb_push(card->cmdrsp_buf, INTF_HEADER_LEN);
+               if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
+                                          PCI_DMA_FROMDEVICE))
+                       return -1;
+       }
+
+       skb_tmp = card->cmd_buf;
+       if (skb_tmp) {
+               MWIFIEX_SKB_PACB(skb_tmp, &buf_pa);
+               pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
+                                PCI_DMA_FROMDEVICE);
+               card->cmd_buf = NULL;
        }
 
        return 0;
@@ -1153,6 +1382,10 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter)
        struct pcie_service_card *card = adapter->card;
        u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
        u32 wrptr, event;
+       dma_addr_t buf_pa;
+
+       if (!mwifiex_pcie_ok_to_access_hw(adapter))
+               mwifiex_pm_wakeup_card(adapter);
 
        if (adapter->event_received) {
                dev_dbg(adapter->dev, "info: Event being processed, "
@@ -1184,6 +1417,10 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter)
 
                dev_dbg(adapter->dev, "info: Read Index: %d\n", rdptr);
                skb_cmd = card->evt_buf_list[rdptr];
+               MWIFIEX_SKB_PACB(skb_cmd, &buf_pa);
+               pci_unmap_single(card->dev, buf_pa, MAX_EVENT_SIZE,
+                                PCI_DMA_FROMDEVICE);
+
                /* Take the pointer and set it to event pointer in adapter
                   and will return back after event handling callback */
                card->evt_buf_list[rdptr] = NULL;
@@ -1228,7 +1465,7 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
        int ret = 0;
        u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
        u32 wrptr;
-       phys_addr_t *buf_pa;
+       dma_addr_t buf_pa;
 
        if (!skb)
                return 0;
@@ -1248,9 +1485,14 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
 
        if (!card->evt_buf_list[rdptr]) {
                skb_push(skb, INTF_HEADER_LEN);
+               if (mwifiex_map_pci_memory(adapter, skb,
+                                          MAX_EVENT_SIZE,
+                                          PCI_DMA_FROMDEVICE))
+                       return -1;
+               MWIFIEX_SKB_PACB(skb, &buf_pa);
                card->evt_buf_list[rdptr] = skb;
-               buf_pa = MWIFIEX_SKB_PACB(skb);
-               card->evtbd_ring[rdptr]->paddr = *buf_pa;
+               MWIFIEX_SKB_PACB(skb, &buf_pa);
+               card->evtbd_ring[rdptr]->paddr = buf_pa;
                card->evtbd_ring[rdptr]->len = (u16)skb->len;
                card->evtbd_ring[rdptr]->flags = 0;
                skb = NULL;
@@ -1299,11 +1541,8 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
        struct sk_buff *skb;
        u32 txlen, tx_blocks = 0, tries, len;
        u32 block_retry_cnt = 0;
-
-       if (!adapter) {
-               pr_err("adapter structure is not valid\n");
-               return -1;
-       }
+       dma_addr_t buf_pa;
+       struct pcie_service_card *card = adapter->card;
 
        if (!firmware || !firmware_len) {
                dev_err(adapter->dev,
@@ -1325,7 +1564,6 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                ret = -ENOMEM;
                goto done;
        }
-       mwifiex_update_sk_buff_pa(skb);
 
        /* Perform firmware data transfer */
        do {
@@ -1400,6 +1638,9 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                        ret = -1;
                        goto done;
                }
+
+               MWIFIEX_SKB_PACB(skb, &buf_pa);
+
                /* Wait for the command done interrupt */
                do {
                        if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS,
@@ -1407,11 +1648,17 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                                dev_err(adapter->dev, "%s: Failed to read "
                                        "interrupt status during fw dnld.\n",
                                        __func__);
+                               pci_unmap_single(card->dev, buf_pa, skb->len,
+                                                PCI_DMA_TODEVICE);
                                ret = -1;
                                goto done;
                        }
                } while ((ireg_intr & CPU_INTR_DOOR_BELL) ==
                         CPU_INTR_DOOR_BELL);
+
+               pci_unmap_single(card->dev, buf_pa, skb->len,
+                                PCI_DMA_TODEVICE);
+
                offset += txlen;
        } while (true);
 
@@ -1594,39 +1841,40 @@ exit:
 static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
 {
        int ret;
-       u32 pcie_ireg = 0;
+       u32 pcie_ireg;
        unsigned long flags;
 
        spin_lock_irqsave(&adapter->int_lock, flags);
        /* Clear out unused interrupts */
-       adapter->int_status &= HOST_INTR_MASK;
+       pcie_ireg = adapter->int_status;
+       adapter->int_status = 0;
        spin_unlock_irqrestore(&adapter->int_lock, flags);
 
-       while (adapter->int_status & HOST_INTR_MASK) {
-               if (adapter->int_status & HOST_INTR_DNLD_DONE) {
-                       adapter->int_status &= ~HOST_INTR_DNLD_DONE;
-                       if (adapter->data_sent) {
-                               dev_dbg(adapter->dev, "info: DATA sent intr\n");
-                               adapter->data_sent = false;
-                       }
+       while (pcie_ireg & HOST_INTR_MASK) {
+               if (pcie_ireg & HOST_INTR_DNLD_DONE) {
+                       pcie_ireg &= ~HOST_INTR_DNLD_DONE;
+                       dev_dbg(adapter->dev, "info: TX DNLD Done\n");
+                       ret = mwifiex_pcie_send_data_complete(adapter);
+                       if (ret)
+                               return ret;
                }
-               if (adapter->int_status & HOST_INTR_UPLD_RDY) {
-                       adapter->int_status &= ~HOST_INTR_UPLD_RDY;
+               if (pcie_ireg & HOST_INTR_UPLD_RDY) {
+                       pcie_ireg &= ~HOST_INTR_UPLD_RDY;
                        dev_dbg(adapter->dev, "info: Rx DATA\n");
                        ret = mwifiex_pcie_process_recv_data(adapter);
                        if (ret)
                                return ret;
                }
-               if (adapter->int_status & HOST_INTR_EVENT_RDY) {
-                       adapter->int_status &= ~HOST_INTR_EVENT_RDY;
+               if (pcie_ireg & HOST_INTR_EVENT_RDY) {
+                       pcie_ireg &= ~HOST_INTR_EVENT_RDY;
                        dev_dbg(adapter->dev, "info: Rx EVENT\n");
                        ret = mwifiex_pcie_process_event_ready(adapter);
                        if (ret)
                                return ret;
                }
 
-               if (adapter->int_status & HOST_INTR_CMD_DONE) {
-                       adapter->int_status &= ~HOST_INTR_CMD_DONE;
+               if (pcie_ireg & HOST_INTR_CMD_DONE) {
+                       pcie_ireg &= ~HOST_INTR_CMD_DONE;
                        if (adapter->cmd_sent) {
                                dev_dbg(adapter->dev,
                                        "info: CMD sent Interrupt\n");
@@ -1654,8 +1902,6 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
                                                 "Write register failed\n");
                                        return -1;
                                }
-                               adapter->int_status |= pcie_ireg;
-                               adapter->int_status &= HOST_INTR_MASK;
                        }
 
                }
@@ -1687,7 +1933,7 @@ static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type,
        }
 
        if (type == MWIFIEX_TYPE_DATA)
-               return mwifiex_pcie_send_data(adapter, skb);
+               return mwifiex_pcie_send_data(adapter, skb, tx_param);
        else if (type == MWIFIEX_TYPE_CMD)
                return mwifiex_pcie_send_cmd(adapter, skb);
 
@@ -1739,6 +1985,7 @@ static int mwifiex_pcie_init(struct mwifiex_adapter *adapter)
        card->pci_mmap = pci_iomap(pdev, 0, 0);
        if (!card->pci_mmap) {
                dev_err(adapter->dev, "iomap(0) error\n");
+               ret = -EIO;
                goto err_iomap0;
        }
        ret = pci_request_region(pdev, 2, DRV_NAME);
@@ -1749,6 +1996,7 @@ static int mwifiex_pcie_init(struct mwifiex_adapter *adapter)
        card->pci_mmap1 = pci_iomap(pdev, 2, 0);
        if (!card->pci_mmap1) {
                dev_err(adapter->dev, "iomap(2) error\n");
+               ret = -EIO;
                goto err_iomap2;
        }
 
@@ -1814,15 +2062,8 @@ static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter)
        struct pcie_service_card *card = adapter->card;
        struct pci_dev *pdev = card->dev;
 
-       mwifiex_pcie_delete_sleep_cookie_buf(adapter);
-       mwifiex_pcie_delete_cmdrsp_buf(adapter);
-       mwifiex_pcie_delete_evtbd_ring(adapter);
-       mwifiex_pcie_delete_rxbd_ring(adapter);
-       mwifiex_pcie_delete_txbd_ring(adapter);
-       card->cmdrsp_buf = NULL;
-
-       dev_dbg(adapter->dev, "Clearing driver ready signature\n");
        if (user_rmmod) {
+               dev_dbg(adapter->dev, "Clearing driver ready signature\n");
                if (mwifiex_write_reg(adapter, REG_DRV_READY, 0x00000000))
                        dev_err(adapter->dev,
                                "Failed to write driver not-ready signature\n");
@@ -1879,6 +2120,13 @@ static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
        if (card) {
                dev_dbg(adapter->dev, "%s(): calling free_irq()\n", __func__);
                free_irq(card->dev->irq, card->dev);
+
+               mwifiex_pcie_delete_sleep_cookie_buf(adapter);
+               mwifiex_pcie_delete_cmdrsp_buf(adapter);
+               mwifiex_pcie_delete_evtbd_ring(adapter);
+               mwifiex_pcie_delete_rxbd_ring(adapter);
+               mwifiex_pcie_delete_txbd_ring(adapter);
+               card->cmdrsp_buf = NULL;
        }
 }
 
@@ -1900,6 +2148,8 @@ static struct mwifiex_if_ops pcie_ops = {
        .event_complete =               mwifiex_pcie_event_complete,
        .update_mp_end_port =           NULL,
        .cleanup_mpa_buf =              NULL,
+       .init_fw_port =                 mwifiex_pcie_init_fw_port,
+       .clean_pcie_ring =              mwifiex_clean_pcie_ring_buf,
 };
 
 /*
index 2f218f9a3fd3efea52e8038345a326bfb7341243..37eeb2ca6b298654adc603dc9be0e3deca27a9f1 100644 (file)
@@ -114,11 +114,12 @@ struct pcie_service_card {
        struct pci_dev *dev;
        struct mwifiex_adapter *adapter;
 
+       u8 txbd_flush;
        u32 txbd_wrptr;
        u32 txbd_rdptr;
        u32 txbd_ring_size;
        u8 *txbd_ring_vbase;
-       phys_addr_t txbd_ring_pbase;
+       dma_addr_t txbd_ring_pbase;
        struct mwifiex_pcie_buf_desc *txbd_ring[MWIFIEX_MAX_TXRX_BD];
        struct sk_buff *tx_buf_list[MWIFIEX_MAX_TXRX_BD];
 
@@ -126,7 +127,7 @@ struct pcie_service_card {
        u32 rxbd_rdptr;
        u32 rxbd_ring_size;
        u8 *rxbd_ring_vbase;
-       phys_addr_t rxbd_ring_pbase;
+       dma_addr_t rxbd_ring_pbase;
        struct mwifiex_pcie_buf_desc *rxbd_ring[MWIFIEX_MAX_TXRX_BD];
        struct sk_buff *rx_buf_list[MWIFIEX_MAX_TXRX_BD];
 
@@ -134,15 +135,39 @@ struct pcie_service_card {
        u32 evtbd_rdptr;
        u32 evtbd_ring_size;
        u8 *evtbd_ring_vbase;
-       phys_addr_t evtbd_ring_pbase;
+       dma_addr_t evtbd_ring_pbase;
        struct mwifiex_pcie_buf_desc *evtbd_ring[MWIFIEX_MAX_EVT_BD];
        struct sk_buff *evt_buf_list[MWIFIEX_MAX_EVT_BD];
 
        struct sk_buff *cmd_buf;
        struct sk_buff *cmdrsp_buf;
-       struct sk_buff *sleep_cookie;
+       u8 *sleep_cookie_vbase;
+       dma_addr_t sleep_cookie_pbase;
        void __iomem *pci_mmap;
        void __iomem *pci_mmap1;
 };
 
+static inline int
+mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr)
+{
+       if (((card->txbd_wrptr & MWIFIEX_TXBD_MASK) ==
+                       (rdptr & MWIFIEX_TXBD_MASK)) &&
+           ((card->txbd_wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) !=
+                       (rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND)))
+               return 1;
+
+       return 0;
+}
+
+static inline int
+mwifiex_pcie_txbd_not_full(struct pcie_service_card *card)
+{
+       if (((card->txbd_wrptr & MWIFIEX_TXBD_MASK) !=
+            (card->txbd_rdptr & MWIFIEX_TXBD_MASK)) ||
+           ((card->txbd_wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) !=
+            (card->txbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND)))
+               return 1;
+
+       return 0;
+}
 #endif /* _MWIFIEX_PCIE_H */
index 5a1c1d0e5599ab2689b8fee3d1e81d796dead8fc..e35b67a9e6a64a18b8dc4fb5570e0b1fc84d5c7e 100644 (file)
@@ -332,7 +332,7 @@ mwifiex_write_data_sync(struct mwifiex_adapter *adapter,
                        u8 *buffer, u32 pkt_len, u32 port)
 {
        struct sdio_mmc_card *card = adapter->card;
-       int ret = -1;
+       int ret;
        u8 blk_mode =
                (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE;
        u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1;
@@ -350,8 +350,7 @@ mwifiex_write_data_sync(struct mwifiex_adapter *adapter,
 
        sdio_claim_host(card->func);
 
-       if (!sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size))
-               ret = 0;
+       ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size);
 
        sdio_release_host(card->func);
 
@@ -365,7 +364,7 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer,
                                  u32 len, u32 port, u8 claim)
 {
        struct sdio_mmc_card *card = adapter->card;
-       int ret = -1;
+       int ret;
        u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE
                       : BLOCK_MODE;
        u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1;
@@ -376,8 +375,7 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer,
        if (claim)
                sdio_claim_host(card->func);
 
-       if (!sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size))
-               ret = 0;
+       ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size);
 
        if (claim)
                sdio_release_host(card->func);
index 5d87195390f863a9492aadaa9ea1d51d6ba8ae8e..c4607859d59d5a45fd58da58e8491e14be5275ea 100644 (file)
@@ -931,7 +931,6 @@ mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv,
        struct host_cmd_ds_pcie_details *host_spec =
                                        &cmd->params.pcie_host_spec;
        struct pcie_service_card *card = priv->adapter->card;
-       phys_addr_t *buf_pa;
 
        cmd->command = cpu_to_le16(HostCmd_CMD_PCIE_DESC_DETAILS);
        cmd->size = cpu_to_le16(sizeof(struct
@@ -953,10 +952,11 @@ mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv,
        host_spec->evtbd_addr_lo = (u32)(card->evtbd_ring_pbase);
        host_spec->evtbd_addr_hi = (u32)(((u64)card->evtbd_ring_pbase)>>32);
        host_spec->evtbd_count = MWIFIEX_MAX_EVT_BD;
-       if (card->sleep_cookie) {
-               buf_pa = MWIFIEX_SKB_PACB(card->sleep_cookie);
-               host_spec->sleep_cookie_addr_lo = (u32) *buf_pa;
-               host_spec->sleep_cookie_addr_hi = (u32) (((u64)*buf_pa) >> 32);
+       if (card->sleep_cookie_vbase) {
+               host_spec->sleep_cookie_addr_lo =
+                                               (u32)(card->sleep_cookie_pbase);
+               host_spec->sleep_cookie_addr_hi =
+                                (u32)(((u64)(card->sleep_cookie_pbase)) >> 32);
                dev_dbg(priv->adapter->dev, "sleep_cook_lo phy addr: 0x%x\n",
                        host_spec->sleep_cookie_addr_lo);
        }
index 65c12eb3e5e73ba8085dd6e57c332b5ca6e28a80..847056415ac9424736d6ca85b9627a61f19e4b3d 100644 (file)
@@ -935,9 +935,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
                                        / MWIFIEX_SDIO_BLOCK_SIZE)
                                       * MWIFIEX_SDIO_BLOCK_SIZE;
                adapter->curr_tx_buf_size = adapter->tx_buf_size;
-               dev_dbg(adapter->dev,
-                       "cmd: max_tx_buf_size=%d, tx_buf_size=%d\n",
-                       adapter->max_tx_buf_size, adapter->tx_buf_size);
+               dev_dbg(adapter->dev, "cmd: curr_tx_buf_size=%d\n",
+                       adapter->curr_tx_buf_size);
 
                if (adapter->if_ops.update_mp_end_port)
                        adapter->if_ops.update_mp_end_port(adapter,
index 8c80024c30ff6345399d53e7132bed310a6c12a6..296faec143657bde52c6304f429f4b0b231221a3 100644 (file)
@@ -117,14 +117,16 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
                dev_dbg(adapter->dev, "data: -EBUSY is returned\n");
                break;
        case -1:
-               adapter->data_sent = false;
+               if (adapter->iface_type != MWIFIEX_PCIE)
+                       adapter->data_sent = false;
                dev_err(adapter->dev, "mwifiex_write_data_async failed: 0x%X\n",
                        ret);
                adapter->dbg.num_tx_host_to_card_failure++;
                mwifiex_write_data_complete(adapter, skb, 0, ret);
                break;
        case -EINPROGRESS:
-               adapter->data_sent = false;
+               if (adapter->iface_type != MWIFIEX_PCIE)
+                       adapter->data_sent = false;
                break;
        case 0:
                mwifiex_write_data_complete(adapter, skb, 0, ret);
index 8dd72240f162d9786e4fac374fdf7144307b0ee8..6e76a15a89501a7aa6cacde72aee43cd10612f1d 100644 (file)
@@ -219,6 +219,7 @@ void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config)
        config->rts_threshold = 0x7FFF;
        config->frag_threshold = 0x7FFF;
        config->retry_limit = 0x7F;
+       config->qos_info = 0xFF;
 }
 
 /* This function parses BSS related parameters from structure
@@ -297,6 +298,38 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
        return;
 }
 
+/* This function parses WMM related parameters from cfg80211_ap_settings
+ * structure and updates bss_config structure.
+ */
+void
+mwifiex_set_wmm_params(struct mwifiex_private *priv,
+                      struct mwifiex_uap_bss_param *bss_cfg,
+                      struct cfg80211_ap_settings *params)
+{
+       const u8 *vendor_ie;
+       struct ieee_types_header *wmm_ie;
+       u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02};
+
+       vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+                                           WLAN_OUI_TYPE_MICROSOFT_WMM,
+                                           params->beacon.tail,
+                                           params->beacon.tail_len);
+       if (vendor_ie) {
+               wmm_ie = (struct ieee_types_header *)vendor_ie;
+               memcpy(&bss_cfg->wmm_info, wmm_ie + 1,
+                      sizeof(bss_cfg->wmm_info));
+               priv->wmm_enabled = 1;
+       } else {
+               memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info));
+               memcpy(&bss_cfg->wmm_info.oui, wmm_oui, sizeof(wmm_oui));
+               bss_cfg->wmm_info.subtype = MWIFIEX_WMM_SUBTYPE;
+               bss_cfg->wmm_info.version = MWIFIEX_WMM_VERSION;
+               priv->wmm_enabled = 0;
+       }
+
+       bss_cfg->qos_info = 0x00;
+       return;
+}
 /* This function parses BSS related parameters from structure
  * and prepares TLVs specific to WEP encryption.
  * These TLVs are appended to command buffer.
@@ -354,6 +387,7 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
        struct host_cmd_tlv_rates *tlv_rates;
        struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer;
        struct mwifiex_ie_types_htcap *htcap;
+       struct mwifiex_ie_types_wmmcap *wmm_cap;
        struct mwifiex_uap_bss_param *bss_cfg = cmd_buf;
        int i;
        u16 cmd_size = *param_size;
@@ -507,6 +541,16 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
                tlv += sizeof(struct mwifiex_ie_types_htcap);
        }
 
+       if (bss_cfg->wmm_info.qos_info != 0xFF) {
+               wmm_cap = (struct mwifiex_ie_types_wmmcap *)tlv;
+               wmm_cap->header.type = cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC);
+               wmm_cap->header.len = cpu_to_le16(sizeof(wmm_cap->wmm_info));
+               memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info,
+                      sizeof(wmm_cap->wmm_info));
+               cmd_size += sizeof(struct mwifiex_ie_types_wmmcap);
+               tlv += sizeof(struct mwifiex_ie_types_wmmcap);
+       }
+
        if (bss_cfg->sta_ao_timer) {
                ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
                ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER);
index 63ac9f2d11ae2fb15af3324175b522f36a2792bc..f90fe21e5bfda65d5bc2a0d6fc6638ac8765e72e 100644 (file)
@@ -672,7 +672,7 @@ static int mwifiex_write_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf,
                           *len, &actual_length, timeout);
        if (ret) {
                dev_err(adapter->dev, "usb_bulk_msg for tx failed: %d\n", ret);
-               ret = -1;
+               return ret;
        }
 
        *len = actual_length;
@@ -691,7 +691,7 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf,
                           *len, &actual_length, timeout);
        if (ret) {
                dev_err(adapter->dev, "usb_bulk_msg for rx failed: %d\n", ret);
-               ret = -1;
+               return ret;
        }
 
        *len = actual_length;
@@ -786,21 +786,6 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
        return 0;
 }
 
-/* This function reads one block of firmware data. */
-static int mwifiex_get_fw_data(struct mwifiex_adapter *adapter,
-                              u32 offset, u32 len, u8 *buf)
-{
-       if (!buf || !len)
-               return -1;
-
-       if (offset + len > adapter->firmware->size)
-               return -1;
-
-       memcpy(buf, adapter->firmware->data + offset, len);
-
-       return 0;
-}
-
 static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                                    struct mwifiex_fw_image *fw)
 {
@@ -836,23 +821,14 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                        dlen = 0;
                } else {
                        /* copy the header of the fw_data to get the length */
-                       if (firmware)
-                               memcpy(&fwdata->fw_hdr, &firmware[tlen],
-                                      sizeof(struct fw_header));
-                       else
-                               mwifiex_get_fw_data(adapter, tlen,
-                                                   sizeof(struct fw_header),
-                                                   (u8 *)&fwdata->fw_hdr);
+                       memcpy(&fwdata->fw_hdr, &firmware[tlen],
+                              sizeof(struct fw_header));
 
                        dlen = le32_to_cpu(fwdata->fw_hdr.data_len);
                        dnld_cmd = le32_to_cpu(fwdata->fw_hdr.dnld_cmd);
                        tlen += sizeof(struct fw_header);
 
-                       if (firmware)
-                               memcpy(fwdata->data, &firmware[tlen], dlen);
-                       else
-                               mwifiex_get_fw_data(adapter, tlen, dlen,
-                                                   (u8 *)fwdata->data);
+                       memcpy(fwdata->data, &firmware[tlen], dlen);
 
                        fwdata->seq_num = cpu_to_le32(fw_seqnum);
                        tlen += dlen;
index 0982375ba3b14c6fbedee1cc6b74a0d2b6eaa9f9..21553976b550ff8c3ab925e58a4f508fa0845033 100644 (file)
@@ -91,7 +91,7 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv,
                memcpy(info->packets_out,
                       priv->wmm.packets_out,
                       sizeof(priv->wmm.packets_out));
-               info->max_tx_buf_size = (u32) adapter->max_tx_buf_size;
+               info->curr_tx_buf_size = (u32) adapter->curr_tx_buf_size;
                info->tx_buf_size = (u32) adapter->tx_buf_size;
                info->rx_tbl_num = mwifiex_get_rx_reorder_tbl(priv,
                                                              info->rx_tbl);
index f6d36b9654a03fa1e23a7f867ac0b6eec62dee14..cb2d0582bd363a42a55c23281eab163fdb05856e 100644 (file)
 
 static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb)
 {
-       return (struct mwifiex_rxinfo *)(skb->cb + sizeof(phys_addr_t));
+       return (struct mwifiex_rxinfo *)(skb->cb + sizeof(dma_addr_t));
 }
 
 static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb)
 {
-       return (struct mwifiex_txinfo *)(skb->cb + sizeof(phys_addr_t));
+       return (struct mwifiex_txinfo *)(skb->cb + sizeof(dma_addr_t));
 }
 
-static inline phys_addr_t *MWIFIEX_SKB_PACB(struct sk_buff *skb)
+static inline void MWIFIEX_SKB_PACB(struct sk_buff *skb, dma_addr_t *buf_pa)
 {
-       return (phys_addr_t *)skb->cb;
+       memcpy(buf_pa, skb->cb, sizeof(dma_addr_t));
 }
 #endif /* !_MWIFIEX_UTIL_H_ */
index 818f871ae987fc4944741562e18fca47b2fdd156..135d96df2063a976ca107841b6d4f494abb1702a 100644 (file)
@@ -568,6 +568,8 @@ mwifiex_clean_txrx(struct mwifiex_private *priv)
        mwifiex_wmm_delete_all_ralist(priv);
        memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid));
 
+       if (priv->adapter->if_ops.clean_pcie_ring)
+               priv->adapter->if_ops.clean_pcie_ring(priv->adapter);
        spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
 }
 
@@ -1206,13 +1208,15 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv,
                                       ra_list_flags);
                break;
        case -1:
-               adapter->data_sent = false;
+               if (adapter->iface_type != MWIFIEX_PCIE)
+                       adapter->data_sent = false;
                dev_err(adapter->dev, "host_to_card failed: %#x\n", ret);
                adapter->dbg.num_tx_host_to_card_failure++;
                mwifiex_write_data_complete(adapter, skb, 0, ret);
                break;
        case -EINPROGRESS:
-               adapter->data_sent = false;
+               if (adapter->iface_type != MWIFIEX_PCIE)
+                       adapter->data_sent = false;
        default:
                break;
        }
index a00a03ea4ec99dc90a71520e1b56daa462632b38..511f434bb4ed9403a560b18dbc4b83a3a89c4cd4 100644 (file)
@@ -101,6 +101,18 @@ MODULE_PARM_DESC(ap_mode_default,
 #define MWL8K_MAX_TX_QUEUES    (MWL8K_TX_WMM_QUEUES + MWL8K_MAX_AMPDU_QUEUES)
 #define mwl8k_tx_queues(priv)  (MWL8K_TX_WMM_QUEUES + (priv)->num_ampdu_queues)
 
+/* txpriorities are mapped with hw queues.
+ * Each hw queue has a txpriority.
+ */
+#define TOTAL_HW_TX_QUEUES     8
+
+/* Each HW queue can have one AMPDU stream.
+ * But, because one of the hw queue is reserved,
+ * maximum AMPDU queues that can be created are
+ * one short of total tx queues.
+ */
+#define MWL8K_NUM_AMPDU_STREAMS        (TOTAL_HW_TX_QUEUES - 1)
+
 struct rxd_ops {
        int rxd_size;
        void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr);
@@ -160,7 +172,6 @@ struct mwl8k_ampdu_stream {
        u8 tid;
        u8 state;
        u8 idx;
-       u8 txq_idx; /* index of this stream in priv->txq */
 };
 
 struct mwl8k_priv {
@@ -202,6 +213,8 @@ struct mwl8k_priv {
        int fw_mutex_depth;
        struct completion *hostcmd_wait;
 
+       atomic_t watchdog_event_pending;
+
        /* lock held over TX and TX reap */
        spinlock_t tx_lock;
 
@@ -272,6 +285,9 @@ struct mwl8k_priv {
        char *fw_pref;
        char *fw_alt;
        struct completion firmware_loading_complete;
+
+       /* bitmap of running BSSes */
+       u32 running_bsses;
 };
 
 #define MAX_WEP_KEY_LEN         13
@@ -1516,6 +1532,9 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
                        return -EBUSY;
        }
 
+       if (atomic_read(&priv->watchdog_event_pending))
+               return 0;
+
        /*
         * The TX queues are stopped at this point, so this test
         * doesn't need to take ->tx_lock.
@@ -1537,6 +1556,14 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
                spin_unlock_bh(&priv->tx_lock);
                timeout = wait_for_completion_timeout(&tx_wait,
                            msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS));
+
+               if (atomic_read(&priv->watchdog_event_pending)) {
+                       spin_lock_bh(&priv->tx_lock);
+                       priv->tx_wait = NULL;
+                       spin_unlock_bh(&priv->tx_lock);
+                       return 0;
+               }
+
                spin_lock_bh(&priv->tx_lock);
 
                if (timeout) {
@@ -1564,6 +1591,7 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
 
                rc = -ETIMEDOUT;
        }
+       priv->tx_wait = NULL;
        spin_unlock_bh(&priv->tx_lock);
 
        return rc;
@@ -1734,14 +1762,13 @@ mwl8k_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid)
        struct mwl8k_priv *priv = hw->priv;
        int i;
 
-       for (i = 0; i < priv->num_ampdu_queues; i++) {
+       for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) {
                stream = &priv->ampdu[i];
                if (stream->state == AMPDU_NO_STREAM) {
                        stream->sta = sta;
                        stream->state = AMPDU_STREAM_NEW;
                        stream->tid = tid;
                        stream->idx = i;
-                       stream->txq_idx = MWL8K_TX_WMM_QUEUES + i;
                        wiphy_debug(hw->wiphy, "Added a new stream for %pM %d",
                                    sta->addr, tid);
                        return stream;
@@ -1782,7 +1809,7 @@ mwl8k_lookup_stream(struct ieee80211_hw *hw, u8 *addr, u8 tid)
        struct mwl8k_priv *priv = hw->priv;
        int i;
 
-       for (i = 0 ; i < priv->num_ampdu_queues; i++) {
+       for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) {
                struct mwl8k_ampdu_stream *stream;
                stream = &priv->ampdu[i];
                if (stream->state == AMPDU_NO_STREAM)
@@ -1829,6 +1856,13 @@ static inline void mwl8k_tx_count_packet(struct ieee80211_sta *sta, u8 tid)
                tx_stats->pkts++;
 }
 
+/* The hardware ampdu queues start from 5.
+ * txpriorities for ampdu queues are
+ * 5 6 7 0 1 2 3 4 ie., queue 5 is highest
+ * and queue 3 is lowest (queue 4 is reserved)
+ */
+#define BA_QUEUE               5
+
 static void
 mwl8k_txq_xmit(struct ieee80211_hw *hw,
               int index,
@@ -1928,8 +1962,13 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw,
                stream = mwl8k_lookup_stream(hw, sta->addr, tid);
                if (stream != NULL) {
                        if (stream->state == AMPDU_STREAM_ACTIVE) {
-                               txpriority = stream->txq_idx;
-                               index = stream->txq_idx;
+                               WARN_ON(!(qos & MWL8K_QOS_ACK_POLICY_BLOCKACK));
+                               txpriority = (BA_QUEUE + stream->idx) %
+                                            TOTAL_HW_TX_QUEUES;
+                               if (stream->idx <= 1)
+                                       index = stream->idx +
+                                               MWL8K_TX_WMM_QUEUES;
+
                        } else if (stream->state == AMPDU_STREAM_NEW) {
                                /* We get here if the driver sends us packets
                                 * after we've initiated a stream, but before
@@ -1971,6 +2010,9 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw,
                        }
                }
                spin_unlock(&priv->stream_lock);
+       } else {
+               qos &= ~MWL8K_QOS_ACK_POLICY_MASK;
+               qos |= MWL8K_QOS_ACK_POLICY_NORMAL;
        }
 
        dma = pci_map_single(priv->pdev, skb->data,
@@ -2117,6 +2159,8 @@ static void mwl8k_fw_unlock(struct ieee80211_hw *hw)
        }
 }
 
+static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable,
+                              u32 bitmap);
 
 /*
  * Command processing.
@@ -2135,6 +2179,34 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
        int rc;
        unsigned long timeout = 0;
        u8 buf[32];
+       u32 bitmap = 0;
+
+       wiphy_dbg(hw->wiphy, "Posting %s [%d]\n",
+                 mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), cmd->macid);
+
+       /* Before posting firmware commands that could change the hardware
+        * characteristics, make sure that all BSSes are stopped temporary.
+        * Enable these stopped BSSes after completion of the commands
+        */
+
+       rc = mwl8k_fw_lock(hw);
+       if (rc)
+               return rc;
+
+       if (priv->ap_fw && priv->running_bsses) {
+               switch (le16_to_cpu(cmd->code)) {
+               case MWL8K_CMD_SET_RF_CHANNEL:
+               case MWL8K_CMD_RADIO_CONTROL:
+               case MWL8K_CMD_RF_TX_POWER:
+               case MWL8K_CMD_TX_POWER:
+               case MWL8K_CMD_RF_ANTENNA:
+               case MWL8K_CMD_RTS_THRESHOLD:
+               case MWL8K_CMD_MIMO_CONFIG:
+                       bitmap = priv->running_bsses;
+                       mwl8k_enable_bsses(hw, false, bitmap);
+                       break;
+               }
+       }
 
        cmd->result = (__force __le16) 0xffff;
        dma_size = le16_to_cpu(cmd->length);
@@ -2143,13 +2215,6 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
        if (pci_dma_mapping_error(priv->pdev, dma_addr))
                return -ENOMEM;
 
-       rc = mwl8k_fw_lock(hw);
-       if (rc) {
-               pci_unmap_single(priv->pdev, dma_addr, dma_size,
-                                               PCI_DMA_BIDIRECTIONAL);
-               return rc;
-       }
-
        priv->hostcmd_wait = &cmd_wait;
        iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
        iowrite32(MWL8K_H2A_INT_DOORBELL,
@@ -2162,7 +2227,6 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
 
        priv->hostcmd_wait = NULL;
 
-       mwl8k_fw_unlock(hw);
 
        pci_unmap_single(priv->pdev, dma_addr, dma_size,
                                        PCI_DMA_BIDIRECTIONAL);
@@ -2189,6 +2253,11 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
                                     ms);
        }
 
+       if (bitmap)
+               mwl8k_enable_bsses(hw, true, bitmap);
+
+       mwl8k_fw_unlock(hw);
+
        return rc;
 }
 
@@ -2450,7 +2519,7 @@ static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw)
                priv->hw_rev = cmd->hw_rev;
                mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));
                priv->ap_macids_supported = 0x000000ff;
-               priv->sta_macids_supported = 0x00000000;
+               priv->sta_macids_supported = 0x00000100;
                priv->num_ampdu_queues = le32_to_cpu(cmd->num_of_ampdu_queues);
                if (priv->num_ampdu_queues > MWL8K_MAX_AMPDU_QUEUES) {
                        wiphy_warn(hw->wiphy, "fw reported %d ampdu queues"
@@ -3469,7 +3538,10 @@ static int mwl8k_cmd_update_mac_addr(struct ieee80211_hw *hw,
        mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
        if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) {
                if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported))
-                       mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT;
+                       if (priv->ap_fw)
+                               mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
+                       else
+                               mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT;
                else
                        mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
        } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) {
@@ -3578,7 +3650,11 @@ static int mwl8k_cmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap)
        return rc;
 }
 
-#define INVALID_BA     0xAA
+#define MWL8K_WMM_QUEUE_NUMBER 3
+
+static void mwl8k_destroy_ba(struct ieee80211_hw *hw,
+                            u8 idx);
+
 static void mwl8k_watchdog_ba_events(struct work_struct *work)
 {
        int rc;
@@ -3586,24 +3662,41 @@ static void mwl8k_watchdog_ba_events(struct work_struct *work)
        struct mwl8k_ampdu_stream *streams;
        struct mwl8k_priv *priv =
                container_of(work, struct mwl8k_priv, watchdog_ba_handle);
+       struct ieee80211_hw *hw = priv->hw;
+       int i;
+       u32 status = 0;
+
+       mwl8k_fw_lock(hw);
 
        rc = mwl8k_cmd_get_watchdog_bitmap(priv->hw, &bitmap);
        if (rc)
-               return;
+               goto done;
 
-       if (bitmap == INVALID_BA)
-               return;
+       spin_lock(&priv->stream_lock);
 
        /* the bitmap is the hw queue number.  Map it to the ampdu queue. */
-       stream_index = bitmap - MWL8K_TX_WMM_QUEUES;
-
-       BUG_ON(stream_index >= priv->num_ampdu_queues);
-
-       streams = &priv->ampdu[stream_index];
-
-       if (streams->state == AMPDU_STREAM_ACTIVE)
-               ieee80211_stop_tx_ba_session(streams->sta, streams->tid);
+       for (i = 0; i < TOTAL_HW_TX_QUEUES; i++) {
+               if (bitmap & (1 << i)) {
+                       stream_index = (i + MWL8K_WMM_QUEUE_NUMBER) %
+                                      TOTAL_HW_TX_QUEUES;
+                       streams = &priv->ampdu[stream_index];
+                       if (streams->state == AMPDU_STREAM_ACTIVE) {
+                               ieee80211_stop_tx_ba_session(streams->sta,
+                                                            streams->tid);
+                               spin_unlock(&priv->stream_lock);
+                               mwl8k_destroy_ba(hw, stream_index);
+                               spin_lock(&priv->stream_lock);
+                       }
+               }
+       }
 
+       spin_unlock(&priv->stream_lock);
+done:
+       atomic_dec(&priv->watchdog_event_pending);
+       status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
+       iowrite32((status | MWL8K_A2H_INT_BA_WATCHDOG),
+                 priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
+       mwl8k_fw_unlock(hw);
        return;
 }
 
@@ -3620,8 +3713,16 @@ static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif, int enable)
 {
        struct mwl8k_cmd_bss_start *cmd;
+       struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
+       struct mwl8k_priv *priv = hw->priv;
        int rc;
 
+       if (enable && (priv->running_bsses & (1 << mwl8k_vif->macid)))
+               return 0;
+
+       if (!enable && !(priv->running_bsses & (1 << mwl8k_vif->macid)))
+               return 0;
+
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
@@ -3633,9 +3734,31 @@ static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,
        rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
        kfree(cmd);
 
+       if (!rc) {
+               if (enable)
+                       priv->running_bsses |= (1 << mwl8k_vif->macid);
+               else
+                       priv->running_bsses &= ~(1 << mwl8k_vif->macid);
+       }
        return rc;
 }
 
+static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, u32 bitmap)
+{
+       struct mwl8k_priv *priv = hw->priv;
+       struct mwl8k_vif *mwl8k_vif, *tmp_vif;
+       struct ieee80211_vif *vif;
+
+       list_for_each_entry_safe(mwl8k_vif, tmp_vif, &priv->vif_list, list) {
+               vif = mwl8k_vif->vif;
+
+               if (!(bitmap & (1 << mwl8k_vif->macid)))
+                       continue;
+
+               if (vif->type == NL80211_IFTYPE_AP)
+                       mwl8k_cmd_bss_start(hw, vif, enable);
+       }
+}
 /*
  * CMD_BASTREAM.
  */
@@ -3763,7 +3886,7 @@ mwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream,
 }
 
 static void mwl8k_destroy_ba(struct ieee80211_hw *hw,
-                            struct mwl8k_ampdu_stream *stream)
+                            u8 idx)
 {
        struct mwl8k_cmd_bastream *cmd;
 
@@ -3775,10 +3898,10 @@ static void mwl8k_destroy_ba(struct ieee80211_hw *hw,
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
        cmd->action = cpu_to_le32(MWL8K_BA_DESTROY);
 
-       cmd->destroy_params.ba_context = cpu_to_le32(stream->idx);
+       cmd->destroy_params.ba_context = cpu_to_le32(idx);
        mwl8k_post_cmd(hw, &cmd->header);
 
-       wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", stream->idx);
+       wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", idx);
 
        kfree(cmd);
 }
@@ -3875,7 +3998,30 @@ static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif, u8 *addr)
 {
        struct mwl8k_cmd_set_new_stn *cmd;
-       int rc;
+       struct mwl8k_priv *priv = hw->priv;
+       int rc, i;
+       u8 idx;
+
+       spin_lock(&priv->stream_lock);
+       /* Destroy any active ampdu streams for this sta */
+       for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) {
+               struct mwl8k_ampdu_stream *s;
+               s = &priv->ampdu[i];
+               if (s->state != AMPDU_NO_STREAM) {
+                       if (memcmp(s->sta->addr, addr, ETH_ALEN) == 0) {
+                               if (s->state == AMPDU_STREAM_ACTIVE) {
+                                       idx = s->idx;
+                                       spin_unlock(&priv->stream_lock);
+                                       mwl8k_destroy_ba(hw, idx);
+                                       spin_lock(&priv->stream_lock);
+                               } else if (s->state == AMPDU_STREAM_NEW) {
+                                       mwl8k_remove_stream(hw, s);
+                               }
+                       }
+               }
+       }
+
+       spin_unlock(&priv->stream_lock);
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
@@ -4119,8 +4265,9 @@ static int mwl8k_set_key(struct ieee80211_hw *hw,
        u8 encr_type;
        u8 *addr;
        struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
+       struct mwl8k_priv *priv = hw->priv;
 
-       if (vif->type == NL80211_IFTYPE_STATION)
+       if (vif->type == NL80211_IFTYPE_STATION && !priv->ap_fw)
                return -EOPNOTSUPP;
 
        if (sta == NULL)
@@ -4303,6 +4450,10 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id)
        }
 
        if (status & MWL8K_A2H_INT_BA_WATCHDOG) {
+               iowrite32(~MWL8K_A2H_INT_BA_WATCHDOG,
+                         priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
+
+               atomic_inc(&priv->watchdog_event_pending);
                status &= ~MWL8K_A2H_INT_BA_WATCHDOG;
                ieee80211_queue_work(hw, &priv->watchdog_ba_handle);
        }
@@ -4446,6 +4597,8 @@ static int mwl8k_start(struct ieee80211_hw *hw)
                priv->irq = -1;
                tasklet_disable(&priv->poll_tx_task);
                tasklet_disable(&priv->poll_rx_task);
+       } else {
+               ieee80211_wake_queues(hw);
        }
 
        return rc;
@@ -4520,12 +4673,18 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
                break;
        case NL80211_IFTYPE_STATION:
                if (priv->ap_fw && di->fw_image_sta) {
-                       /* we must load the sta fw to meet this request */
-                       if (!list_empty(&priv->vif_list))
-                               return -EBUSY;
-                       rc = mwl8k_reload_firmware(hw, di->fw_image_sta);
-                       if (rc)
-                               return rc;
+                       if (!list_empty(&priv->vif_list)) {
+                               wiphy_warn(hw->wiphy, "AP interface is running.\n"
+                                          "Adding STA interface for WDS");
+                       } else {
+                               /* we must load the sta fw to
+                                * meet this request.
+                                */
+                               rc = mwl8k_reload_firmware(hw,
+                                                          di->fw_image_sta);
+                               if (rc)
+                                       return rc;
+                       }
                }
                macids_supported = priv->sta_macids_supported;
                break;
@@ -4549,7 +4708,7 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
        /* Set the mac address.  */
        mwl8k_cmd_set_mac_addr(hw, vif, vif->addr);
 
-       if (priv->ap_fw)
+       if (vif->type == NL80211_IFTYPE_AP)
                mwl8k_cmd_set_new_stn_add_self(hw, vif);
 
        priv->macids_used |= 1 << mwl8k_vif->macid;
@@ -4574,7 +4733,7 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw,
        struct mwl8k_priv *priv = hw->priv;
        struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
 
-       if (priv->ap_fw)
+       if (vif->type == NL80211_IFTYPE_AP)
                mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr);
 
        mwl8k_cmd_del_mac_addr(hw, vif, vif->addr);
@@ -4648,9 +4807,11 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
        if (rc)
                goto out;
 
-       rc = mwl8k_cmd_set_rf_channel(hw, conf);
-       if (rc)
-               goto out;
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               rc = mwl8k_cmd_set_rf_channel(hw, conf);
+               if (rc)
+                       goto out;
+       }
 
        if (conf->power_level > 18)
                conf->power_level = 18;
@@ -4663,12 +4824,6 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
                                goto out;
                }
 
-               rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3);
-               if (rc)
-                       wiphy_warn(hw->wiphy, "failed to set # of RX antennas");
-               rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7);
-               if (rc)
-                       wiphy_warn(hw->wiphy, "failed to set # of TX antennas");
 
        } else {
                rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level);
@@ -4726,7 +4881,8 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                rcu_read_unlock();
        }
 
-       if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) {
+       if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc &&
+           !priv->ap_fw) {
                rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates);
                if (rc)
                        goto out;
@@ -4734,6 +4890,25 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                rc = mwl8k_cmd_use_fixed_rate_sta(hw);
                if (rc)
                        goto out;
+       } else {
+               if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc &&
+                   priv->ap_fw) {
+                       int idx;
+                       int rate;
+
+                       /* Use AP firmware specific rate command.
+                        */
+                       idx = ffs(vif->bss_conf.basic_rates);
+                       if (idx)
+                               idx--;
+
+                       if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+                               rate = mwl8k_rates_24[idx].hw_value;
+                       else
+                               rate = mwl8k_rates_50[idx].hw_value;
+
+                       mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
+               }
        }
 
        if (changed & BSS_CHANGED_ERP_PREAMBLE) {
@@ -4743,13 +4918,13 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        goto out;
        }
 
-       if (changed & BSS_CHANGED_ERP_SLOT) {
+       if ((changed & BSS_CHANGED_ERP_SLOT) && !priv->ap_fw)  {
                rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot);
                if (rc)
                        goto out;
        }
 
-       if (vif->bss_conf.assoc &&
+       if (vif->bss_conf.assoc && !priv->ap_fw &&
            (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT |
                        BSS_CHANGED_HT))) {
                rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates);
@@ -4829,11 +5004,9 @@ static void
 mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                       struct ieee80211_bss_conf *info, u32 changed)
 {
-       struct mwl8k_priv *priv = hw->priv;
-
-       if (!priv->ap_fw)
+       if (vif->type == NL80211_IFTYPE_STATION)
                mwl8k_bss_info_changed_sta(hw, vif, info, changed);
-       else
+       if (vif->type == NL80211_IFTYPE_AP)
                mwl8k_bss_info_changed_ap(hw, vif, info, changed);
 }
 
@@ -5094,7 +5267,7 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        int i, rc = 0;
        struct mwl8k_priv *priv = hw->priv;
        struct mwl8k_ampdu_stream *stream;
-       u8 *addr = sta->addr;
+       u8 *addr = sta->addr, idx;
        struct mwl8k_sta *sta_info = MWL8K_STA(sta);
 
        if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION))
@@ -5172,11 +5345,14 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                }
                ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
                break;
-       case IEEE80211_AMPDU_TX_STOP:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
                if (stream) {
                        if (stream->state == AMPDU_STREAM_ACTIVE) {
+                               idx = stream->idx;
                                spin_unlock(&priv->stream_lock);
-                               mwl8k_destroy_ba(hw, stream);
+                               mwl8k_destroy_ba(hw, idx);
                                spin_lock(&priv->stream_lock);
                        }
                        mwl8k_remove_stream(hw, stream);
@@ -5192,8 +5368,9 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                if (!rc)
                        stream->state = AMPDU_STREAM_ACTIVE;
                else {
+                       idx = stream->idx;
                        spin_unlock(&priv->stream_lock);
-                       mwl8k_destroy_ba(hw, stream);
+                       mwl8k_destroy_ba(hw, idx);
                        spin_lock(&priv->stream_lock);
                        wiphy_debug(hw->wiphy,
                                "Failed adding stream for sta %pM tid %d\n",
@@ -5256,7 +5433,7 @@ enum {
        MWL8366,
 };
 
-#define MWL8K_8366_AP_FW_API 2
+#define MWL8K_8366_AP_FW_API 3
 #define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw"
 #define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api)
 
@@ -5464,6 +5641,7 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)
                if (priv->rxd_ops == NULL) {
                        wiphy_err(hw->wiphy,
                                  "Driver does not have AP firmware image support for this hardware\n");
+                       rc = -ENOENT;
                        goto err_stop_firmware;
                }
        } else {
@@ -5473,6 +5651,7 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)
        priv->sniffer_enabled = false;
        priv->wmm_enabled = false;
        priv->pending_tx_pkts = 0;
+       atomic_set(&priv->watchdog_event_pending, 0);
 
        rc = mwl8k_rxq_init(hw, 0);
        if (rc)
@@ -5552,6 +5731,15 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)
                goto err_free_irq;
        }
 
+       /* Configure Antennas */
+       rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3);
+       if (rc)
+               wiphy_warn(hw->wiphy, "failed to set # of RX antennas");
+       rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7);
+       if (rc)
+               wiphy_warn(hw->wiphy, "failed to set # of TX antennas");
+
+
        /* Disable interrupts */
        iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
        free_irq(priv->pdev->irq, hw);
@@ -5639,6 +5827,7 @@ fail:
 
 static const struct ieee80211_iface_limit ap_if_limits[] = {
        { .max = 8,     .types = BIT(NL80211_IFTYPE_AP) },
+       { .max = 1,     .types = BIT(NL80211_IFTYPE_STATION) },
 };
 
 static const struct ieee80211_iface_combination ap_if_comb = {
@@ -5731,6 +5920,7 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
 
        if (priv->ap_macids_supported || priv->device_info->fw_image_ap) {
                hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
+               hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
                hw->wiphy->iface_combinations = &ap_if_comb;
                hw->wiphy->n_iface_combinations = 1;
        }
@@ -5809,6 +5999,7 @@ static int mwl8k_probe(struct pci_dev *pdev,
        priv->sram = pci_iomap(pdev, 0, 0x10000);
        if (priv->sram == NULL) {
                wiphy_err(hw->wiphy, "Cannot map device SRAM\n");
+               rc = -EIO;
                goto err_iounmap;
        }
 
@@ -5821,6 +6012,7 @@ static int mwl8k_probe(struct pci_dev *pdev,
                priv->regs = pci_iomap(pdev, 2, 0x10000);
                if (priv->regs == NULL) {
                        wiphy_err(hw->wiphy, "Cannot map device registers\n");
+                       rc = -EIO;
                        goto err_iounmap;
                }
        }
@@ -5851,6 +6043,8 @@ static int mwl8k_probe(struct pci_dev *pdev,
 
        priv->hw_restart_in_progress = false;
 
+       priv->running_bsses = 0;
+
        return rc;
 
 err_stop_firmware:
index 933e5d9419373529bc2b5428c34e9ea1c49f7331..57e3af8ebb4b39622450d1fbee0742b39ab5a0a3 100644 (file)
@@ -559,6 +559,7 @@ static int p54p_probe(struct pci_dev *pdev,
        mem_len = pci_resource_len(pdev, 0);
        if (mem_len < sizeof(struct p54p_csr)) {
                dev_err(&pdev->dev, "Too short PCI resources\n");
+               err = -ENODEV;
                goto err_disable_dev;
        }
 
@@ -568,8 +569,10 @@ static int p54p_probe(struct pci_dev *pdev,
                goto err_disable_dev;
        }
 
-       if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) ||
-           pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
+       err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (!err)
+               err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (err) {
                dev_err(&pdev->dev, "No suitable DMA available\n");
                goto err_free_reg;
        }
index 4e44b1af119aae45a1bf5956dd80fc283736bd78..1c22b81e6ef35e30f86afc994445ff66ef4f7c36 100644 (file)
@@ -1503,6 +1503,7 @@ static int prism54_get_auth(struct net_device *ndev,
                        case DOT11_AUTH_BOTH:
                        case DOT11_AUTH_SK:
                                param->value = IW_AUTH_ALG_SHARED_KEY;
+                               break;
                        case DOT11_AUTH_NONE:
                        default:
                                param->value = 0;
index 598ca1cafb958cc258867e8970bc2ed506472878..e7cf37f550d10dc70b3aa0b4ab9922325154d18c 100644 (file)
@@ -1107,12 +1107,15 @@ static int ray_get_essid(struct net_device *dev, struct iw_request_info *info,
                         union iwreq_data *wrqu, char *extra)
 {
        ray_dev_t *local = netdev_priv(dev);
+       UCHAR tmp[IW_ESSID_MAX_SIZE + 1];
 
        /* Get the essid that was set */
        memcpy(extra, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE);
+       memcpy(tmp, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE);
+       tmp[IW_ESSID_MAX_SIZE] = '\0';
 
        /* Push it out ! */
-       wrqu->essid.length = strlen(extra);
+       wrqu->essid.length = strlen(tmp);
        wrqu->essid.flags = 1;  /* active */
 
        return 0;
@@ -1842,6 +1845,8 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
        UCHAR tmp;
        UCHAR cmd;
        UCHAR status;
+       UCHAR memtmp[ESSID_SIZE + 1];
+
 
        if (dev == NULL)        /* Note that we want interrupts with dev->start == 0 */
                return IRQ_NONE;
@@ -1901,17 +1906,21 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
                        break;
                case CCS_START_NETWORK:
                case CCS_JOIN_NETWORK:
+                       memcpy(memtmp, local->sparm.b4.a_current_ess_id,
+                                                               ESSID_SIZE);
+                       memtmp[ESSID_SIZE] = '\0';
+
                        if (status == CCS_COMMAND_COMPLETE) {
                                if (readb
                                    (&pccs->var.start_network.net_initiated) ==
                                    1) {
                                        dev_dbg(&link->dev,
                                              "ray_cs interrupt network \"%s\" started\n",
-                                             local->sparm.b4.a_current_ess_id);
+                                             memtmp);
                                } else {
                                        dev_dbg(&link->dev,
                                              "ray_cs interrupt network \"%s\" joined\n",
-                                             local->sparm.b4.a_current_ess_id);
+                                             memtmp);
                                }
                                memcpy_fromio(&local->bss_id,
                                              pccs->var.start_network.bssid,
@@ -1939,12 +1948,12 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
                                if (status == CCS_START_NETWORK) {
                                        dev_dbg(&link->dev,
                                              "ray_cs interrupt network \"%s\" start failed\n",
-                                             local->sparm.b4.a_current_ess_id);
+                                             memtmp);
                                        local->timer.function = start_net;
                                } else {
                                        dev_dbg(&link->dev,
                                              "ray_cs interrupt network \"%s\" join failed\n",
-                                             local->sparm.b4.a_current_ess_id);
+                                             memtmp);
                                        local->timer.function = join_net;
                                }
                                add_timer(&local->timer);
index 197b4466a5d2a44378f998fdefa2c221ae6124c6..a658b4bc7da2fea78f2160b97907f20d547abde5 100644 (file)
@@ -80,7 +80,7 @@ static inline bool rt2800_is_305x_soc(struct rt2x00_dev *rt2x00dev)
            rt2x00_rf(rt2x00dev, RF3022))
                return true;
 
-       NOTICE(rt2x00dev, "Unknown RF chipset on rt305x\n");
+       WARNING(rt2x00dev, "Unknown RF chipset on rt305x\n");
        return false;
 }
 
@@ -1296,8 +1296,7 @@ void rt2800_config_filter(struct rt2x00_dev *rt2x00dev,
                           !(filter_flags & FIF_CONTROL));
        rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_PSPOLL,
                           !(filter_flags & FIF_PSPOLL));
-       rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_BA,
-                          !(filter_flags & FIF_CONTROL));
+       rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_BA, 0);
        rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_BAR,
                           !(filter_flags & FIF_CONTROL));
        rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_CNTL,
@@ -3866,6 +3865,400 @@ static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev,
        return rfcsr24;
 }
 
+static void rt2800_init_rfcsr_305x_soc(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_rfcsr_write(rt2x00dev, 0, 0x50);
+       rt2800_rfcsr_write(rt2x00dev, 1, 0x01);
+       rt2800_rfcsr_write(rt2x00dev, 2, 0xf7);
+       rt2800_rfcsr_write(rt2x00dev, 3, 0x75);
+       rt2800_rfcsr_write(rt2x00dev, 4, 0x40);
+       rt2800_rfcsr_write(rt2x00dev, 5, 0x03);
+       rt2800_rfcsr_write(rt2x00dev, 6, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 7, 0x50);
+       rt2800_rfcsr_write(rt2x00dev, 8, 0x39);
+       rt2800_rfcsr_write(rt2x00dev, 9, 0x0f);
+       rt2800_rfcsr_write(rt2x00dev, 10, 0x60);
+       rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
+       rt2800_rfcsr_write(rt2x00dev, 12, 0x75);
+       rt2800_rfcsr_write(rt2x00dev, 13, 0x75);
+       rt2800_rfcsr_write(rt2x00dev, 14, 0x90);
+       rt2800_rfcsr_write(rt2x00dev, 15, 0x58);
+       rt2800_rfcsr_write(rt2x00dev, 16, 0xb3);
+       rt2800_rfcsr_write(rt2x00dev, 17, 0x92);
+       rt2800_rfcsr_write(rt2x00dev, 18, 0x2c);
+       rt2800_rfcsr_write(rt2x00dev, 19, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 20, 0xba);
+       rt2800_rfcsr_write(rt2x00dev, 21, 0xdb);
+       rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 23, 0x31);
+       rt2800_rfcsr_write(rt2x00dev, 24, 0x08);
+       rt2800_rfcsr_write(rt2x00dev, 25, 0x01);
+       rt2800_rfcsr_write(rt2x00dev, 26, 0x25);
+       rt2800_rfcsr_write(rt2x00dev, 27, 0x23);
+       rt2800_rfcsr_write(rt2x00dev, 28, 0x13);
+       rt2800_rfcsr_write(rt2x00dev, 29, 0x83);
+       rt2800_rfcsr_write(rt2x00dev, 30, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 31, 0x00);
+}
+
+static void rt2800_init_rfcsr_30xx(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_rfcsr_write(rt2x00dev, 4, 0x40);
+       rt2800_rfcsr_write(rt2x00dev, 5, 0x03);
+       rt2800_rfcsr_write(rt2x00dev, 6, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 7, 0x60);
+       rt2800_rfcsr_write(rt2x00dev, 9, 0x0f);
+       rt2800_rfcsr_write(rt2x00dev, 10, 0x41);
+       rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
+       rt2800_rfcsr_write(rt2x00dev, 12, 0x7b);
+       rt2800_rfcsr_write(rt2x00dev, 14, 0x90);
+       rt2800_rfcsr_write(rt2x00dev, 15, 0x58);
+       rt2800_rfcsr_write(rt2x00dev, 16, 0xb3);
+       rt2800_rfcsr_write(rt2x00dev, 17, 0x92);
+       rt2800_rfcsr_write(rt2x00dev, 18, 0x2c);
+       rt2800_rfcsr_write(rt2x00dev, 19, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 20, 0xba);
+       rt2800_rfcsr_write(rt2x00dev, 21, 0xdb);
+       rt2800_rfcsr_write(rt2x00dev, 24, 0x16);
+       rt2800_rfcsr_write(rt2x00dev, 25, 0x01);
+       rt2800_rfcsr_write(rt2x00dev, 29, 0x1f);
+}
+
+static void rt2800_init_rfcsr_3290(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_rfcsr_write(rt2x00dev, 1, 0x0f);
+       rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 3, 0x08);
+       rt2800_rfcsr_write(rt2x00dev, 4, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 6, 0xa0);
+       rt2800_rfcsr_write(rt2x00dev, 8, 0xf3);
+       rt2800_rfcsr_write(rt2x00dev, 9, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 10, 0x53);
+       rt2800_rfcsr_write(rt2x00dev, 11, 0x4a);
+       rt2800_rfcsr_write(rt2x00dev, 12, 0x46);
+       rt2800_rfcsr_write(rt2x00dev, 13, 0x9f);
+       rt2800_rfcsr_write(rt2x00dev, 18, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 22, 0x20);
+       rt2800_rfcsr_write(rt2x00dev, 25, 0x83);
+       rt2800_rfcsr_write(rt2x00dev, 26, 0x82);
+       rt2800_rfcsr_write(rt2x00dev, 27, 0x09);
+       rt2800_rfcsr_write(rt2x00dev, 29, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 33, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 34, 0x05);
+       rt2800_rfcsr_write(rt2x00dev, 35, 0x12);
+       rt2800_rfcsr_write(rt2x00dev, 36, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 38, 0x85);
+       rt2800_rfcsr_write(rt2x00dev, 39, 0x1b);
+       rt2800_rfcsr_write(rt2x00dev, 40, 0x0b);
+       rt2800_rfcsr_write(rt2x00dev, 41, 0xbb);
+       rt2800_rfcsr_write(rt2x00dev, 42, 0xd5);
+       rt2800_rfcsr_write(rt2x00dev, 43, 0x7b);
+       rt2800_rfcsr_write(rt2x00dev, 44, 0x0e);
+       rt2800_rfcsr_write(rt2x00dev, 45, 0xa2);
+       rt2800_rfcsr_write(rt2x00dev, 46, 0x73);
+       rt2800_rfcsr_write(rt2x00dev, 47, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 48, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 49, 0x98);
+       rt2800_rfcsr_write(rt2x00dev, 52, 0x38);
+       rt2800_rfcsr_write(rt2x00dev, 53, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 54, 0x78);
+       rt2800_rfcsr_write(rt2x00dev, 55, 0x43);
+       rt2800_rfcsr_write(rt2x00dev, 56, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 57, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 58, 0x7f);
+       rt2800_rfcsr_write(rt2x00dev, 59, 0x09);
+       rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
+       rt2800_rfcsr_write(rt2x00dev, 61, 0xc1);
+}
+
+static void rt2800_init_rfcsr_3352(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_rfcsr_write(rt2x00dev, 0, 0xf0);
+       rt2800_rfcsr_write(rt2x00dev, 1, 0x23);
+       rt2800_rfcsr_write(rt2x00dev, 2, 0x50);
+       rt2800_rfcsr_write(rt2x00dev, 3, 0x18);
+       rt2800_rfcsr_write(rt2x00dev, 4, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 5, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 6, 0x33);
+       rt2800_rfcsr_write(rt2x00dev, 7, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 8, 0xf1);
+       rt2800_rfcsr_write(rt2x00dev, 9, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 10, 0xd2);
+       rt2800_rfcsr_write(rt2x00dev, 11, 0x42);
+       rt2800_rfcsr_write(rt2x00dev, 12, 0x1c);
+       rt2800_rfcsr_write(rt2x00dev, 13, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 14, 0x5a);
+       rt2800_rfcsr_write(rt2x00dev, 15, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 16, 0x01);
+       rt2800_rfcsr_write(rt2x00dev, 18, 0x45);
+       rt2800_rfcsr_write(rt2x00dev, 19, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 20, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 21, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 23, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 24, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 25, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 26, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 27, 0x03);
+       rt2800_rfcsr_write(rt2x00dev, 28, 0x03);
+       rt2800_rfcsr_write(rt2x00dev, 29, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 33, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 34, 0x01);
+       rt2800_rfcsr_write(rt2x00dev, 35, 0x03);
+       rt2800_rfcsr_write(rt2x00dev, 36, 0xbd);
+       rt2800_rfcsr_write(rt2x00dev, 37, 0x3c);
+       rt2800_rfcsr_write(rt2x00dev, 38, 0x5f);
+       rt2800_rfcsr_write(rt2x00dev, 39, 0xc5);
+       rt2800_rfcsr_write(rt2x00dev, 40, 0x33);
+       rt2800_rfcsr_write(rt2x00dev, 41, 0x5b);
+       rt2800_rfcsr_write(rt2x00dev, 42, 0x5b);
+       rt2800_rfcsr_write(rt2x00dev, 43, 0xdb);
+       rt2800_rfcsr_write(rt2x00dev, 44, 0xdb);
+       rt2800_rfcsr_write(rt2x00dev, 45, 0xdb);
+       rt2800_rfcsr_write(rt2x00dev, 46, 0xdd);
+       rt2800_rfcsr_write(rt2x00dev, 47, 0x0d);
+       rt2800_rfcsr_write(rt2x00dev, 48, 0x14);
+       rt2800_rfcsr_write(rt2x00dev, 49, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 50, 0x2d);
+       rt2800_rfcsr_write(rt2x00dev, 51, 0x7f);
+       rt2800_rfcsr_write(rt2x00dev, 52, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 53, 0x52);
+       rt2800_rfcsr_write(rt2x00dev, 54, 0x1b);
+       rt2800_rfcsr_write(rt2x00dev, 55, 0x7f);
+       rt2800_rfcsr_write(rt2x00dev, 56, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 57, 0x52);
+       rt2800_rfcsr_write(rt2x00dev, 58, 0x1b);
+       rt2800_rfcsr_write(rt2x00dev, 59, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 60, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 61, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 62, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 63, 0x00);
+}
+
+static void rt2800_init_rfcsr_3390(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_rfcsr_write(rt2x00dev, 0, 0xa0);
+       rt2800_rfcsr_write(rt2x00dev, 1, 0xe1);
+       rt2800_rfcsr_write(rt2x00dev, 2, 0xf1);
+       rt2800_rfcsr_write(rt2x00dev, 3, 0x62);
+       rt2800_rfcsr_write(rt2x00dev, 4, 0x40);
+       rt2800_rfcsr_write(rt2x00dev, 5, 0x8b);
+       rt2800_rfcsr_write(rt2x00dev, 6, 0x42);
+       rt2800_rfcsr_write(rt2x00dev, 7, 0x34);
+       rt2800_rfcsr_write(rt2x00dev, 8, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 9, 0xc0);
+       rt2800_rfcsr_write(rt2x00dev, 10, 0x61);
+       rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
+       rt2800_rfcsr_write(rt2x00dev, 12, 0x3b);
+       rt2800_rfcsr_write(rt2x00dev, 13, 0xe0);
+       rt2800_rfcsr_write(rt2x00dev, 14, 0x90);
+       rt2800_rfcsr_write(rt2x00dev, 15, 0x53);
+       rt2800_rfcsr_write(rt2x00dev, 16, 0xe0);
+       rt2800_rfcsr_write(rt2x00dev, 17, 0x94);
+       rt2800_rfcsr_write(rt2x00dev, 18, 0x5c);
+       rt2800_rfcsr_write(rt2x00dev, 19, 0x4a);
+       rt2800_rfcsr_write(rt2x00dev, 20, 0xb2);
+       rt2800_rfcsr_write(rt2x00dev, 21, 0xf6);
+       rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 23, 0x14);
+       rt2800_rfcsr_write(rt2x00dev, 24, 0x08);
+       rt2800_rfcsr_write(rt2x00dev, 25, 0x3d);
+       rt2800_rfcsr_write(rt2x00dev, 26, 0x85);
+       rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 28, 0x41);
+       rt2800_rfcsr_write(rt2x00dev, 29, 0x8f);
+       rt2800_rfcsr_write(rt2x00dev, 30, 0x20);
+       rt2800_rfcsr_write(rt2x00dev, 31, 0x0f);
+}
+
+static void rt2800_init_rfcsr_3572(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_rfcsr_write(rt2x00dev, 0, 0x70);
+       rt2800_rfcsr_write(rt2x00dev, 1, 0x81);
+       rt2800_rfcsr_write(rt2x00dev, 2, 0xf1);
+       rt2800_rfcsr_write(rt2x00dev, 3, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 4, 0x4c);
+       rt2800_rfcsr_write(rt2x00dev, 5, 0x05);
+       rt2800_rfcsr_write(rt2x00dev, 6, 0x4a);
+       rt2800_rfcsr_write(rt2x00dev, 7, 0xd8);
+       rt2800_rfcsr_write(rt2x00dev, 9, 0xc3);
+       rt2800_rfcsr_write(rt2x00dev, 10, 0xf1);
+       rt2800_rfcsr_write(rt2x00dev, 11, 0xb9);
+       rt2800_rfcsr_write(rt2x00dev, 12, 0x70);
+       rt2800_rfcsr_write(rt2x00dev, 13, 0x65);
+       rt2800_rfcsr_write(rt2x00dev, 14, 0xa0);
+       rt2800_rfcsr_write(rt2x00dev, 15, 0x53);
+       rt2800_rfcsr_write(rt2x00dev, 16, 0x4c);
+       rt2800_rfcsr_write(rt2x00dev, 17, 0x23);
+       rt2800_rfcsr_write(rt2x00dev, 18, 0xac);
+       rt2800_rfcsr_write(rt2x00dev, 19, 0x93);
+       rt2800_rfcsr_write(rt2x00dev, 20, 0xb3);
+       rt2800_rfcsr_write(rt2x00dev, 21, 0xd0);
+       rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 23, 0x3c);
+       rt2800_rfcsr_write(rt2x00dev, 24, 0x16);
+       rt2800_rfcsr_write(rt2x00dev, 25, 0x15);
+       rt2800_rfcsr_write(rt2x00dev, 26, 0x85);
+       rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 29, 0x9b);
+       rt2800_rfcsr_write(rt2x00dev, 30, 0x09);
+       rt2800_rfcsr_write(rt2x00dev, 31, 0x10);
+}
+
+static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_rfcsr_write(rt2x00dev, 1, 0x0f);
+       rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 3, 0x88);
+       rt2800_rfcsr_write(rt2x00dev, 5, 0x10);
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
+               rt2800_rfcsr_write(rt2x00dev, 6, 0xe0);
+       else
+               rt2800_rfcsr_write(rt2x00dev, 6, 0xa0);
+       rt2800_rfcsr_write(rt2x00dev, 7, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 10, 0x53);
+       rt2800_rfcsr_write(rt2x00dev, 11, 0x4a);
+       rt2800_rfcsr_write(rt2x00dev, 12, 0xc6);
+       rt2800_rfcsr_write(rt2x00dev, 13, 0x9f);
+       rt2800_rfcsr_write(rt2x00dev, 14, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 15, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 16, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 18, 0x03);
+       rt2800_rfcsr_write(rt2x00dev, 19, 0x00);
+
+       rt2800_rfcsr_write(rt2x00dev, 20, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 21, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 22, 0x20);
+       rt2800_rfcsr_write(rt2x00dev, 23, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 24, 0x00);
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
+               rt2800_rfcsr_write(rt2x00dev, 25, 0x80);
+       else
+               rt2800_rfcsr_write(rt2x00dev, 25, 0xc0);
+       rt2800_rfcsr_write(rt2x00dev, 26, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 27, 0x09);
+       rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 29, 0x10);
+
+       rt2800_rfcsr_write(rt2x00dev, 30, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 33, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 34, 0x07);
+       rt2800_rfcsr_write(rt2x00dev, 35, 0x12);
+       rt2800_rfcsr_write(rt2x00dev, 36, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 37, 0x08);
+       rt2800_rfcsr_write(rt2x00dev, 38, 0x85);
+       rt2800_rfcsr_write(rt2x00dev, 39, 0x1b);
+
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
+               rt2800_rfcsr_write(rt2x00dev, 40, 0x0b);
+       else
+               rt2800_rfcsr_write(rt2x00dev, 40, 0x4b);
+       rt2800_rfcsr_write(rt2x00dev, 41, 0xbb);
+       rt2800_rfcsr_write(rt2x00dev, 42, 0xd2);
+       rt2800_rfcsr_write(rt2x00dev, 43, 0x9a);
+       rt2800_rfcsr_write(rt2x00dev, 44, 0x0e);
+       rt2800_rfcsr_write(rt2x00dev, 45, 0xa2);
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
+               rt2800_rfcsr_write(rt2x00dev, 46, 0x73);
+       else
+               rt2800_rfcsr_write(rt2x00dev, 46, 0x7b);
+       rt2800_rfcsr_write(rt2x00dev, 47, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 48, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 49, 0x94);
+
+       rt2800_rfcsr_write(rt2x00dev, 52, 0x38);
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
+               rt2800_rfcsr_write(rt2x00dev, 53, 0x00);
+       else
+               rt2800_rfcsr_write(rt2x00dev, 53, 0x84);
+       rt2800_rfcsr_write(rt2x00dev, 54, 0x78);
+       rt2800_rfcsr_write(rt2x00dev, 55, 0x44);
+       rt2800_rfcsr_write(rt2x00dev, 56, 0x22);
+       rt2800_rfcsr_write(rt2x00dev, 57, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 58, 0x7f);
+       rt2800_rfcsr_write(rt2x00dev, 59, 0x63);
+
+       rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
+               rt2800_rfcsr_write(rt2x00dev, 61, 0xd1);
+       else
+               rt2800_rfcsr_write(rt2x00dev, 61, 0xdd);
+       rt2800_rfcsr_write(rt2x00dev, 62, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 63, 0x00);
+}
+
+static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_rfcsr_write(rt2x00dev, 1, 0x17);
+       rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 3, 0x88);
+       rt2800_rfcsr_write(rt2x00dev, 5, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 6, 0xe0);
+       rt2800_rfcsr_write(rt2x00dev, 7, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 10, 0x53);
+       rt2800_rfcsr_write(rt2x00dev, 11, 0x4a);
+       rt2800_rfcsr_write(rt2x00dev, 12, 0x46);
+       rt2800_rfcsr_write(rt2x00dev, 13, 0x9f);
+       rt2800_rfcsr_write(rt2x00dev, 14, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 15, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 16, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 18, 0x03);
+       rt2800_rfcsr_write(rt2x00dev, 19, 0x4d);
+       rt2800_rfcsr_write(rt2x00dev, 20, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 21, 0x8d);
+       rt2800_rfcsr_write(rt2x00dev, 22, 0x20);
+       rt2800_rfcsr_write(rt2x00dev, 23, 0x0b);
+       rt2800_rfcsr_write(rt2x00dev, 24, 0x44);
+       rt2800_rfcsr_write(rt2x00dev, 25, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 26, 0x82);
+       rt2800_rfcsr_write(rt2x00dev, 27, 0x09);
+       rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 29, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 32, 0x20);
+       rt2800_rfcsr_write(rt2x00dev, 33, 0xC0);
+       rt2800_rfcsr_write(rt2x00dev, 34, 0x07);
+       rt2800_rfcsr_write(rt2x00dev, 35, 0x12);
+       rt2800_rfcsr_write(rt2x00dev, 36, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 37, 0x08);
+       rt2800_rfcsr_write(rt2x00dev, 38, 0x89);
+       rt2800_rfcsr_write(rt2x00dev, 39, 0x1b);
+       rt2800_rfcsr_write(rt2x00dev, 40, 0x0f);
+       rt2800_rfcsr_write(rt2x00dev, 41, 0xbb);
+       rt2800_rfcsr_write(rt2x00dev, 42, 0xd5);
+       rt2800_rfcsr_write(rt2x00dev, 43, 0x9b);
+       rt2800_rfcsr_write(rt2x00dev, 44, 0x0e);
+       rt2800_rfcsr_write(rt2x00dev, 45, 0xa2);
+       rt2800_rfcsr_write(rt2x00dev, 46, 0x73);
+       rt2800_rfcsr_write(rt2x00dev, 47, 0x0c);
+       rt2800_rfcsr_write(rt2x00dev, 48, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 49, 0x94);
+       rt2800_rfcsr_write(rt2x00dev, 50, 0x94);
+       rt2800_rfcsr_write(rt2x00dev, 51, 0x3a);
+       rt2800_rfcsr_write(rt2x00dev, 52, 0x48);
+       rt2800_rfcsr_write(rt2x00dev, 53, 0x44);
+       rt2800_rfcsr_write(rt2x00dev, 54, 0x38);
+       rt2800_rfcsr_write(rt2x00dev, 55, 0x43);
+       rt2800_rfcsr_write(rt2x00dev, 56, 0xa1);
+       rt2800_rfcsr_write(rt2x00dev, 57, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 58, 0x39);
+       rt2800_rfcsr_write(rt2x00dev, 59, 0x07);
+       rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
+       rt2800_rfcsr_write(rt2x00dev, 61, 0x91);
+       rt2800_rfcsr_write(rt2x00dev, 62, 0x39);
+       rt2800_rfcsr_write(rt2x00dev, 63, 0x07);
+}
+
 static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
 {
        struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
@@ -3889,6 +4282,7 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
        /*
         * Init RF calibration.
         */
+
        if (rt2x00_rt(rt2x00dev, RT3290) ||
            rt2x00_rt(rt2x00dev, RT5390) ||
            rt2x00_rt(rt2x00dev, RT5392)) {
@@ -3907,379 +4301,35 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
                rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
        }
 
-       if (rt2x00_rt(rt2x00dev, RT3070) ||
-           rt2x00_rt(rt2x00dev, RT3071) ||
-           rt2x00_rt(rt2x00dev, RT3090)) {
-               rt2800_rfcsr_write(rt2x00dev, 4, 0x40);
-               rt2800_rfcsr_write(rt2x00dev, 5, 0x03);
-               rt2800_rfcsr_write(rt2x00dev, 6, 0x02);
-               rt2800_rfcsr_write(rt2x00dev, 7, 0x60);
-               rt2800_rfcsr_write(rt2x00dev, 9, 0x0f);
-               rt2800_rfcsr_write(rt2x00dev, 10, 0x41);
-               rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
-               rt2800_rfcsr_write(rt2x00dev, 12, 0x7b);
-               rt2800_rfcsr_write(rt2x00dev, 14, 0x90);
-               rt2800_rfcsr_write(rt2x00dev, 15, 0x58);
-               rt2800_rfcsr_write(rt2x00dev, 16, 0xb3);
-               rt2800_rfcsr_write(rt2x00dev, 17, 0x92);
-               rt2800_rfcsr_write(rt2x00dev, 18, 0x2c);
-               rt2800_rfcsr_write(rt2x00dev, 19, 0x02);
-               rt2800_rfcsr_write(rt2x00dev, 20, 0xba);
-               rt2800_rfcsr_write(rt2x00dev, 21, 0xdb);
-               rt2800_rfcsr_write(rt2x00dev, 24, 0x16);
-               rt2800_rfcsr_write(rt2x00dev, 25, 0x01);
-               rt2800_rfcsr_write(rt2x00dev, 29, 0x1f);
-       } else if (rt2x00_rt(rt2x00dev, RT3290)) {
-               rt2800_rfcsr_write(rt2x00dev, 1, 0x0f);
-               rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 3, 0x08);
-               rt2800_rfcsr_write(rt2x00dev, 4, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 6, 0xa0);
-               rt2800_rfcsr_write(rt2x00dev, 8, 0xf3);
-               rt2800_rfcsr_write(rt2x00dev, 9, 0x02);
-               rt2800_rfcsr_write(rt2x00dev, 10, 0x53);
-               rt2800_rfcsr_write(rt2x00dev, 11, 0x4a);
-               rt2800_rfcsr_write(rt2x00dev, 12, 0x46);
-               rt2800_rfcsr_write(rt2x00dev, 13, 0x9f);
-               rt2800_rfcsr_write(rt2x00dev, 18, 0x02);
-               rt2800_rfcsr_write(rt2x00dev, 22, 0x20);
-               rt2800_rfcsr_write(rt2x00dev, 25, 0x83);
-               rt2800_rfcsr_write(rt2x00dev, 26, 0x82);
-               rt2800_rfcsr_write(rt2x00dev, 27, 0x09);
-               rt2800_rfcsr_write(rt2x00dev, 29, 0x10);
-               rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
-               rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 33, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 34, 0x05);
-               rt2800_rfcsr_write(rt2x00dev, 35, 0x12);
-               rt2800_rfcsr_write(rt2x00dev, 36, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 38, 0x85);
-               rt2800_rfcsr_write(rt2x00dev, 39, 0x1b);
-               rt2800_rfcsr_write(rt2x00dev, 40, 0x0b);
-               rt2800_rfcsr_write(rt2x00dev, 41, 0xbb);
-               rt2800_rfcsr_write(rt2x00dev, 42, 0xd5);
-               rt2800_rfcsr_write(rt2x00dev, 43, 0x7b);
-               rt2800_rfcsr_write(rt2x00dev, 44, 0x0e);
-               rt2800_rfcsr_write(rt2x00dev, 45, 0xa2);
-               rt2800_rfcsr_write(rt2x00dev, 46, 0x73);
-               rt2800_rfcsr_write(rt2x00dev, 47, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 48, 0x10);
-               rt2800_rfcsr_write(rt2x00dev, 49, 0x98);
-               rt2800_rfcsr_write(rt2x00dev, 52, 0x38);
-               rt2800_rfcsr_write(rt2x00dev, 53, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 54, 0x78);
-               rt2800_rfcsr_write(rt2x00dev, 55, 0x43);
-               rt2800_rfcsr_write(rt2x00dev, 56, 0x02);
-               rt2800_rfcsr_write(rt2x00dev, 57, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 58, 0x7f);
-               rt2800_rfcsr_write(rt2x00dev, 59, 0x09);
-               rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
-               rt2800_rfcsr_write(rt2x00dev, 61, 0xc1);
-       } else if (rt2x00_rt(rt2x00dev, RT3390)) {
-               rt2800_rfcsr_write(rt2x00dev, 0, 0xa0);
-               rt2800_rfcsr_write(rt2x00dev, 1, 0xe1);
-               rt2800_rfcsr_write(rt2x00dev, 2, 0xf1);
-               rt2800_rfcsr_write(rt2x00dev, 3, 0x62);
-               rt2800_rfcsr_write(rt2x00dev, 4, 0x40);
-               rt2800_rfcsr_write(rt2x00dev, 5, 0x8b);
-               rt2800_rfcsr_write(rt2x00dev, 6, 0x42);
-               rt2800_rfcsr_write(rt2x00dev, 7, 0x34);
-               rt2800_rfcsr_write(rt2x00dev, 8, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 9, 0xc0);
-               rt2800_rfcsr_write(rt2x00dev, 10, 0x61);
-               rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
-               rt2800_rfcsr_write(rt2x00dev, 12, 0x3b);
-               rt2800_rfcsr_write(rt2x00dev, 13, 0xe0);
-               rt2800_rfcsr_write(rt2x00dev, 14, 0x90);
-               rt2800_rfcsr_write(rt2x00dev, 15, 0x53);
-               rt2800_rfcsr_write(rt2x00dev, 16, 0xe0);
-               rt2800_rfcsr_write(rt2x00dev, 17, 0x94);
-               rt2800_rfcsr_write(rt2x00dev, 18, 0x5c);
-               rt2800_rfcsr_write(rt2x00dev, 19, 0x4a);
-               rt2800_rfcsr_write(rt2x00dev, 20, 0xb2);
-               rt2800_rfcsr_write(rt2x00dev, 21, 0xf6);
-               rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 23, 0x14);
-               rt2800_rfcsr_write(rt2x00dev, 24, 0x08);
-               rt2800_rfcsr_write(rt2x00dev, 25, 0x3d);
-               rt2800_rfcsr_write(rt2x00dev, 26, 0x85);
-               rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 28, 0x41);
-               rt2800_rfcsr_write(rt2x00dev, 29, 0x8f);
-               rt2800_rfcsr_write(rt2x00dev, 30, 0x20);
-               rt2800_rfcsr_write(rt2x00dev, 31, 0x0f);
-       } else if (rt2x00_rt(rt2x00dev, RT3572)) {
-               rt2800_rfcsr_write(rt2x00dev, 0, 0x70);
-               rt2800_rfcsr_write(rt2x00dev, 1, 0x81);
-               rt2800_rfcsr_write(rt2x00dev, 2, 0xf1);
-               rt2800_rfcsr_write(rt2x00dev, 3, 0x02);
-               rt2800_rfcsr_write(rt2x00dev, 4, 0x4c);
-               rt2800_rfcsr_write(rt2x00dev, 5, 0x05);
-               rt2800_rfcsr_write(rt2x00dev, 6, 0x4a);
-               rt2800_rfcsr_write(rt2x00dev, 7, 0xd8);
-               rt2800_rfcsr_write(rt2x00dev, 9, 0xc3);
-               rt2800_rfcsr_write(rt2x00dev, 10, 0xf1);
-               rt2800_rfcsr_write(rt2x00dev, 11, 0xb9);
-               rt2800_rfcsr_write(rt2x00dev, 12, 0x70);
-               rt2800_rfcsr_write(rt2x00dev, 13, 0x65);
-               rt2800_rfcsr_write(rt2x00dev, 14, 0xa0);
-               rt2800_rfcsr_write(rt2x00dev, 15, 0x53);
-               rt2800_rfcsr_write(rt2x00dev, 16, 0x4c);
-               rt2800_rfcsr_write(rt2x00dev, 17, 0x23);
-               rt2800_rfcsr_write(rt2x00dev, 18, 0xac);
-               rt2800_rfcsr_write(rt2x00dev, 19, 0x93);
-               rt2800_rfcsr_write(rt2x00dev, 20, 0xb3);
-               rt2800_rfcsr_write(rt2x00dev, 21, 0xd0);
-               rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 23, 0x3c);
-               rt2800_rfcsr_write(rt2x00dev, 24, 0x16);
-               rt2800_rfcsr_write(rt2x00dev, 25, 0x15);
-               rt2800_rfcsr_write(rt2x00dev, 26, 0x85);
-               rt2800_rfcsr_write(rt2x00dev, 27, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 29, 0x9b);
-               rt2800_rfcsr_write(rt2x00dev, 30, 0x09);
-               rt2800_rfcsr_write(rt2x00dev, 31, 0x10);
-       } else if (rt2800_is_305x_soc(rt2x00dev)) {
-               rt2800_rfcsr_write(rt2x00dev, 0, 0x50);
-               rt2800_rfcsr_write(rt2x00dev, 1, 0x01);
-               rt2800_rfcsr_write(rt2x00dev, 2, 0xf7);
-               rt2800_rfcsr_write(rt2x00dev, 3, 0x75);
-               rt2800_rfcsr_write(rt2x00dev, 4, 0x40);
-               rt2800_rfcsr_write(rt2x00dev, 5, 0x03);
-               rt2800_rfcsr_write(rt2x00dev, 6, 0x02);
-               rt2800_rfcsr_write(rt2x00dev, 7, 0x50);
-               rt2800_rfcsr_write(rt2x00dev, 8, 0x39);
-               rt2800_rfcsr_write(rt2x00dev, 9, 0x0f);
-               rt2800_rfcsr_write(rt2x00dev, 10, 0x60);
-               rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
-               rt2800_rfcsr_write(rt2x00dev, 12, 0x75);
-               rt2800_rfcsr_write(rt2x00dev, 13, 0x75);
-               rt2800_rfcsr_write(rt2x00dev, 14, 0x90);
-               rt2800_rfcsr_write(rt2x00dev, 15, 0x58);
-               rt2800_rfcsr_write(rt2x00dev, 16, 0xb3);
-               rt2800_rfcsr_write(rt2x00dev, 17, 0x92);
-               rt2800_rfcsr_write(rt2x00dev, 18, 0x2c);
-               rt2800_rfcsr_write(rt2x00dev, 19, 0x02);
-               rt2800_rfcsr_write(rt2x00dev, 20, 0xba);
-               rt2800_rfcsr_write(rt2x00dev, 21, 0xdb);
-               rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 23, 0x31);
-               rt2800_rfcsr_write(rt2x00dev, 24, 0x08);
-               rt2800_rfcsr_write(rt2x00dev, 25, 0x01);
-               rt2800_rfcsr_write(rt2x00dev, 26, 0x25);
-               rt2800_rfcsr_write(rt2x00dev, 27, 0x23);
-               rt2800_rfcsr_write(rt2x00dev, 28, 0x13);
-               rt2800_rfcsr_write(rt2x00dev, 29, 0x83);
-               rt2800_rfcsr_write(rt2x00dev, 30, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 31, 0x00);
+       if (rt2800_is_305x_soc(rt2x00dev)) {
+               rt2800_init_rfcsr_305x_soc(rt2x00dev);
                return 0;
-       } else if (rt2x00_rt(rt2x00dev, RT3352)) {
-               rt2800_rfcsr_write(rt2x00dev, 0, 0xf0);
-               rt2800_rfcsr_write(rt2x00dev, 1, 0x23);
-               rt2800_rfcsr_write(rt2x00dev, 2, 0x50);
-               rt2800_rfcsr_write(rt2x00dev, 3, 0x18);
-               rt2800_rfcsr_write(rt2x00dev, 4, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 5, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 6, 0x33);
-               rt2800_rfcsr_write(rt2x00dev, 7, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 8, 0xf1);
-               rt2800_rfcsr_write(rt2x00dev, 9, 0x02);
-               rt2800_rfcsr_write(rt2x00dev, 10, 0xd2);
-               rt2800_rfcsr_write(rt2x00dev, 11, 0x42);
-               rt2800_rfcsr_write(rt2x00dev, 12, 0x1c);
-               rt2800_rfcsr_write(rt2x00dev, 13, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 14, 0x5a);
-               rt2800_rfcsr_write(rt2x00dev, 15, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 16, 0x01);
-               rt2800_rfcsr_write(rt2x00dev, 18, 0x45);
-               rt2800_rfcsr_write(rt2x00dev, 19, 0x02);
-               rt2800_rfcsr_write(rt2x00dev, 20, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 21, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 23, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 24, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 25, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 26, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 27, 0x03);
-               rt2800_rfcsr_write(rt2x00dev, 28, 0x03);
-               rt2800_rfcsr_write(rt2x00dev, 29, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
-               rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 33, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 34, 0x01);
-               rt2800_rfcsr_write(rt2x00dev, 35, 0x03);
-               rt2800_rfcsr_write(rt2x00dev, 36, 0xbd);
-               rt2800_rfcsr_write(rt2x00dev, 37, 0x3c);
-               rt2800_rfcsr_write(rt2x00dev, 38, 0x5f);
-               rt2800_rfcsr_write(rt2x00dev, 39, 0xc5);
-               rt2800_rfcsr_write(rt2x00dev, 40, 0x33);
-               rt2800_rfcsr_write(rt2x00dev, 41, 0x5b);
-               rt2800_rfcsr_write(rt2x00dev, 42, 0x5b);
-               rt2800_rfcsr_write(rt2x00dev, 43, 0xdb);
-               rt2800_rfcsr_write(rt2x00dev, 44, 0xdb);
-               rt2800_rfcsr_write(rt2x00dev, 45, 0xdb);
-               rt2800_rfcsr_write(rt2x00dev, 46, 0xdd);
-               rt2800_rfcsr_write(rt2x00dev, 47, 0x0d);
-               rt2800_rfcsr_write(rt2x00dev, 48, 0x14);
-               rt2800_rfcsr_write(rt2x00dev, 49, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 50, 0x2d);
-               rt2800_rfcsr_write(rt2x00dev, 51, 0x7f);
-               rt2800_rfcsr_write(rt2x00dev, 52, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 53, 0x52);
-               rt2800_rfcsr_write(rt2x00dev, 54, 0x1b);
-               rt2800_rfcsr_write(rt2x00dev, 55, 0x7f);
-               rt2800_rfcsr_write(rt2x00dev, 56, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 57, 0x52);
-               rt2800_rfcsr_write(rt2x00dev, 58, 0x1b);
-               rt2800_rfcsr_write(rt2x00dev, 59, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 60, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 61, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 62, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 63, 0x00);
-       } else if (rt2x00_rt(rt2x00dev, RT5390)) {
-               rt2800_rfcsr_write(rt2x00dev, 1, 0x0f);
-               rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 3, 0x88);
-               rt2800_rfcsr_write(rt2x00dev, 5, 0x10);
-               if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
-                       rt2800_rfcsr_write(rt2x00dev, 6, 0xe0);
-               else
-                       rt2800_rfcsr_write(rt2x00dev, 6, 0xa0);
-               rt2800_rfcsr_write(rt2x00dev, 7, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 10, 0x53);
-               rt2800_rfcsr_write(rt2x00dev, 11, 0x4a);
-               rt2800_rfcsr_write(rt2x00dev, 12, 0xc6);
-               rt2800_rfcsr_write(rt2x00dev, 13, 0x9f);
-               rt2800_rfcsr_write(rt2x00dev, 14, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 15, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 16, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 18, 0x03);
-               rt2800_rfcsr_write(rt2x00dev, 19, 0x00);
-
-               rt2800_rfcsr_write(rt2x00dev, 20, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 21, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 22, 0x20);
-               rt2800_rfcsr_write(rt2x00dev, 23, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 24, 0x00);
-               if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
-                       rt2800_rfcsr_write(rt2x00dev, 25, 0x80);
-               else
-                       rt2800_rfcsr_write(rt2x00dev, 25, 0xc0);
-               rt2800_rfcsr_write(rt2x00dev, 26, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 27, 0x09);
-               rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 29, 0x10);
-
-               rt2800_rfcsr_write(rt2x00dev, 30, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 33, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 34, 0x07);
-               rt2800_rfcsr_write(rt2x00dev, 35, 0x12);
-               rt2800_rfcsr_write(rt2x00dev, 36, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 37, 0x08);
-               rt2800_rfcsr_write(rt2x00dev, 38, 0x85);
-               rt2800_rfcsr_write(rt2x00dev, 39, 0x1b);
-
-               if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
-                       rt2800_rfcsr_write(rt2x00dev, 40, 0x0b);
-               else
-                       rt2800_rfcsr_write(rt2x00dev, 40, 0x4b);
-               rt2800_rfcsr_write(rt2x00dev, 41, 0xbb);
-               rt2800_rfcsr_write(rt2x00dev, 42, 0xd2);
-               rt2800_rfcsr_write(rt2x00dev, 43, 0x9a);
-               rt2800_rfcsr_write(rt2x00dev, 44, 0x0e);
-               rt2800_rfcsr_write(rt2x00dev, 45, 0xa2);
-               if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
-                       rt2800_rfcsr_write(rt2x00dev, 46, 0x73);
-               else
-                       rt2800_rfcsr_write(rt2x00dev, 46, 0x7b);
-               rt2800_rfcsr_write(rt2x00dev, 47, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 48, 0x10);
-               rt2800_rfcsr_write(rt2x00dev, 49, 0x94);
-
-               rt2800_rfcsr_write(rt2x00dev, 52, 0x38);
-               if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
-                       rt2800_rfcsr_write(rt2x00dev, 53, 0x00);
-               else
-                       rt2800_rfcsr_write(rt2x00dev, 53, 0x84);
-               rt2800_rfcsr_write(rt2x00dev, 54, 0x78);
-               rt2800_rfcsr_write(rt2x00dev, 55, 0x44);
-               rt2800_rfcsr_write(rt2x00dev, 56, 0x22);
-               rt2800_rfcsr_write(rt2x00dev, 57, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 58, 0x7f);
-               rt2800_rfcsr_write(rt2x00dev, 59, 0x63);
-
-               rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
-               if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
-                       rt2800_rfcsr_write(rt2x00dev, 61, 0xd1);
-               else
-                       rt2800_rfcsr_write(rt2x00dev, 61, 0xdd);
-               rt2800_rfcsr_write(rt2x00dev, 62, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 63, 0x00);
-       } else if (rt2x00_rt(rt2x00dev, RT5392)) {
-               rt2800_rfcsr_write(rt2x00dev, 1, 0x17);
-               rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 3, 0x88);
-               rt2800_rfcsr_write(rt2x00dev, 5, 0x10);
-               rt2800_rfcsr_write(rt2x00dev, 6, 0xe0);
-               rt2800_rfcsr_write(rt2x00dev, 7, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 10, 0x53);
-               rt2800_rfcsr_write(rt2x00dev, 11, 0x4a);
-               rt2800_rfcsr_write(rt2x00dev, 12, 0x46);
-               rt2800_rfcsr_write(rt2x00dev, 13, 0x9f);
-               rt2800_rfcsr_write(rt2x00dev, 14, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 15, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 16, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 18, 0x03);
-               rt2800_rfcsr_write(rt2x00dev, 19, 0x4d);
-               rt2800_rfcsr_write(rt2x00dev, 20, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 21, 0x8d);
-               rt2800_rfcsr_write(rt2x00dev, 22, 0x20);
-               rt2800_rfcsr_write(rt2x00dev, 23, 0x0b);
-               rt2800_rfcsr_write(rt2x00dev, 24, 0x44);
-               rt2800_rfcsr_write(rt2x00dev, 25, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 26, 0x82);
-               rt2800_rfcsr_write(rt2x00dev, 27, 0x09);
-               rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 29, 0x10);
-               rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
-               rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
-               rt2800_rfcsr_write(rt2x00dev, 32, 0x20);
-               rt2800_rfcsr_write(rt2x00dev, 33, 0xC0);
-               rt2800_rfcsr_write(rt2x00dev, 34, 0x07);
-               rt2800_rfcsr_write(rt2x00dev, 35, 0x12);
-               rt2800_rfcsr_write(rt2x00dev, 36, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 37, 0x08);
-               rt2800_rfcsr_write(rt2x00dev, 38, 0x89);
-               rt2800_rfcsr_write(rt2x00dev, 39, 0x1b);
-               rt2800_rfcsr_write(rt2x00dev, 40, 0x0f);
-               rt2800_rfcsr_write(rt2x00dev, 41, 0xbb);
-               rt2800_rfcsr_write(rt2x00dev, 42, 0xd5);
-               rt2800_rfcsr_write(rt2x00dev, 43, 0x9b);
-               rt2800_rfcsr_write(rt2x00dev, 44, 0x0e);
-               rt2800_rfcsr_write(rt2x00dev, 45, 0xa2);
-               rt2800_rfcsr_write(rt2x00dev, 46, 0x73);
-               rt2800_rfcsr_write(rt2x00dev, 47, 0x0c);
-               rt2800_rfcsr_write(rt2x00dev, 48, 0x10);
-               rt2800_rfcsr_write(rt2x00dev, 49, 0x94);
-               rt2800_rfcsr_write(rt2x00dev, 50, 0x94);
-               rt2800_rfcsr_write(rt2x00dev, 51, 0x3a);
-               rt2800_rfcsr_write(rt2x00dev, 52, 0x48);
-               rt2800_rfcsr_write(rt2x00dev, 53, 0x44);
-               rt2800_rfcsr_write(rt2x00dev, 54, 0x38);
-               rt2800_rfcsr_write(rt2x00dev, 55, 0x43);
-               rt2800_rfcsr_write(rt2x00dev, 56, 0xa1);
-               rt2800_rfcsr_write(rt2x00dev, 57, 0x00);
-               rt2800_rfcsr_write(rt2x00dev, 58, 0x39);
-               rt2800_rfcsr_write(rt2x00dev, 59, 0x07);
-               rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
-               rt2800_rfcsr_write(rt2x00dev, 61, 0x91);
-               rt2800_rfcsr_write(rt2x00dev, 62, 0x39);
-               rt2800_rfcsr_write(rt2x00dev, 63, 0x07);
+       }
+
+       switch (rt2x00dev->chip.rt) {
+       case RT3070:
+       case RT3071:
+       case RT3090:
+               rt2800_init_rfcsr_30xx(rt2x00dev);
+               break;
+       case RT3290:
+               rt2800_init_rfcsr_3290(rt2x00dev);
+               break;
+       case RT3352:
+               rt2800_init_rfcsr_3352(rt2x00dev);
+               break;
+       case RT3390:
+               rt2800_init_rfcsr_3390(rt2x00dev);
+               break;
+       case RT3572:
+               rt2800_init_rfcsr_3572(rt2x00dev);
+               break;
+       case RT5390:
+               rt2800_init_rfcsr_5390(rt2x00dev);
+               break;
+       case RT5392:
+               rt2800_init_rfcsr_5392(rt2x00dev);
+               break;
        }
 
        if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) {
@@ -4620,12 +4670,14 @@ static void rt2800_efuse_read(struct rt2x00_dev *rt2x00dev, unsigned int i)
        mutex_unlock(&rt2x00dev->csr_mutex);
 }
 
-void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
+int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
 {
        unsigned int i;
 
        for (i = 0; i < EEPROM_SIZE / sizeof(u16); i += 8)
                rt2800_efuse_read(rt2x00dev, i);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse);
 
@@ -4635,11 +4687,14 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
        u16 word;
        u8 *mac;
        u8 default_lna_gain;
+       int retval;
 
        /*
         * Read the EEPROM.
         */
-       rt2800_read_eeprom(rt2x00dev);
+       retval = rt2800_read_eeprom(rt2x00dev);
+       if (retval)
+               return retval;
 
        /*
         * Start validation of the data that has been read.
@@ -5090,8 +5145,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
            IEEE80211_HW_SUPPORTS_PS |
            IEEE80211_HW_PS_NULLFUNC_STACK |
            IEEE80211_HW_AMPDU_AGGREGATION |
-           IEEE80211_HW_REPORTS_TX_ACK_STATUS |
-           IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL;
+           IEEE80211_HW_REPORTS_TX_ACK_STATUS;
 
        /*
         * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices
@@ -5484,7 +5538,9 @@ int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        case IEEE80211_AMPDU_TX_START:
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                break;
-       case IEEE80211_AMPDU_TX_STOP:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                break;
        case IEEE80211_AMPDU_TX_OPERATIONAL:
index a128ceadcb3e733620c37fa03e49666cf80b2e09..6ec739466db46e681f86fc4f59a282949082cf38 100644 (file)
@@ -43,7 +43,7 @@ struct rt2800_ops {
                            const unsigned int offset,
                            const struct rt2x00_field32 field, u32 *reg);
 
-       void (*read_eeprom)(struct rt2x00_dev *rt2x00dev);
+       int (*read_eeprom)(struct rt2x00_dev *rt2x00dev);
        bool (*hwcrypt_disabled)(struct rt2x00_dev *rt2x00dev);
 
        int (*drv_write_firmware)(struct rt2x00_dev *rt2x00dev,
@@ -117,11 +117,11 @@ static inline int rt2800_regbusy_read(struct rt2x00_dev *rt2x00dev,
        return rt2800ops->regbusy_read(rt2x00dev, offset, field, reg);
 }
 
-static inline void rt2800_read_eeprom(struct rt2x00_dev *rt2x00dev)
+static inline int rt2800_read_eeprom(struct rt2x00_dev *rt2x00dev)
 {
        const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
 
-       rt2800ops->read_eeprom(rt2x00dev);
+       return rt2800ops->read_eeprom(rt2x00dev);
 }
 
 static inline bool rt2800_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev)
@@ -207,7 +207,7 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev);
 void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev);
 
 int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev);
-void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev);
+int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev);
 
 int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev);
 
index 9224d874bf24b62e017bfe8906f686b929a45db6..48a01aa21f1c94039d9485d5e635926d58a17750 100644 (file)
@@ -90,17 +90,22 @@ static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token)
 }
 
 #if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X)
-static void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
+static int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
 {
        void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE);
 
+       if (!base_addr)
+               return -ENOMEM;
+
        memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE);
 
        iounmap(base_addr);
+       return 0;
 }
 #else
-static inline void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
+static inline int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
 {
+       return -ENOMEM;
 }
 #endif /* CONFIG_RALINK_RT288X || CONFIG_RALINK_RT305X */
 
@@ -135,7 +140,7 @@ static void rt2800pci_eepromregister_write(struct eeprom_93cx6 *eeprom)
        rt2x00pci_register_write(rt2x00dev, E2PROM_CSR, reg);
 }
 
-static void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev)
+static int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev)
 {
        struct eeprom_93cx6 eeprom;
        u32 reg;
@@ -164,6 +169,8 @@ static void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev)
 
        eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom,
                               EEPROM_SIZE / sizeof(u16));
+
+       return 0;
 }
 
 static int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev)
@@ -171,13 +178,14 @@ static int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev)
        return rt2800_efuse_detect(rt2x00dev);
 }
 
-static inline void rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
+static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
 {
-       rt2800_read_eeprom_efuse(rt2x00dev);
+       return rt2800_read_eeprom_efuse(rt2x00dev);
 }
 #else
-static inline void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev)
+static inline int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev)
 {
+       return -EOPNOTSUPP;
 }
 
 static inline int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev)
@@ -185,8 +193,9 @@ static inline int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev)
        return 0;
 }
 
-static inline void rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
+static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
 {
+       return -EOPNOTSUPP;
 }
 #endif /* CONFIG_PCI */
 
@@ -970,14 +979,18 @@ static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance)
 /*
  * Device probe functions.
  */
-static void rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev)
+static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev)
 {
+       int retval;
+
        if (rt2x00_is_soc(rt2x00dev))
-               rt2800pci_read_eeprom_soc(rt2x00dev);
+               retval = rt2800pci_read_eeprom_soc(rt2x00dev);
        else if (rt2800pci_efuse_detect(rt2x00dev))
-               rt2800pci_read_eeprom_efuse(rt2x00dev);
+               retval = rt2800pci_read_eeprom_efuse(rt2x00dev);
        else
-               rt2800pci_read_eeprom_pci(rt2x00dev);
+               retval = rt2800pci_read_eeprom_pci(rt2x00dev);
+
+       return retval;
 }
 
 static const struct ieee80211_ops rt2800pci_mac80211_ops = {
@@ -1139,6 +1152,7 @@ static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = {
        { PCI_DEVICE(0x1814, 0x3562) },
        { PCI_DEVICE(0x1814, 0x3592) },
        { PCI_DEVICE(0x1814, 0x3593) },
+       { PCI_DEVICE(0x1814, 0x359f) },
 #endif
 #ifdef CONFIG_RT2800PCI_RT53XX
        { PCI_DEVICE(0x1814, 0x5360) },
index 5c149b58ab46dd61b53406511352b3f80f7c48cb..42b5b659af16252dcb69b14b43d7b047ecf2abe7 100644 (file)
@@ -540,9 +540,9 @@ rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg)
        tx_pid  = rt2x00_get_field32(word, TXWI_W1_PACKETID);
 
        if (wcid != tx_wcid || ack != tx_ack || (!is_agg && pid != tx_pid)) {
-               WARNING(entry->queue->rt2x00dev,
-                       "TX status report missed for queue %d entry %d\n",
-                       entry->queue->qid, entry->entry_idx);
+               DEBUG(entry->queue->rt2x00dev,
+                     "TX status report missed for queue %d entry %d\n",
+                     entry->queue->qid, entry->entry_idx);
                return TXDONE_UNKNOWN;
        }
 
@@ -735,13 +735,17 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
 /*
  * Device probe functions.
  */
-static void rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev)
+static int rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev)
 {
+       int retval;
+
        if (rt2800_efuse_detect(rt2x00dev))
-               rt2800_read_eeprom_efuse(rt2x00dev);
+               retval = rt2800_read_eeprom_efuse(rt2x00dev);
        else
-               rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom,
-                                     EEPROM_SIZE);
+               retval = rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom,
+                                              EEPROM_SIZE);
+
+       return retval;
 }
 
 static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
@@ -964,6 +968,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x07d1, 0x3c13) },
        { USB_DEVICE(0x07d1, 0x3c15) },
        { USB_DEVICE(0x07d1, 0x3c16) },
+       { USB_DEVICE(0x07d1, 0x3c17) },
        { USB_DEVICE(0x2001, 0x3c1b) },
        /* Draytek */
        { USB_DEVICE(0x07fa, 0x7712) },
@@ -1111,6 +1116,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        /* Zyxel */
        { USB_DEVICE(0x0586, 0x3416) },
        { USB_DEVICE(0x0586, 0x3418) },
+       { USB_DEVICE(0x0586, 0x341a) },
        { USB_DEVICE(0x0586, 0x341e) },
        { USB_DEVICE(0x0586, 0x343e) },
 #ifdef CONFIG_RT2800USB_RT33XX
@@ -1162,6 +1168,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
 #ifdef CONFIG_RT2800USB_RT53XX
        /* Arcadyan */
        { USB_DEVICE(0x043e, 0x7a12) },
+       { USB_DEVICE(0x043e, 0x7a32) },
        /* Azurewave */
        { USB_DEVICE(0x13d3, 0x3329) },
        { USB_DEVICE(0x13d3, 0x3365) },
@@ -1173,16 +1180,20 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x2001, 0x3c1e) },
        /* LG innotek */
        { USB_DEVICE(0x043e, 0x7a22) },
+       { USB_DEVICE(0x043e, 0x7a42) },
        /* Panasonic */
        { USB_DEVICE(0x04da, 0x1801) },
        { USB_DEVICE(0x04da, 0x1800) },
+       { USB_DEVICE(0x04da, 0x23f6) },
        /* Philips */
        { USB_DEVICE(0x0471, 0x2104) },
+       { USB_DEVICE(0x0471, 0x2126) },
+       { USB_DEVICE(0x0471, 0x2180) },
+       { USB_DEVICE(0x0471, 0x2181) },
+       { USB_DEVICE(0x0471, 0x2182) },
        /* Ralink */
        { USB_DEVICE(0x148f, 0x5370) },
        { USB_DEVICE(0x148f, 0x5372) },
-       /* Unknown */
-       { USB_DEVICE(0x04da, 0x23f6) },
 #endif
 #ifdef CONFIG_RT2800USB_UNKNOWN
        /*
@@ -1219,7 +1230,6 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x18c5, 0x0008) },
        /* D-Link */
        { USB_DEVICE(0x07d1, 0x3c0b) },
-       { USB_DEVICE(0x07d1, 0x3c17) },
        /* Encore */
        { USB_DEVICE(0x203d, 0x14a1) },
        /* Gemtek */
@@ -1257,8 +1267,6 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x083a, 0xc522) },
        { USB_DEVICE(0x083a, 0xd522) },
        { USB_DEVICE(0x083a, 0xf511) },
-       /* Zyxel */
-       { USB_DEVICE(0x0586, 0x341a) },
 #endif
        { 0, }
 };
index 0751b35ef6dcd536ba51c3554dc4b58e359a9628..9a3f31a543ce95a4e7a0398defedb866cfe4a8d9 100644 (file)
 #define ERROR_PROBE(__msg, __args...) \
        DEBUG_PRINTK_PROBE(KERN_ERR, "Error", __msg, ##__args)
 #define WARNING(__dev, __msg, __args...) \
-       DEBUG_PRINTK(__dev, KERN_WARNING, "Warning", __msg, ##__args)
-#define NOTICE(__dev, __msg, __args...) \
-       DEBUG_PRINTK(__dev, KERN_NOTICE, "Notice", __msg, ##__args)
+       DEBUG_PRINTK_MSG(__dev, KERN_WARNING, "Warning", __msg, ##__args)
 #define INFO(__dev, __msg, __args...) \
-       DEBUG_PRINTK(__dev, KERN_INFO, "Info", __msg, ##__args)
+       DEBUG_PRINTK_MSG(__dev, KERN_INFO, "Info", __msg, ##__args)
 #define DEBUG(__dev, __msg, __args...) \
        DEBUG_PRINTK(__dev, KERN_DEBUG, "Debug", __msg, ##__args)
 #define EEPROM(__dev, __msg, __args...) \
@@ -1016,6 +1014,26 @@ struct rt2x00_dev {
         * Protect the interrupt mask register.
         */
        spinlock_t irqmask_lock;
+
+       /*
+        * List of BlockAckReq TX entries that need driver BlockAck processing.
+        */
+       struct list_head bar_list;
+       spinlock_t bar_list_lock;
+};
+
+struct rt2x00_bar_list_entry {
+       struct list_head list;
+       struct rcu_head head;
+
+       struct queue_entry *entry;
+       int block_acked;
+
+       /* Relevant parts of the IEEE80211 BAR header */
+       __u8 ra[6];
+       __u8 ta[6];
+       __le16 control;
+       __le16 start_seq_num;
 };
 
 /*
index cdbfc303f67a0f1a5c20200d1c83661c971bbd30..c7a5d26481c855b7823dd9122805c60b14a54dec 100644 (file)
@@ -271,6 +271,50 @@ void rt2x00lib_dmadone(struct queue_entry *entry)
 }
 EXPORT_SYMBOL_GPL(rt2x00lib_dmadone);
 
+static inline int rt2x00lib_txdone_bar_status(struct queue_entry *entry)
+{
+       struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+       struct ieee80211_bar *bar = (void *) entry->skb->data;
+       struct rt2x00_bar_list_entry *bar_entry;
+       int ret;
+
+       if (likely(!ieee80211_is_back_req(bar->frame_control)))
+               return 0;
+
+       /*
+        * Unlike all other frames, the status report for BARs does
+        * not directly come from the hardware as it is incapable of
+        * matching a BA to a previously send BAR. The hardware will
+        * report all BARs as if they weren't acked at all.
+        *
+        * Instead the RX-path will scan for incoming BAs and set the
+        * block_acked flag if it sees one that was likely caused by
+        * a BAR from us.
+        *
+        * Remove remaining BARs here and return their status for
+        * TX done processing.
+        */
+       ret = 0;
+       rcu_read_lock();
+       list_for_each_entry_rcu(bar_entry, &rt2x00dev->bar_list, list) {
+               if (bar_entry->entry != entry)
+                       continue;
+
+               spin_lock_bh(&rt2x00dev->bar_list_lock);
+               /* Return whether this BAR was blockacked or not */
+               ret = bar_entry->block_acked;
+               /* Remove the BAR from our checklist */
+               list_del_rcu(&bar_entry->list);
+               spin_unlock_bh(&rt2x00dev->bar_list_lock);
+               kfree_rcu(bar_entry, head);
+
+               break;
+       }
+       rcu_read_unlock();
+
+       return ret;
+}
+
 void rt2x00lib_txdone(struct queue_entry *entry,
                      struct txdone_entry_desc *txdesc)
 {
@@ -324,9 +368,12 @@ void rt2x00lib_txdone(struct queue_entry *entry,
        rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry->skb);
 
        /*
-        * Determine if the frame has been successfully transmitted.
+        * Determine if the frame has been successfully transmitted and
+        * remove BARs from our check list while checking for their
+        * TX status.
         */
        success =
+           rt2x00lib_txdone_bar_status(entry) ||
            test_bit(TXDONE_SUCCESS, &txdesc->flags) ||
            test_bit(TXDONE_UNKNOWN, &txdesc->flags);
 
@@ -491,6 +538,50 @@ static void rt2x00lib_sleep(struct work_struct *work)
                                 IEEE80211_CONF_CHANGE_PS);
 }
 
+static void rt2x00lib_rxdone_check_ba(struct rt2x00_dev *rt2x00dev,
+                                     struct sk_buff *skb,
+                                     struct rxdone_entry_desc *rxdesc)
+{
+       struct rt2x00_bar_list_entry *entry;
+       struct ieee80211_bar *ba = (void *)skb->data;
+
+       if (likely(!ieee80211_is_back(ba->frame_control)))
+               return;
+
+       if (rxdesc->size < sizeof(*ba) + FCS_LEN)
+               return;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(entry, &rt2x00dev->bar_list, list) {
+
+               if (ba->start_seq_num != entry->start_seq_num)
+                       continue;
+
+#define TID_CHECK(a, b) (                                              \
+       ((a) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK)) ==        \
+       ((b) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK)))          \
+
+               if (!TID_CHECK(ba->control, entry->control))
+                       continue;
+
+#undef TID_CHECK
+
+               if (compare_ether_addr(ba->ra, entry->ta))
+                       continue;
+
+               if (compare_ether_addr(ba->ta, entry->ra))
+                       continue;
+
+               /* Mark BAR since we received the according BA */
+               spin_lock_bh(&rt2x00dev->bar_list_lock);
+               entry->block_acked = 1;
+               spin_unlock_bh(&rt2x00dev->bar_list_lock);
+               break;
+       }
+       rcu_read_unlock();
+
+}
+
 static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev,
                                      struct sk_buff *skb,
                                      struct rxdone_entry_desc *rxdesc)
@@ -673,6 +764,12 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)
         */
        rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc);
 
+       /*
+        * Check for incoming BlockAcks to match to the BlockAckReqs
+        * we've send out.
+        */
+       rt2x00lib_rxdone_check_ba(rt2x00dev, entry->skb, &rxdesc);
+
        /*
         * Update extra components
         */
@@ -1139,7 +1236,8 @@ static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev)
         */
        if_limit = &rt2x00dev->if_limits_ap;
        if_limit->max = rt2x00dev->ops->max_ap_intf;
-       if_limit->types = BIT(NL80211_IFTYPE_AP);
+       if_limit->types = BIT(NL80211_IFTYPE_AP) |
+                       BIT(NL80211_IFTYPE_MESH_POINT);
 
        /*
         * Build up AP interface combinations structure.
@@ -1183,6 +1281,8 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
 
        spin_lock_init(&rt2x00dev->irqmask_lock);
        mutex_init(&rt2x00dev->csr_mutex);
+       INIT_LIST_HEAD(&rt2x00dev->bar_list);
+       spin_lock_init(&rt2x00dev->bar_list_lock);
 
        set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
 
@@ -1349,7 +1449,7 @@ EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev);
 #ifdef CONFIG_PM
 int rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev, pm_message_t state)
 {
-       NOTICE(rt2x00dev, "Going to sleep.\n");
+       DEBUG(rt2x00dev, "Going to sleep.\n");
 
        /*
         * Prevent mac80211 from accessing driver while suspended.
@@ -1389,7 +1489,7 @@ EXPORT_SYMBOL_GPL(rt2x00lib_suspend);
 
 int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev)
 {
-       NOTICE(rt2x00dev, "Waking up.\n");
+       DEBUG(rt2x00dev, "Waking up.\n");
 
        /*
         * Restore/enable extra components.
index ed7a1bb3f2450223e780e344e014efa51d290dbb..20c6eccce5aa32004f5b8b5f2aacf5703903fc6c 100644 (file)
@@ -731,9 +731,9 @@ int rt2x00mac_conf_tx(struct ieee80211_hw *hw,
        queue->aifs = params->aifs;
        queue->txop = params->txop;
 
-       INFO(rt2x00dev,
-            "Configured TX queue %d - CWmin: %d, CWmax: %d, Aifs: %d, TXop: %d.\n",
-            queue_idx, queue->cw_min, queue->cw_max, queue->aifs, queue->txop);
+       DEBUG(rt2x00dev,
+             "Configured TX queue %d - CWmin: %d, CWmax: %d, Aifs: %d, TXop: %d.\n",
+             queue_idx, queue->cw_min, queue->cw_max, queue->aifs, queue->txop);
 
        return 0;
 }
index e488b944a0340834ed96c02c91df59e9b3f5e142..f35d85a71bbcab0e416f4e6db115e82538098414 100644 (file)
@@ -582,6 +582,48 @@ static void rt2x00queue_kick_tx_queue(struct data_queue *queue,
                queue->rt2x00dev->ops->lib->kick_queue(queue);
 }
 
+static void rt2x00queue_bar_check(struct queue_entry *entry)
+{
+       struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+       struct ieee80211_bar *bar = (void *) (entry->skb->data +
+                                   rt2x00dev->ops->extra_tx_headroom);
+       struct rt2x00_bar_list_entry *bar_entry;
+
+       if (likely(!ieee80211_is_back_req(bar->frame_control)))
+               return;
+
+       bar_entry = kmalloc(sizeof(*bar_entry), GFP_ATOMIC);
+
+       /*
+        * If the alloc fails we still send the BAR out but just don't track
+        * it in our bar list. And as a result we will report it to mac80211
+        * back as failed.
+        */
+       if (!bar_entry)
+               return;
+
+       bar_entry->entry = entry;
+       bar_entry->block_acked = 0;
+
+       /*
+        * Copy the relevant parts of the 802.11 BAR into out check list
+        * such that we can use RCU for less-overhead in the RX path since
+        * sending BARs and processing the according BlockAck should be
+        * the exception.
+        */
+       memcpy(bar_entry->ra, bar->ra, sizeof(bar->ra));
+       memcpy(bar_entry->ta, bar->ta, sizeof(bar->ta));
+       bar_entry->control = bar->control;
+       bar_entry->start_seq_num = bar->start_seq_num;
+
+       /*
+        * Insert BAR into our BAR check list.
+        */
+       spin_lock_bh(&rt2x00dev->bar_list_lock);
+       list_add_tail_rcu(&bar_entry->list, &rt2x00dev->bar_list);
+       spin_unlock_bh(&rt2x00dev->bar_list_lock);
+}
+
 int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
                               bool local)
 {
@@ -680,6 +722,11 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
                goto out;
        }
 
+       /*
+        * Put BlockAckReqs into our check list for driver BA processing.
+        */
+       rt2x00queue_bar_check(entry);
+
        set_bit(ENTRY_DATA_PENDING, &entry->flags);
 
        rt2x00queue_index_inc(entry, Q_INDEX);
index be33aa14c8afaa6672497d156abebb4b3cd146f9..d3ce9fbef00ee0b8d3e88eefcfd7111d9d0080ae 100644 (file)
@@ -879,7 +879,9 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw,
                         "IEEE80211_AMPDU_TX_START: TID:%d\n", tid);
                return rtl_tx_agg_start(hw, sta, tid, ssn);
                break;
-       case IEEE80211_AMPDU_TX_STOP:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
                RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE,
                         "IEEE80211_AMPDU_TX_STOP: TID:%d\n", tid);
                return rtl_tx_agg_stop(hw, sta, tid);
index c1e065f136baa631a9245b87f897dea167865431..204f46c4510d842c7a1c8c20555f990f9ae86051 100644 (file)
@@ -217,19 +217,6 @@ static void rtl_tx_status(void *ppriv,
        }
 }
 
-static void rtl_rate_init(void *ppriv,
-                         struct ieee80211_supported_band *sband,
-                         struct ieee80211_sta *sta, void *priv_sta)
-{
-}
-
-static void rtl_rate_update(void *ppriv,
-                           struct ieee80211_supported_band *sband,
-                           struct ieee80211_sta *sta, void *priv_sta,
-                           u32 changed)
-{
-}
-
 static void *rtl_rate_alloc(struct ieee80211_hw *hw,
                struct dentry *debugfsdir)
 {
@@ -274,8 +261,6 @@ static struct rate_control_ops rtl_rate_ops = {
        .free = rtl_rate_free,
        .alloc_sta = rtl_rate_alloc_sta,
        .free_sta = rtl_rate_free_sta,
-       .rate_init = rtl_rate_init,
-       .rate_update = rtl_rate_update,
        .tx_status = rtl_tx_status,
        .get_rate = rtl_get_rate,
 };
index c1608cddc5299e5150a323d0a3efb7a2f0b63de6..d7d0d4948b01f2e804e6bcddfca93b10c7cdb312 100644 (file)
@@ -158,8 +158,6 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy,
        const struct ieee80211_reg_rule *reg_rule;
        struct ieee80211_channel *ch;
        unsigned int i;
-       u32 bandwidth = 0;
-       int r;
 
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
 
@@ -174,9 +172,8 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy,
                            (ch->flags & IEEE80211_CHAN_RADAR))
                                continue;
                        if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
-                               r = freq_reg_info(wiphy, ch->center_freq,
-                                                 bandwidth, &reg_rule);
-                               if (r)
+                               reg_rule = freq_reg_info(wiphy, ch->center_freq);
+                               if (IS_ERR(reg_rule))
                                        continue;
 
                                /*
@@ -211,8 +208,6 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy,
        struct ieee80211_supported_band *sband;
        struct ieee80211_channel *ch;
        const struct ieee80211_reg_rule *reg_rule;
-       u32 bandwidth = 0;
-       int r;
 
        if (!wiphy->bands[IEEE80211_BAND_2GHZ])
                return;
@@ -240,16 +235,16 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy,
         */
 
        ch = &sband->channels[11];      /* CH 12 */
-       r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
-       if (!r) {
+       reg_rule = freq_reg_info(wiphy, ch->center_freq);
+       if (!IS_ERR(reg_rule)) {
                if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
                        if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
                                ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
        }
 
        ch = &sband->channels[12];      /* CH 13 */
-       r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
-       if (!r) {
+       reg_rule = freq_reg_info(wiphy, ch->center_freq);
+       if (!IS_ERR(reg_rule)) {
                if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
                        if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
                                ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
@@ -303,9 +298,9 @@ static void _rtl_reg_apply_world_flags(struct wiphy *wiphy,
        return;
 }
 
-static int _rtl_reg_notifier_apply(struct wiphy *wiphy,
-                                  struct regulatory_request *request,
-                                  struct rtl_regulatory *reg)
+static void _rtl_reg_notifier_apply(struct wiphy *wiphy,
+                                   struct regulatory_request *request,
+                                   struct rtl_regulatory *reg)
 {
        /* We always apply this */
        _rtl_reg_apply_radar_flags(wiphy);
@@ -319,8 +314,6 @@ static int _rtl_reg_notifier_apply(struct wiphy *wiphy,
                _rtl_reg_apply_world_flags(wiphy, request->initiator, reg);
                break;
        }
-
-       return 0;
 }
 
 static const struct ieee80211_regdomain *_rtl_regdomain_select(
@@ -353,9 +346,9 @@ static const struct ieee80211_regdomain *_rtl_regdomain_select(
 
 static int _rtl_regd_init_wiphy(struct rtl_regulatory *reg,
                                struct wiphy *wiphy,
-                               int (*reg_notifier) (struct wiphy *wiphy,
-                                                    struct regulatory_request *
-                                                    request))
+                               void (*reg_notifier) (struct wiphy *wiphy,
+                                                     struct regulatory_request *
+                                                     request))
 {
        const struct ieee80211_regdomain *regd;
 
@@ -384,7 +377,7 @@ static struct country_code_to_enum_rd *_rtl_regd_find_country(u16 countrycode)
 }
 
 int rtl_regd_init(struct ieee80211_hw *hw,
-                 int (*reg_notifier) (struct wiphy *wiphy,
+                 void (*reg_notifier) (struct wiphy *wiphy,
                                       struct regulatory_request *request))
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -426,12 +419,12 @@ int rtl_regd_init(struct ieee80211_hw *hw,
        return 0;
 }
 
-int rtl_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
+void rtl_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
 {
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct rtl_priv *rtlpriv = rtl_priv(hw);
 
        RT_TRACE(rtlpriv, COMP_REGD, DBG_LOUD, "\n");
 
-       return _rtl_reg_notifier_apply(wiphy, request, &rtlpriv->regd);
+       _rtl_reg_notifier_apply(wiphy, request, &rtlpriv->regd);
 }
index 70ef2f418a44134d1fd1d810afd5f4ace4705151..4e1f4f00e6e9633ed30c59ca2220039e733fdb39 100644 (file)
@@ -55,7 +55,7 @@ enum country_code_type_t {
 };
 
 int rtl_regd_init(struct ieee80211_hw *hw,
-                 int (*reg_notifier) (struct wiphy *wiphy,
-                                      struct regulatory_request *request));
-int rtl_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request);
+                 void (*reg_notifier) (struct wiphy *wiphy,
+                                       struct regulatory_request *request));
+void rtl_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request);
 #endif
index 1cdf5a271c9f3221f8c608201b47b4a6af768585..b793a659a46535e3689867d69f6ec28f6a76c400 100644 (file)
@@ -669,7 +669,8 @@ static void rtl92c_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw
        u8 thermalvalue, delta, delta_lck, delta_iqk;
        long ele_a, ele_d, temp_cck, val_x, value32;
        long val_y, ele_c = 0;
-       u8 ofdm_index[2], cck_index = 0, ofdm_index_old[2], cck_index_old = 0;
+       u8 ofdm_index[2], ofdm_index_old[2], cck_index_old = 0;
+       s8 cck_index = 0;
        int i;
        bool is2t = IS_92C_SERIAL(rtlhal->version);
        s8 txpwr_level[2] = {0, 0};
index b450931b6a51c0ff52668e267b270b5e66d7d6de..a73a17bc56dd068f9cb9d0042dff53b9c88a4a3b 100644 (file)
@@ -76,7 +76,7 @@ static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw)
                                      GFP_KERNEL, hw, rtl_fw_cb);
 
 
-       return 0;
+       return err;
 }
 
 static void rtl92cu_deinit_sw_vars(struct ieee80211_hw *hw)
index fd8df233ff2299d451c01be3211daf8ed20c28b2..5251fb8a111eee18ab04461c8f1eaed4c357a87d 100644 (file)
@@ -841,9 +841,9 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
        long ele_a = 0, ele_d, temp_cck, val_x, value32;
        long val_y, ele_c = 0;
        u8 ofdm_index[2];
-       u8 cck_index = 0;
+       s8 cck_index = 0;
        u8 ofdm_index_old[2];
-       u8 cck_index_old = 0;
+       s8 cck_index_old = 0;
        u8 index;
        int i;
        bool is2t = IS_92D_SINGLEPHY(rtlhal->version);
index a0fbf284420ec78b2570756dc4c1edd2638be057..cdb570ffb4b5028a6243e8d7fe6c5f76da617478 100644 (file)
@@ -452,7 +452,7 @@ static void _rtl92de_translate_rx_signal_stuff(struct ieee80211_hw *hw,
        u8 *praddr;
        u16 type, cfc;
        __le16 fc;
-       bool packet_matchbssid, packet_toself, packet_beacon;
+       bool packet_matchbssid, packet_toself, packet_beacon = false;
 
        tmp_buf = skb->data + pstats->rx_drvinfo_size + pstats->rx_bufshift;
        hdr = (struct ieee80211_hdr *)tmp_buf;
index 206561d7282f906a6662ce43610f87666362dbe3..f8431a3c2c9d6ba1380b358d26ac0743bb9ef9cb 100644 (file)
@@ -480,7 +480,7 @@ static void _rtl92se_translate_rx_signal_stuff(struct ieee80211_hw *hw,
        u8 *praddr;
        __le16 fc;
        u16 type, cfc;
-       bool packet_matchbssid, packet_toself, packet_beacon;
+       bool packet_matchbssid, packet_toself, packet_beacon = false;
 
        tmp_buf = skb->data + pstats->rx_drvinfo_size + pstats->rx_bufshift;
 
index f55b1767ef574cd8c7afdfa4a98509008f1472f3..35cb8f83eed4c2b4fcf9c008c49650bd65a85fa3 100644 (file)
@@ -252,7 +252,7 @@ static void _rtl8723ae_fill_h2c_command(struct ieee80211_hw *hw,
        u16 box_reg = 0, box_extreg = 0;
        u8 u1tmp;
        bool isfw_rd = false;
-       bool bwrite_sucess = false;
+       bool bwrite_success = false;
        u8 wait_h2c_limmit = 100;
        u8 wait_writeh2c_limmit = 100;
        u8 boxcontent[4], boxextcontent[2];
@@ -291,7 +291,7 @@ static void _rtl8723ae_fill_h2c_command(struct ieee80211_hw *hw,
                }
        }
 
-       while (!bwrite_sucess) {
+       while (!bwrite_success) {
                wait_writeh2c_limmit--;
                if (wait_writeh2c_limmit == 0) {
                        RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -429,7 +429,7 @@ static void _rtl8723ae_fill_h2c_command(struct ieee80211_hw *hw,
                        break;
                }
 
-               bwrite_sucess = true;
+               bwrite_success = true;
 
                rtlhal->last_hmeboxnum = boxnum + 1;
                if (rtlhal->last_hmeboxnum == 4)
@@ -512,7 +512,6 @@ static bool _rtl8723ae_cmd_send_packet(struct ieee80211_hw *hw,
        struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
        struct rtl8192_tx_ring *ring;
        struct rtl_tx_desc *pdesc;
-       u8 own;
        unsigned long flags;
        struct sk_buff *pskb = NULL;
 
@@ -525,7 +524,6 @@ static bool _rtl8723ae_cmd_send_packet(struct ieee80211_hw *hw,
        spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
 
        pdesc = &ring->desc[0];
-       own = (u8) rtlpriv->cfg->ops->get_desc((u8 *) pdesc, true, HW_DESC_OWN);
 
        rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *) pdesc, 1, 1, skb);
 
index 887d521fe6901ca375c90a9f8f6fae28fd63c464..68c28340f791623f9e78b98d2568a48b3668a86a 100644 (file)
@@ -1433,7 +1433,6 @@ static void _rtl8723ae_dm_bt_coexist_2_ant(struct ieee80211_hw *hw)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
        struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
-       u8 bt_retry_cnt;
        u8 bt_info_original;
        RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG,
                 "[BTCoex] Get bt info by fw!!\n");
@@ -1445,7 +1444,6 @@ static void _rtl8723ae_dm_bt_coexist_2_ant(struct ieee80211_hw *hw)
                                 "[BTCoex] c2h for btInfo not rcvd yet!!\n");
        }
 
-       bt_retry_cnt = rtlhal->hal_coex_8723.bt_retry_cnt;
        bt_info_original = rtlhal->hal_coex_8723.c2h_bt_info_original;
 
        /* when bt inquiry or page scan, we have to set h2c 0x25
index 0a8c03863fb22989ce03677d6e97d68a1a140160..149804816ac4b614088a9717c9c6be6ee45f568b 100644 (file)
@@ -703,11 +703,9 @@ static void _rtl8723ae_hw_configure(struct ieee80211_hw *hw)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
        u8 reg_bw_opmode;
-       u32 reg_ratr, reg_prsr;
+       u32 reg_prsr;
 
        reg_bw_opmode = BW_OPMODE_20MHZ;
-       reg_ratr = RATE_ALL_CCK | RATE_ALL_OFDM_AG |
-           RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS;
        reg_prsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
 
        rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, 0x8);
@@ -2030,7 +2028,7 @@ bool rtl8723ae_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
        struct rtl_phy *rtlphy = &(rtlpriv->phy);
-       enum rf_pwrstate e_rfpowerstate_toset, cur_rfstate;
+       enum rf_pwrstate e_rfpowerstate_toset;
        u8 u1tmp;
        bool actuallyset = false;
 
@@ -2049,8 +2047,6 @@ bool rtl8723ae_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
                spin_unlock(&rtlpriv->locks.rf_ps_lock);
        }
 
-       cur_rfstate = ppsc->rfpwr_state;
-
        rtl_write_byte(rtlpriv, REG_GPIO_IO_SEL_2,
                       rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL_2)&~(BIT(1)));
 
index 3d8536bb0d2bf5055c181d5d8f4b67d25153488b..eafbb18dd48e69a44d67cd60f171b5915662e58c 100644 (file)
@@ -614,17 +614,11 @@ bool rtl8723ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        int i;
-       bool rtstatus = true;
        u32 *radioa_array_table;
-       u32 *radiob_array_table;
-       u16 radioa_arraylen, radiob_arraylen;
+       u16 radioa_arraylen;
 
        radioa_arraylen = Rtl8723ERADIOA_1TARRAYLENGTH;
        radioa_array_table = RTL8723E_RADIOA_1TARRAY;
-       radiob_arraylen = RTL8723E_RADIOB_1TARRAYLENGTH;
-       radiob_array_table = RTL8723E_RADIOB_1TARRAY;
-
-       rtstatus = true;
 
        switch (rfpath) {
        case RF90_PATH_A:
@@ -1531,11 +1525,8 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw,
                0x522, 0x550, 0x551, 0x040
        };
        const u32 retrycount = 2;
-       u32 bbvalue;
 
        if (t == 0) {
-               bbvalue = rtl_get_bbreg(hw, 0x800, MASKDWORD);
-
                phy_save_adda_regs(hw, adda_reg, rtlphy->adda_backup, 16);
                phy_save_mac_regs(hw, iqk_mac_reg, rtlphy->iqk_mac_backup);
        }
@@ -1712,8 +1703,7 @@ void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery)
        long result[4][8];
        u8 i, final_candidate;
        bool patha_ok, pathb_ok;
-       long reg_e94, reg_e9c, reg_ea4, reg_eac, reg_eb4, reg_ebc, reg_ec4,
-           reg_ecc, reg_tmp = 0;
+       long reg_e94, reg_e9c, reg_ea4, reg_eb4, reg_ebc, reg_tmp = 0;
        bool is12simular, is13simular, is23simular;
        bool start_conttx = false, singletone = false;
        u32 iqk_bb_reg[10] = {
@@ -1780,21 +1770,15 @@ void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery)
                reg_e94 = result[i][0];
                reg_e9c = result[i][1];
                reg_ea4 = result[i][2];
-               reg_eac = result[i][3];
                reg_eb4 = result[i][4];
                reg_ebc = result[i][5];
-               reg_ec4 = result[i][6];
-               reg_ecc = result[i][7];
        }
        if (final_candidate != 0xff) {
                rtlphy->reg_e94 = reg_e94 = result[final_candidate][0];
                rtlphy->reg_e9c = reg_e9c = result[final_candidate][1];
                reg_ea4 = result[final_candidate][2];
-               reg_eac = result[final_candidate][3];
                rtlphy->reg_eb4 = reg_eb4 = result[final_candidate][4];
                rtlphy->reg_ebc = reg_ebc = result[final_candidate][5];
-               reg_ec4 = result[final_candidate][6];
-               reg_ecc = result[final_candidate][7];
                patha_ok = pathb_ok = true;
        } else {
                rtlphy->reg_e94 = rtlphy->reg_eb4 = 0x100;
index a313be8c21d28d5708fdbc34e49f0db3fa7836a2..b1fd2b328abfa8f63733ce39aed85e059b25f0f2 100644 (file)
@@ -244,10 +244,9 @@ static void _rtl8723ae_translate_rx_signal_stuff(struct ieee80211_hw *hw,
        struct ieee80211_hdr *hdr;
        u8 *tmp_buf;
        u8 *praddr;
-       u8 *psaddr;
        __le16 fc;
        u16 type;
-       bool packet_matchbssid, packet_toself, packet_beacon;
+       bool packet_matchbssid, packet_toself, packet_beacon = false;
 
        tmp_buf = skb->data + pstatus->rx_drvinfo_size + pstatus->rx_bufshift;
 
@@ -255,7 +254,6 @@ static void _rtl8723ae_translate_rx_signal_stuff(struct ieee80211_hw *hw,
        fc = hdr->frame_control;
        type = WLAN_FC_GET_TYPE(fc);
        praddr = hdr->addr1;
-       psaddr = ieee80211_get_SA(hdr);
 
        packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) &&
                            (!compare_ether_addr(mac->bssid,
index 2106fcf9ed1917a667120558cd10f39079e6b9e3..156b52732f3d576cb458bc3f6b3e2eb2818f2d90 100644 (file)
@@ -837,8 +837,6 @@ static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb,
        u32 ep_num;
        struct urb *_urb = NULL;
        struct sk_buff *_skb = NULL;
-       struct sk_buff_head *skb_list;
-       struct usb_anchor *urb_list;
 
        WARN_ON(NULL == rtlusb->usb_tx_aggregate_hdl);
        if (unlikely(IS_USB_STOP(rtlusb))) {
@@ -848,7 +846,6 @@ static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb,
                return;
        }
        ep_num = rtlusb->ep_map.ep_mapping[qnum];
-       skb_list = &rtlusb->tx_skb_queue[ep_num];
        _skb = skb;
        _urb = _rtl_usb_tx_urb_setup(hw, _skb, ep_num);
        if (unlikely(!_urb)) {
@@ -856,7 +853,6 @@ static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb,
                         "Can't allocate urb. Drop skb!\n");
                return;
        }
-       urb_list = &rtlusb->tx_pending[ep_num];
        _rtl_submit_tx_urb(hw, _urb);
 }
 
index 21a5f4f4a13509da671834fe196d0fca19fa2b9f..f13258a8d9956be1be84eae9eecbcec3f103fb94 100644 (file)
@@ -1702,7 +1702,7 @@ struct rtl_works {
 
 struct rtl_debug {
        u32 dbgp_type[DBGP_TYPE_MAX];
-       u32 global_debuglevel;
+       int global_debuglevel;
        u64 global_debugcomponents;
 
        /* add for proc debug */
index be800119d0a355d5404122341def2dba8b21c647..cbe1e7fef61b226aa7af9614808bf747ec8d1ebe 100644 (file)
@@ -12,4 +12,13 @@ source "drivers/net/wireless/ti/wl18xx/Kconfig"
 
 # keep last for automatic dependencies
 source "drivers/net/wireless/ti/wlcore/Kconfig"
+
+config WILINK_PLATFORM_DATA
+       bool "TI WiLink platform data"
+       depends on WLCORE_SDIO || WL1251_SDIO
+       default y
+       ---help---
+       Small platform data bit needed to pass data to the sdio modules.
+
+
 endif # WL_TI
index 4d6823983c04e38e71a467deb15a098214e52daa..af14231aeedeffc6e7d8c87dcfbe8c84cfee7de2 100644 (file)
@@ -1,5 +1,7 @@
 obj-$(CONFIG_WLCORE)                   += wlcore/
 obj-$(CONFIG_WL12XX)                   += wl12xx/
-obj-$(CONFIG_WL12XX_PLATFORM_DATA)     += wlcore/
 obj-$(CONFIG_WL1251)                   += wl1251/
 obj-$(CONFIG_WL18XX)                   += wl18xx/
+
+# small builtin driver bit
+obj-$(CONFIG_WILINK_PLATFORM_DATA)     += wilink_platform_data.o
index 1fb65849414f347899b40bb8fcb26c212a121d1e..8fec4ed36ac2ecae7824d00e6e79aa5b5b5880c0 100644 (file)
@@ -1,6 +1,6 @@
 menuconfig WL1251
        tristate "TI wl1251 driver support"
-       depends on MAC80211 && EXPERIMENTAL && GENERIC_HARDIRQS
+       depends on MAC80211 && GENERIC_HARDIRQS
        select FW_LOADER
        select CRC7
        ---help---
index da509aa7d0092ffb95d00936908873c4d6a4af58..e6a24056b3c87267cc124c37d65a1dec72c2ed54 100644 (file)
@@ -1,3 +1,3 @@
-wl12xx-objs    = main.o cmd.o acx.o debugfs.o
+wl12xx-objs    = main.o cmd.o acx.o debugfs.o scan.o event.o
 
 obj-$(CONFIG_WL12XX)           += wl12xx.o
index 622206241e83210e4246bbfc91f089837c6bdbb8..7dc9f965037d4189467c053f0c81f35b71a33166 100644 (file)
@@ -284,3 +284,40 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl)
        kfree(radio_parms);
        return ret;
 }
+
+int wl12xx_cmd_channel_switch(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch)
+{
+       struct wl12xx_cmd_channel_switch *cmd;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "cmd channel switch");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+       cmd->channel = ch_switch->channel->hw_value;
+       cmd->switch_time = ch_switch->count;
+       cmd->stop_tx = ch_switch->block_tx;
+
+       /* FIXME: control from mac80211 in the future */
+       /* Enable TX on the target channel */
+       cmd->post_switch_tx_disable = 0;
+
+       ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send channel switch command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+
+out:
+       return ret;
+}
index 140a0e8829d540050102e4dde4a0ba926420fa5d..32cbad54e993ea402647ef945bcf55ac517f4283 100644 (file)
@@ -103,10 +103,30 @@ struct wl1271_ext_radio_parms_cmd {
        u8 padding[3];
 } __packed;
 
+struct wl12xx_cmd_channel_switch {
+       struct wl1271_cmd_header header;
+
+       u8 role_id;
+
+       /* The new serving channel */
+       u8 channel;
+       /* Relative time of the serving channel switch in TBTT units */
+       u8 switch_time;
+       /* Stop the role TX, should expect it after radar detection */
+       u8 stop_tx;
+       /* The target channel tx status 1-stopped 0-open*/
+       u8 post_switch_tx_disable;
+
+       u8 padding[3];
+} __packed;
+
 int wl1271_cmd_general_parms(struct wl1271 *wl);
 int wl128x_cmd_general_parms(struct wl1271 *wl);
 int wl1271_cmd_radio_parms(struct wl1271 *wl);
 int wl128x_cmd_radio_parms(struct wl1271 *wl);
 int wl1271_cmd_ext_radio_parms(struct wl1271 *wl);
+int wl12xx_cmd_channel_switch(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch);
 
 #endif /* __WL12XX_CMD_H__ */
diff --git a/drivers/net/wireless/ti/wl12xx/event.c b/drivers/net/wireless/ti/wl12xx/event.c
new file mode 100644 (file)
index 0000000..6ac0ed7
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "event.h"
+#include "scan.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+
+int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout)
+{
+       u32 local_event;
+
+       switch (event) {
+       case WLCORE_EVENT_ROLE_STOP_COMPLETE:
+               local_event = ROLE_STOP_COMPLETE_EVENT_ID;
+               break;
+
+       case WLCORE_EVENT_PEER_REMOVE_COMPLETE:
+               local_event = PEER_REMOVE_COMPLETE_EVENT_ID;
+               break;
+
+       default:
+               /* event not implemented */
+               return 0;
+       }
+       return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
+}
+
+int wl12xx_process_mailbox_events(struct wl1271 *wl)
+{
+       struct wl12xx_event_mailbox *mbox = wl->mbox;
+       u32 vector;
+
+
+       vector = le32_to_cpu(mbox->events_vector);
+       vector &= ~(le32_to_cpu(mbox->events_mask));
+
+       wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector);
+
+       if (vector & SCAN_COMPLETE_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT, "status: 0x%x",
+                            mbox->scheduled_scan_status);
+
+               if (wl->scan_wlvif)
+                       wl12xx_scan_completed(wl, wl->scan_wlvif);
+       }
+
+       if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT,
+                            "PERIODIC_SCAN_REPORT_EVENT (status 0x%0x)",
+                            mbox->scheduled_scan_status);
+
+               wlcore_scan_sched_scan_results(wl);
+       }
+
+       if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID)
+               wlcore_event_sched_scan_completed(wl,
+                                                 mbox->scheduled_scan_status);
+       if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
+               wlcore_event_soft_gemini_sense(wl,
+                                              mbox->soft_gemini_sense_info);
+
+       if (vector & BSS_LOSE_EVENT_ID)
+               wlcore_event_beacon_loss(wl, 0xff);
+
+       if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID)
+               wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric);
+
+       if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)
+               wlcore_event_ba_rx_constraint(wl,
+                                             BIT(mbox->role_id),
+                                             mbox->rx_ba_allowed);
+
+       if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID)
+               wlcore_event_channel_switch(wl, 0xff,
+                                           mbox->channel_switch_status);
+
+       if (vector & DUMMY_PACKET_EVENT_ID)
+               wlcore_event_dummy_packet(wl);
+
+       /*
+        * "TX retries exceeded" has a different meaning according to mode.
+        * In AP mode the offending station is disconnected.
+        */
+       if (vector & MAX_TX_RETRY_EVENT_ID)
+               wlcore_event_max_tx_failure(wl,
+                               le16_to_cpu(mbox->sta_tx_retry_exceeded));
+
+       if (vector & INACTIVE_STA_EVENT_ID)
+               wlcore_event_inactive_sta(wl,
+                                         le16_to_cpu(mbox->sta_aging_status));
+
+       if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
+               wlcore_event_roc_complete(wl);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/ti/wl12xx/event.h b/drivers/net/wireless/ti/wl12xx/event.h
new file mode 100644 (file)
index 0000000..a5cc3fc
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_EVENT_H__
+#define __WL12XX_EVENT_H__
+
+#include "../wlcore/wlcore.h"
+
+enum {
+       MEASUREMENT_START_EVENT_ID               = BIT(8),
+       MEASUREMENT_COMPLETE_EVENT_ID            = BIT(9),
+       SCAN_COMPLETE_EVENT_ID                   = BIT(10),
+       WFD_DISCOVERY_COMPLETE_EVENT_ID          = BIT(11),
+       AP_DISCOVERY_COMPLETE_EVENT_ID           = BIT(12),
+       RESERVED1                                = BIT(13),
+       PSPOLL_DELIVERY_FAILURE_EVENT_ID         = BIT(14),
+       ROLE_STOP_COMPLETE_EVENT_ID              = BIT(15),
+       RADAR_DETECTED_EVENT_ID                  = BIT(16),
+       CHANNEL_SWITCH_COMPLETE_EVENT_ID         = BIT(17),
+       BSS_LOSE_EVENT_ID                        = BIT(18),
+       REGAINED_BSS_EVENT_ID                    = BIT(19),
+       MAX_TX_RETRY_EVENT_ID                    = BIT(20),
+       DUMMY_PACKET_EVENT_ID                    = BIT(21),
+       SOFT_GEMINI_SENSE_EVENT_ID               = BIT(22),
+       CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID        = BIT(23),
+       SOFT_GEMINI_AVALANCHE_EVENT_ID           = BIT(24),
+       PLT_RX_CALIBRATION_COMPLETE_EVENT_ID     = BIT(25),
+       INACTIVE_STA_EVENT_ID                    = BIT(26),
+       PEER_REMOVE_COMPLETE_EVENT_ID            = BIT(27),
+       PERIODIC_SCAN_COMPLETE_EVENT_ID          = BIT(28),
+       PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(29),
+       BA_SESSION_RX_CONSTRAINT_EVENT_ID        = BIT(30),
+       REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(31),
+};
+
+struct wl12xx_event_mailbox {
+       __le32 events_vector;
+       __le32 events_mask;
+       __le32 reserved_1;
+       __le32 reserved_2;
+
+       u8 number_of_scan_results;
+       u8 scan_tag;
+       u8 completed_scan_status;
+       u8 reserved_3;
+
+       u8 soft_gemini_sense_info;
+       u8 soft_gemini_protective_info;
+       s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
+       u8 change_auto_mode_timeout;
+       u8 scheduled_scan_status;
+       u8 reserved4;
+       /* tuned channel (roc) */
+       u8 roc_channel;
+
+       __le16 hlid_removed_bitmap;
+
+       /* bitmap of aged stations (by HLID) */
+       __le16 sta_aging_status;
+
+       /* bitmap of stations (by HLID) which exceeded max tx retries */
+       __le16 sta_tx_retry_exceeded;
+
+       /* discovery completed results */
+       u8 discovery_tag;
+       u8 number_of_preq_results;
+       u8 number_of_prsp_results;
+       u8 reserved_5;
+
+       /* rx ba constraint */
+       u8 role_id; /* 0xFF means any role. */
+       u8 rx_ba_allowed;
+       u8 reserved_6[2];
+
+       /* Channel switch results */
+
+       u8 channel_switch_role_id;
+       u8 channel_switch_status;
+       u8 reserved_7[2];
+
+       u8 ps_poll_delivery_failure_role_ids;
+       u8 stopped_role_ids;
+       u8 started_role_ids;
+
+       u8 reserved_8[9];
+} __packed;
+
+int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout);
+int wl12xx_process_mailbox_events(struct wl1271 *wl);
+
+#endif
+
index e5f5f8f391447ce3ea74a525c5e5a30090b849c6..1c627da85083949c9dd9837e09b53b5ca8a9ac74 100644 (file)
@@ -38,6 +38,8 @@
 #include "reg.h"
 #include "cmd.h"
 #include "acx.h"
+#include "scan.h"
+#include "event.h"
 #include "debugfs.h"
 
 static char *fref_param;
@@ -208,6 +210,8 @@ static struct wlcore_conf wl12xx_conf = {
                .tmpl_short_retry_limit      = 10,
                .tmpl_long_retry_limit       = 10,
                .tx_watchdog_timeout         = 5000,
+               .slow_link_thold             = 3,
+               .fast_link_thold             = 10,
        },
        .conn = {
                .wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
@@ -265,8 +269,10 @@ static struct wlcore_conf wl12xx_conf = {
        .scan = {
                .min_dwell_time_active        = 7500,
                .max_dwell_time_active        = 30000,
-               .min_dwell_time_passive       = 100000,
-               .max_dwell_time_passive       = 100000,
+               .min_dwell_time_active_long   = 25000,
+               .max_dwell_time_active_long   = 50000,
+               .dwell_time_passive           = 100000,
+               .dwell_time_dfs               = 150000,
                .num_probe_reqs               = 2,
                .split_scan_timeout           = 50000,
        },
@@ -368,6 +374,10 @@ static struct wlcore_conf wl12xx_conf = {
                .increase_time              = 1,
                .window_size                = 16,
        },
+       .recovery = {
+               .bug_on_recovery            = 0,
+               .no_recovery                = 0,
+       },
 };
 
 static struct wl12xx_priv_conf wl12xx_default_priv_conf = {
@@ -601,9 +611,9 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
 {
        int ret;
 
-       if (wl->chip.id != CHIP_ID_1283_PG20) {
+       if (wl->chip.id != CHIP_ID_128X_PG20) {
                struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
-               struct wl127x_rx_mem_pool_addr rx_mem_addr;
+               struct wl12xx_priv *priv = wl->priv;
 
                /*
                 * Choose the block we want to read
@@ -612,13 +622,13 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
                 */
                u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK;
 
-               rx_mem_addr.addr = (mem_block << 8) +
+               priv->rx_mem_addr->addr = (mem_block << 8) +
                        le32_to_cpu(wl_mem_map->packet_memory_pool_start);
 
-               rx_mem_addr.addr_extra = rx_mem_addr.addr + 4;
+               priv->rx_mem_addr->addr_extra = priv->rx_mem_addr->addr + 4;
 
-               ret = wlcore_write(wl, WL1271_SLV_REG_DATA, &rx_mem_addr,
-                                  sizeof(rx_mem_addr), false);
+               ret = wlcore_write(wl, WL1271_SLV_REG_DATA, priv->rx_mem_addr,
+                                  sizeof(*priv->rx_mem_addr), false);
                if (ret < 0)
                        return ret;
        }
@@ -631,13 +641,15 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
        int ret = 0;
 
        switch (wl->chip.id) {
-       case CHIP_ID_1271_PG10:
+       case CHIP_ID_127X_PG10:
                wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
                               wl->chip.id);
 
                wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
                              WLCORE_QUIRK_DUAL_PROBE_TMPL |
-                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE |
+                             WLCORE_QUIRK_START_STA_FAILS |
+                             WLCORE_QUIRK_AP_ZERO_SESSION_ID;
                wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL127X_FW_NAME_MULTI;
                memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
@@ -646,18 +658,22 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                /* read data preparation is only needed by wl127x */
                wl->ops->prepare_read = wl127x_prepare_read;
 
-               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER,
-                                     WL127X_MAJOR_VER, WL127X_SUBTYPE_VER,
-                                     WL127X_MINOR_VER);
+               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
+                             WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER,
+                             WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
+                             WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER,
+                             WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
                break;
 
-       case CHIP_ID_1271_PG20:
+       case CHIP_ID_127X_PG20:
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
                             wl->chip.id);
 
                wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
                              WLCORE_QUIRK_DUAL_PROBE_TMPL |
-                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE |
+                             WLCORE_QUIRK_START_STA_FAILS |
+                             WLCORE_QUIRK_AP_ZERO_SESSION_ID;
                wl->plt_fw_name = WL127X_PLT_FW_NAME;
                wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL127X_FW_NAME_MULTI;
@@ -667,12 +683,14 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                /* read data preparation is only needed by wl127x */
                wl->ops->prepare_read = wl127x_prepare_read;
 
-               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER,
-                                     WL127X_MAJOR_VER, WL127X_SUBTYPE_VER,
-                                     WL127X_MINOR_VER);
+               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
+                             WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER,
+                             WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
+                             WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER,
+                             WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
                break;
 
-       case CHIP_ID_1283_PG20:
+       case CHIP_ID_128X_PG20:
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
                             wl->chip.id);
                wl->plt_fw_name = WL128X_PLT_FW_NAME;
@@ -682,19 +700,30 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                /* wl128x requires TX blocksize alignment */
                wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
                              WLCORE_QUIRK_DUAL_PROBE_TMPL |
-                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
-
-               wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER,
-                                     WL128X_MAJOR_VER, WL128X_SUBTYPE_VER,
-                                     WL128X_MINOR_VER);
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE |
+                             WLCORE_QUIRK_START_STA_FAILS |
+                             WLCORE_QUIRK_AP_ZERO_SESSION_ID;
+
+               wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER,
+                             WL128X_IFTYPE_SR_VER,  WL128X_MAJOR_SR_VER,
+                             WL128X_SUBTYPE_SR_VER, WL128X_MINOR_SR_VER,
+                             WL128X_IFTYPE_MR_VER,  WL128X_MAJOR_MR_VER,
+                             WL128X_SUBTYPE_MR_VER, WL128X_MINOR_MR_VER);
                break;
-       case CHIP_ID_1283_PG10:
+       case CHIP_ID_128X_PG10:
        default:
                wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
                ret = -ENODEV;
                goto out;
        }
 
+       /* common settings */
+       wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY;
+       wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY;
+       wl->sched_scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
+       wl->sched_scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+       wl->max_channels_5 = WL12XX_MAX_CHANNELS_5GHZ;
+       wl->ba_rx_session_count_max = WL12XX_RX_BA_MAX_SESSIONS;
 out:
        return ret;
 }
@@ -1067,7 +1096,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
        u32 clk;
        int selected_clock = -1;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                ret = wl128x_boot_clk(wl, &selected_clock);
                if (ret < 0)
                        goto out;
@@ -1098,7 +1127,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
 
        wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
 
-       if (wl->chip.id == CHIP_ID_1283_PG20)
+       if (wl->chip.id == CHIP_ID_128X_PG20)
                clk |= ((selected_clock & 0x3) << 1) << 4;
        else
                clk |= (priv->ref_clock << 1) << 4;
@@ -1152,7 +1181,7 @@ static int wl12xx_pre_upload(struct wl1271 *wl)
        /* WL1271: The reference driver skips steps 7 to 10 (jumps directly
         * to upload_fw) */
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
                if (ret < 0)
                        goto out;
@@ -1219,6 +1248,23 @@ static int wl12xx_boot(struct wl1271 *wl)
        if (ret < 0)
                goto out;
 
+       wl->event_mask = BSS_LOSE_EVENT_ID |
+               REGAINED_BSS_EVENT_ID |
+               SCAN_COMPLETE_EVENT_ID |
+               ROLE_STOP_COMPLETE_EVENT_ID |
+               RSSI_SNR_TRIGGER_0_EVENT_ID |
+               PSPOLL_DELIVERY_FAILURE_EVENT_ID |
+               SOFT_GEMINI_SENSE_EVENT_ID |
+               PERIODIC_SCAN_REPORT_EVENT_ID |
+               PERIODIC_SCAN_COMPLETE_EVENT_ID |
+               DUMMY_PACKET_EVENT_ID |
+               PEER_REMOVE_COMPLETE_EVENT_ID |
+               BA_SESSION_RX_CONSTRAINT_EVENT_ID |
+               REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
+               INACTIVE_STA_EVENT_ID |
+               MAX_TX_RETRY_EVENT_ID |
+               CHANNEL_SWITCH_COMPLETE_EVENT_ID;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -1261,7 +1307,7 @@ static void
 wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
                          u32 blks, u32 spare_blks)
 {
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                desc->wl128x_mem.total_mem_blocks = blks;
        } else {
                desc->wl127x_mem.extra_blocks = spare_blks;
@@ -1275,7 +1321,7 @@ wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
 {
        u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len);
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                desc->wl128x_mem.extra_bytes = aligned_len - skb->len;
                desc->length = cpu_to_le16(aligned_len >> 2);
 
@@ -1339,7 +1385,7 @@ static int wl12xx_hw_init(struct wl1271 *wl)
 {
        int ret;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
 
                ret = wl128x_cmd_general_parms(wl);
@@ -1394,22 +1440,6 @@ static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,
        return wlvif->rate_set;
 }
 
-static int wl12xx_identify_fw(struct wl1271 *wl)
-{
-       unsigned int *fw_ver = wl->chip.fw_ver;
-
-       /* Only new station firmwares support routing fw logs to the host */
-       if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
-           (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN))
-               wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED;
-
-       /* This feature is not yet supported for AP mode */
-       if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP)
-               wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED;
-
-       return 0;
-}
-
 static void wl12xx_conf_init(struct wl1271 *wl)
 {
        struct wl12xx_priv *priv = wl->priv;
@@ -1426,7 +1456,7 @@ static bool wl12xx_mac_in_fuse(struct wl1271 *wl)
        bool supported = false;
        u8 major, minor;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver);
                minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver);
 
@@ -1482,7 +1512,7 @@ static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
        u16 die_info;
        int ret;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20)
+       if (wl->chip.id == CHIP_ID_128X_PG20)
                ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1,
                                          &die_info);
        else
@@ -1589,16 +1619,46 @@ static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
        return wlcore_set_key(wl, cmd, vif, sta, key_conf);
 }
 
+static int wl12xx_set_peer_cap(struct wl1271 *wl,
+                              struct ieee80211_sta_ht_cap *ht_cap,
+                              bool allow_ht_operation,
+                              u32 rate_set, u8 hlid)
+{
+       return wl1271_acx_set_ht_capabilities(wl, ht_cap, allow_ht_operation,
+                                             hlid);
+}
+
+static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+                                struct wl1271_link *lnk)
+{
+       u8 thold;
+
+       if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map))
+               thold = wl->conf.tx.fast_link_thold;
+       else
+               thold = wl->conf.tx.slow_link_thold;
+
+       return lnk->allocated_pkts < thold;
+}
+
+static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
+                               struct wl1271_link *lnk)
+{
+       /* any link is good for low priority */
+       return true;
+}
+
 static int wl12xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl12xx_ops = {
        .setup                  = wl12xx_setup,
        .identify_chip          = wl12xx_identify_chip,
-       .identify_fw            = wl12xx_identify_fw,
        .boot                   = wl12xx_boot,
        .plt_init               = wl12xx_plt_init,
        .trigger_cmd            = wl12xx_trigger_cmd,
        .ack_event              = wl12xx_ack_event,
+       .wait_for_event         = wl12xx_wait_for_event,
+       .process_mailbox_events = wl12xx_process_mailbox_events,
        .calc_tx_blocks         = wl12xx_calc_tx_blocks,
        .set_tx_desc_blocks     = wl12xx_set_tx_desc_blocks,
        .set_tx_desc_data_len   = wl12xx_set_tx_desc_data_len,
@@ -1615,9 +1675,17 @@ static struct wlcore_ops wl12xx_ops = {
        .set_rx_csum            = NULL,
        .ap_get_mimo_wide_rate_mask = NULL,
        .debugfs_init           = wl12xx_debugfs_add_files,
+       .scan_start             = wl12xx_scan_start,
+       .scan_stop              = wl12xx_scan_stop,
+       .sched_scan_start       = wl12xx_sched_scan_start,
+       .sched_scan_stop        = wl12xx_scan_sched_scan_stop,
        .get_spare_blocks       = wl12xx_get_spare_blocks,
        .set_key                = wl12xx_set_key,
+       .channel_switch         = wl12xx_cmd_channel_switch,
        .pre_pkt_send           = NULL,
+       .set_peer_cap           = wl12xx_set_peer_cap,
+       .lnk_high_prio          = wl12xx_lnk_high_prio,
+       .lnk_low_prio           = wl12xx_lnk_low_prio,
 };
 
 static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
@@ -1636,11 +1704,13 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
 static int wl12xx_setup(struct wl1271 *wl)
 {
        struct wl12xx_priv *priv = wl->priv;
-       struct wl12xx_platform_data *pdata = wl->pdev->dev.platform_data;
+       struct wlcore_platdev_data *pdev_data = wl->pdev->dev.platform_data;
+       struct wl12xx_platform_data *pdata = pdev_data->pdata;
 
        wl->rtable = wl12xx_rtable;
        wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
        wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
+       wl->num_channels = 1;
        wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;
        wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
@@ -1693,6 +1763,10 @@ static int wl12xx_setup(struct wl1271 *wl)
                        wl1271_error("Invalid tcxo parameter %s", tcxo_param);
        }
 
+       priv->rx_mem_addr = kmalloc(sizeof(*priv->rx_mem_addr), GFP_KERNEL);
+       if (!priv->rx_mem_addr)
+               return -ENOMEM;
+
        return 0;
 }
 
@@ -1703,7 +1777,8 @@ static int wl12xx_probe(struct platform_device *pdev)
        int ret;
 
        hw = wlcore_alloc_hw(sizeof(struct wl12xx_priv),
-                            WL12XX_AGGR_BUFFER_SIZE);
+                            WL12XX_AGGR_BUFFER_SIZE,
+                            sizeof(struct wl12xx_event_mailbox));
        if (IS_ERR(hw)) {
                wl1271_error("can't allocate hw");
                ret = PTR_ERR(hw);
@@ -1725,6 +1800,21 @@ out:
        return ret;
 }
 
+static int wl12xx_remove(struct platform_device *pdev)
+{
+       struct wl1271 *wl = platform_get_drvdata(pdev);
+       struct wl12xx_priv *priv;
+
+       if (!wl)
+               goto out;
+       priv = wl->priv;
+
+       kfree(priv->rx_mem_addr);
+
+out:
+       return wlcore_remove(pdev);
+}
+
 static const struct platform_device_id wl12xx_id_table[] = {
        { "wl12xx", 0 },
        {  } /* Terminating Entry */
@@ -1733,7 +1823,7 @@ MODULE_DEVICE_TABLE(platform, wl12xx_id_table);
 
 static struct platform_driver wl12xx_driver = {
        .probe          = wl12xx_probe,
-       .remove         = wlcore_remove,
+       .remove         = wl12xx_remove,
        .id_table       = wl12xx_id_table,
        .driver = {
                .name   = "wl12xx_driver",
diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c
new file mode 100644 (file)
index 0000000..affdb3e
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/ieee80211.h>
+#include "scan.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/tx.h"
+
+static int wl1271_get_scan_channels(struct wl1271 *wl,
+                                   struct cfg80211_scan_request *req,
+                                   struct basic_scan_channel_params *channels,
+                                   enum ieee80211_band band, bool passive)
+{
+       struct conf_scan_settings *c = &wl->conf.scan;
+       int i, j;
+       u32 flags;
+
+       for (i = 0, j = 0;
+            i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
+            i++) {
+               flags = req->channels[i]->flags;
+
+               if (!test_bit(i, wl->scan.scanned_ch) &&
+                   !(flags & IEEE80211_CHAN_DISABLED) &&
+                   (req->channels[i]->band == band) &&
+                   /*
+                    * In passive scans, we scan all remaining
+                    * channels, even if not marked as such.
+                    * In active scans, we only scan channels not
+                    * marked as passive.
+                    */
+                   (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) {
+                       wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
+                                    req->channels[i]->band,
+                                    req->channels[i]->center_freq);
+                       wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
+                                    req->channels[i]->hw_value,
+                                    req->channels[i]->flags);
+                       wl1271_debug(DEBUG_SCAN,
+                                    "max_antenna_gain %d, max_power %d",
+                                    req->channels[i]->max_antenna_gain,
+                                    req->channels[i]->max_power);
+                       wl1271_debug(DEBUG_SCAN, "beacon_found %d",
+                                    req->channels[i]->beacon_found);
+
+                       if (!passive) {
+                               channels[j].min_duration =
+                                       cpu_to_le32(c->min_dwell_time_active);
+                               channels[j].max_duration =
+                                       cpu_to_le32(c->max_dwell_time_active);
+                       } else {
+                               channels[j].min_duration =
+                                       cpu_to_le32(c->dwell_time_passive);
+                               channels[j].max_duration =
+                                       cpu_to_le32(c->dwell_time_passive);
+                       }
+                       channels[j].early_termination = 0;
+                       channels[j].tx_power_att = req->channels[i]->max_power;
+                       channels[j].channel = req->channels[i]->hw_value;
+
+                       memset(&channels[j].bssid_lsb, 0xff, 4);
+                       memset(&channels[j].bssid_msb, 0xff, 2);
+
+                       /* Mark the channels we already used */
+                       set_bit(i, wl->scan.scanned_ch);
+
+                       j++;
+               }
+       }
+
+       return j;
+}
+
+#define WL1271_NOTHING_TO_SCAN 1
+
+static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           enum ieee80211_band band,
+                           bool passive, u32 basic_rate)
+{
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct wl1271_cmd_scan *cmd;
+       struct wl1271_cmd_trigger_scan_to *trigger;
+       int ret;
+       u16 scan_options = 0;
+
+       /* skip active scans if we don't have SSIDs */
+       if (!passive && wl->scan.req->n_ssids == 0)
+               return WL1271_NOTHING_TO_SCAN;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
+       if (!cmd || !trigger) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (wl->conf.scan.split_scan_timeout)
+               scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN;
+
+       if (passive)
+               scan_options |= WL1271_SCAN_OPT_PASSIVE;
+
+       cmd->params.role_id = wlvif->role_id;
+
+       if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cmd->params.scan_options = cpu_to_le16(scan_options);
+
+       cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
+                                                   cmd->channels,
+                                                   band, passive);
+       if (cmd->params.n_ch == 0) {
+               ret = WL1271_NOTHING_TO_SCAN;
+               goto out;
+       }
+
+       cmd->params.tx_rate = cpu_to_le32(basic_rate);
+       cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
+       cmd->params.tid_trigger = CONF_TX_AC_ANY_TID;
+       cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
+
+       if (band == IEEE80211_BAND_2GHZ)
+               cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
+       else
+               cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
+
+       if (wl->scan.ssid_len && wl->scan.ssid) {
+               cmd->params.ssid_len = wl->scan.ssid_len;
+               memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
+       }
+
+       memcpy(cmd->addr, vif->addr, ETH_ALEN);
+
+       ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                        cmd->params.role_id, band,
+                                        wl->scan.ssid, wl->scan.ssid_len,
+                                        wl->scan.req->ie,
+                                        wl->scan.req->ie_len, false);
+       if (ret < 0) {
+               wl1271_error("PROBE request template failed");
+               goto out;
+       }
+
+       trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout);
+       ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
+                             sizeof(*trigger), 0);
+       if (ret < 0) {
+               wl1271_error("trigger scan to failed for hw scan");
+               goto out;
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+
+       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN failed");
+               goto out;
+       }
+
+out:
+       kfree(cmd);
+       kfree(trigger);
+       return ret;
+}
+
+int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       struct wl1271_cmd_header *cmd = NULL;
+       int ret = 0;
+
+       if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
+               return -EINVAL;
+
+       wl1271_debug(DEBUG_CMD, "cmd scan stop");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd,
+                             sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("cmd stop_scan failed");
+               goto out;
+       }
+out:
+       kfree(cmd);
+       return ret;
+}
+
+void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       int ret = 0;
+       enum ieee80211_band band;
+       u32 rate, mask;
+
+       switch (wl->scan.state) {
+       case WL1271_SCAN_STATE_IDLE:
+               break;
+
+       case WL1271_SCAN_STATE_2GHZ_ACTIVE:
+               band = IEEE80211_BAND_2GHZ;
+               mask = wlvif->bitrate_masks[band];
+               if (wl->scan.req->no_cck) {
+                       mask &= ~CONF_TX_CCK_RATES;
+                       if (!mask)
+                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
+               }
+               rate = wl1271_tx_min_rate_get(wl, mask);
+               ret = wl1271_scan_send(wl, wlvif, band, false, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_2GHZ_PASSIVE:
+               band = IEEE80211_BAND_2GHZ;
+               mask = wlvif->bitrate_masks[band];
+               if (wl->scan.req->no_cck) {
+                       mask &= ~CONF_TX_CCK_RATES;
+                       if (!mask)
+                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
+               }
+               rate = wl1271_tx_min_rate_get(wl, mask);
+               ret = wl1271_scan_send(wl, wlvif, band, true, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       if (wl->enable_11a)
+                               wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
+                       else
+                               wl->scan.state = WL1271_SCAN_STATE_DONE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_5GHZ_ACTIVE:
+               band = IEEE80211_BAND_5GHZ;
+               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
+               ret = wl1271_scan_send(wl, wlvif, band, false, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_5GHZ_PASSIVE:
+               band = IEEE80211_BAND_5GHZ;
+               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
+               ret = wl1271_scan_send(wl, wlvif, band, true, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       wl->scan.state = WL1271_SCAN_STATE_DONE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_DONE:
+               wl->scan.failed = false;
+               cancel_delayed_work(&wl->scan_complete_work);
+               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                            msecs_to_jiffies(0));
+               break;
+
+       default:
+               wl1271_error("invalid scan state");
+               break;
+       }
+
+       if (ret < 0) {
+               cancel_delayed_work(&wl->scan_complete_work);
+               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                            msecs_to_jiffies(0));
+       }
+}
+
+static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd,
+                                  struct wlcore_scan_channels *cmd_channels)
+{
+       memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive));
+       memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active));
+       cmd->dfs = cmd_channels->dfs;
+       cmd->n_pactive_ch = cmd_channels->passive_active;
+
+       memcpy(cmd->channels_2, cmd_channels->channels_2,
+              sizeof(cmd->channels_2));
+       memcpy(cmd->channels_5, cmd_channels->channels_5,
+              sizeof(cmd->channels_2));
+       /* channels_4 are not supported, so no need to copy them */
+}
+
+int wl1271_scan_sched_scan_config(struct wl1271 *wl,
+                                 struct wl12xx_vif *wlvif,
+                                 struct cfg80211_sched_scan_request *req,
+                                 struct ieee80211_sched_scan_ies *ies)
+{
+       struct wl1271_cmd_sched_scan_config *cfg = NULL;
+       struct wlcore_scan_channels *cfg_channels = NULL;
+       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
+       int i, ret;
+       bool force_passive = !req->n_ssids;
+
+       wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
+
+       cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+       if (!cfg)
+               return -ENOMEM;
+
+       cfg->role_id = wlvif->role_id;
+       cfg->rssi_threshold = c->rssi_threshold;
+       cfg->snr_threshold  = c->snr_threshold;
+       cfg->n_probe_reqs = c->num_probe_reqs;
+       /* cycles set to 0 it means infinite (until manually stopped) */
+       cfg->cycles = 0;
+       /* report APs when at least 1 is found */
+       cfg->report_after = 1;
+       /* don't stop scanning automatically when something is found */
+       cfg->terminate = 0;
+       cfg->tag = WL1271_SCAN_DEFAULT_TAG;
+       /* don't filter on BSS type */
+       cfg->bss_type = SCAN_BSS_TYPE_ANY;
+       /* currently NL80211 supports only a single interval */
+       for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
+               cfg->intervals[i] = cpu_to_le32(req->interval);
+
+       cfg->ssid_len = 0;
+       ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
+       if (ret < 0)
+               goto out;
+
+       cfg->filter_type = ret;
+
+       wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
+
+       cfg_channels = kzalloc(sizeof(*cfg_channels), GFP_KERNEL);
+       if (!cfg_channels) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (!wlcore_set_scan_chan_params(wl, cfg_channels, req->channels,
+                                        req->n_channels, req->n_ssids,
+                                        SCAN_TYPE_PERIODIC)) {
+               wl1271_error("scan channel list is empty");
+               ret = -EINVAL;
+               goto out;
+       }
+       wl12xx_adjust_channels(cfg, cfg_channels);
+
+       if (!force_passive && cfg->active[0]) {
+               u8 band = IEEE80211_BAND_2GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                                wlvif->role_id, band,
+                                                req->ssids[0].ssid,
+                                                req->ssids[0].ssid_len,
+                                                ies->ie[band],
+                                                ies->len[band], true);
+               if (ret < 0) {
+                       wl1271_error("2.4GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       if (!force_passive && cfg->active[1]) {
+               u8 band = IEEE80211_BAND_5GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                                wlvif->role_id, band,
+                                                req->ssids[0].ssid,
+                                                req->ssids[0].ssid_len,
+                                                ies->ie[band],
+                                                ies->len[band], true);
+               if (ret < 0) {
+                       wl1271_error("5GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg));
+
+       ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg,
+                             sizeof(*cfg), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN configuration failed");
+               goto out;
+       }
+out:
+       kfree(cfg_channels);
+       kfree(cfg);
+       return ret;
+}
+
+int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       struct wl1271_cmd_sched_scan_start *start;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
+
+       if (wlvif->bss_type != BSS_TYPE_STA_BSS)
+               return -EOPNOTSUPP;
+
+       if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) &&
+           test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
+               return -EBUSY;
+
+       start = kzalloc(sizeof(*start), GFP_KERNEL);
+       if (!start)
+               return -ENOMEM;
+
+       start->role_id = wlvif->role_id;
+       start->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
+                             sizeof(*start), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send scan start command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(start);
+       return ret;
+}
+
+int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif  *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies)
+{
+       int ret;
+
+       ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
+       if (ret < 0)
+               return ret;
+
+       return wl1271_scan_sched_scan_start(wl, wlvif);
+}
+
+void wl12xx_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif)
+{
+       struct wl1271_cmd_sched_scan_stop *stop;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
+
+       /* FIXME: what to do if alloc'ing to stop fails? */
+       stop = kzalloc(sizeof(*stop), GFP_KERNEL);
+       if (!stop) {
+               wl1271_error("failed to alloc memory to send sched scan stop");
+               return;
+       }
+
+       stop->role_id = wlvif->role_id;
+       stop->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
+                             sizeof(*stop), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send sched scan stop command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(stop);
+}
+
+int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req)
+{
+       wl1271_scan_stm(wl, wlvif);
+       return 0;
+}
+
+void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       wl1271_scan_stm(wl, wlvif);
+}
diff --git a/drivers/net/wireless/ti/wl12xx/scan.h b/drivers/net/wireless/ti/wl12xx/scan.h
new file mode 100644 (file)
index 0000000..264af7a
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_SCAN_H__
+#define __WL12XX_SCAN_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/scan.h"
+
+#define WL12XX_MAX_CHANNELS_5GHZ 23
+
+struct basic_scan_params {
+       /* Scan option flags (WL1271_SCAN_OPT_*) */
+       __le16 scan_options;
+       u8 role_id;
+       /* Number of scan channels in the list (maximum 30) */
+       u8 n_ch;
+       /* This field indicates the number of probe requests to send
+          per channel for an active scan */
+       u8 n_probe_reqs;
+       u8 tid_trigger;
+       u8 ssid_len;
+       u8 use_ssid_list;
+
+       /* Rate bit field for sending the probes */
+       __le32 tx_rate;
+
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+       /* Band to scan */
+       u8 band;
+
+       u8 scan_tag;
+       u8 padding2[2];
+} __packed;
+
+struct basic_scan_channel_params {
+       /* Duration in TU to wait for frames on a channel for active scan */
+       __le32 min_duration;
+       __le32 max_duration;
+       __le32 bssid_lsb;
+       __le16 bssid_msb;
+       u8 early_termination;
+       u8 tx_power_att;
+       u8 channel;
+       /* FW internal use only! */
+       u8 dfs_candidate;
+       u8 activity_detected;
+       u8 pad;
+} __packed;
+
+struct wl1271_cmd_scan {
+       struct wl1271_cmd_header header;
+
+       struct basic_scan_params params;
+       struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
+
+       /* src mac address */
+       u8 addr[ETH_ALEN];
+       u8 padding[2];
+} __packed;
+
+struct wl1271_cmd_sched_scan_config {
+       struct wl1271_cmd_header header;
+
+       __le32 intervals[SCAN_MAX_CYCLE_INTERVALS];
+
+       s8 rssi_threshold; /* for filtering (in dBm) */
+       s8 snr_threshold;  /* for filtering (in dB) */
+
+       u8 cycles;       /* maximum number of scan cycles */
+       u8 report_after; /* report when this number of results are received */
+       u8 terminate;    /* stop scanning after reporting */
+
+       u8 tag;
+       u8 bss_type; /* for filtering */
+       u8 filter_type;
+
+       u8 ssid_len;     /* For SCAN_SSID_FILTER_SPECIFIC */
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+
+       u8 n_probe_reqs; /* Number of probes requests per channel */
+
+       u8 passive[SCAN_MAX_BANDS];
+       u8 active[SCAN_MAX_BANDS];
+
+       u8 dfs;
+
+       u8 n_pactive_ch; /* number of pactive (passive until fw detects energy)
+                           channels in BG band */
+       u8 role_id;
+       u8 padding[1];
+       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+       struct conn_scan_ch_params channels_5[WL12XX_MAX_CHANNELS_5GHZ];
+       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+} __packed;
+
+struct wl1271_cmd_sched_scan_start {
+       struct wl1271_cmd_header header;
+
+       u8 tag;
+       u8 role_id;
+       u8 padding[2];
+} __packed;
+
+struct wl1271_cmd_sched_scan_stop {
+       struct wl1271_cmd_header header;
+
+       u8 tag;
+       u8 role_id;
+       u8 padding[2];
+} __packed;
+
+int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req);
+int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif  *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies);
+void wl12xx_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif);
+#endif
index 7182bbf6625daac99ddcf00b3581c32ee39c2ef6..222d03540200498a9400c9dc867159780bdf9311 100644 (file)
 
 #include "conf.h"
 
-/* minimum FW required for driver for wl127x */
+/* WiLink 6/7 chip IDs */
+#define CHIP_ID_127X_PG10              (0x04030101)
+#define CHIP_ID_127X_PG20              (0x04030111)
+#define CHIP_ID_128X_PG10              (0x05030101)
+#define CHIP_ID_128X_PG20              (0x05030111)
+
+/* FW chip version for wl127x */
 #define WL127X_CHIP_VER                6
-#define WL127X_IFTYPE_VER      3
-#define WL127X_MAJOR_VER       10
-#define WL127X_SUBTYPE_VER     2
-#define WL127X_MINOR_VER       115
+/* minimum single-role FW version for wl127x */
+#define WL127X_IFTYPE_SR_VER   3
+#define WL127X_MAJOR_SR_VER    10
+#define WL127X_SUBTYPE_SR_VER  WLCORE_FW_VER_IGNORE
+#define WL127X_MINOR_SR_VER    115
+/* minimum multi-role FW version for wl127x */
+#define WL127X_IFTYPE_MR_VER   5
+#define WL127X_MAJOR_MR_VER    7
+#define WL127X_SUBTYPE_MR_VER  WLCORE_FW_VER_IGNORE
+#define WL127X_MINOR_MR_VER    115
 
-/* minimum FW required for driver for wl128x */
+/* FW chip version for wl128x */
 #define WL128X_CHIP_VER                7
-#define WL128X_IFTYPE_VER      3
-#define WL128X_MAJOR_VER       10
-#define WL128X_SUBTYPE_VER     2
-#define WL128X_MINOR_VER       115
+/* minimum single-role FW version for wl128x */
+#define WL128X_IFTYPE_SR_VER   3
+#define WL128X_MAJOR_SR_VER    10
+#define WL128X_SUBTYPE_SR_VER  WLCORE_FW_VER_IGNORE
+#define WL128X_MINOR_SR_VER    115
+/* minimum multi-role FW version for wl128x */
+#define WL128X_IFTYPE_MR_VER   5
+#define WL128X_MAJOR_MR_VER    7
+#define WL128X_SUBTYPE_MR_VER  WLCORE_FW_VER_IGNORE
+#define WL128X_MINOR_MR_VER    42
 
 #define WL12XX_AGGR_BUFFER_SIZE        (4 * PAGE_SIZE)
 
@@ -45,6 +63,8 @@
 
 #define WL12XX_NUM_MAC_ADDRESSES 2
 
+#define WL12XX_RX_BA_MAX_SESSIONS 3
+
 struct wl127x_rx_mem_pool_addr {
        u32 addr;
        u32 addr_extra;
@@ -55,6 +75,8 @@ struct wl12xx_priv {
 
        int ref_clock;
        int tcxo_clock;
+
+       struct wl127x_rx_mem_pool_addr *rx_mem_addr;
 };
 
 #endif /* __WL12XX_PRIV_H__ */
index 67c098734c7fc2b335707c96ed05d0bc002bbab9..ae2b817357857538c4296b41d1fe8cf3bf4d2674 100644 (file)
@@ -1,3 +1,3 @@
-wl18xx-objs    = main.o acx.o tx.o io.o debugfs.o
+wl18xx-objs    = main.o acx.o tx.o io.o debugfs.o scan.o cmd.o event.o
 
 obj-$(CONFIG_WL18XX)           += wl18xx.o
index 72840e23bf5992462fed5c4266919b600cc6300d..a169bb5a5dbf7984c0cdb52390492e0d6a27fbb0 100644 (file)
@@ -75,7 +75,7 @@ int wl18xx_acx_set_checksum_state(struct wl1271 *wl)
 
        acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED;
 
-       ret = wl1271_cmd_configure(wl, ACX_CHECKSUM_CONFIG, acx, sizeof(*acx));
+       ret = wl1271_cmd_configure(wl, ACX_CSUM_CONFIG, acx, sizeof(*acx));
        if (ret < 0) {
                wl1271_warning("failed to set Tx checksum state: %d", ret);
                goto out;
@@ -109,3 +109,88 @@ out:
        kfree(acx);
        return ret;
 }
+
+int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide)
+{
+       struct wlcore_peer_ht_operation_mode *acx;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "acx peer ht operation mode hlid %d bw %d",
+                    hlid, wide);
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       acx->hlid = hlid;
+       acx->bandwidth = wide ? WLCORE_BANDWIDTH_40MHZ : WLCORE_BANDWIDTH_20MHZ;
+
+       ret = wl1271_cmd_configure(wl, ACX_PEER_HT_OPERATION_MODE_CFG, acx,
+                                  sizeof(*acx));
+
+       if (ret < 0) {
+               wl1271_warning("acx peer ht operation mode failed: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+
+}
+
+/*
+ * this command is basically the same as wl1271_acx_ht_capabilities,
+ * with the addition of supported rates. they should be unified in
+ * the next fw api change
+ */
+int wl18xx_acx_set_peer_cap(struct wl1271 *wl,
+                           struct ieee80211_sta_ht_cap *ht_cap,
+                           bool allow_ht_operation,
+                           u32 rate_set, u8 hlid)
+{
+       struct wlcore_acx_peer_cap *acx;
+       int ret = 0;
+       u32 ht_capabilites = 0;
+
+       wl1271_debug(DEBUG_ACX,
+                    "acx set cap ht_supp: %d ht_cap: %d rates: 0x%x",
+                    ht_cap->ht_supported, ht_cap->cap, rate_set);
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (allow_ht_operation && ht_cap->ht_supported) {
+               /* no need to translate capabilities - use the spec values */
+               ht_capabilites = ht_cap->cap;
+
+               /*
+                * this bit is not employed by the spec but only by FW to
+                * indicate peer HT support
+                */
+               ht_capabilites |= WL12XX_HT_CAP_HT_OPERATION;
+
+               /* get data from A-MPDU parameters field */
+               acx->ampdu_max_length = ht_cap->ampdu_factor;
+               acx->ampdu_min_spacing = ht_cap->ampdu_density;
+       }
+
+       acx->hlid = hlid;
+       acx->ht_capabilites = cpu_to_le32(ht_capabilites);
+       acx->supported_rates = cpu_to_le32(rate_set);
+
+       ret = wl1271_cmd_configure(wl, ACX_PEER_CAP, acx, sizeof(*acx));
+       if (ret < 0) {
+               wl1271_warning("acx ht capabilities setting failed: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+}
index e2609a6b7341d6048b05bc9bcee6071090da0dbc..0e636def1217b9c150f323fcbbc8bd5214bf0386 100644 (file)
 #include "../wlcore/acx.h"
 
 enum {
-       ACX_CLEAR_STATISTICS             = 0x0047,
+       ACX_NS_IPV6_FILTER               = 0x0050,
+       ACX_PEER_HT_OPERATION_MODE_CFG   = 0x0051,
+       ACX_CSUM_CONFIG                  = 0x0052,
+       ACX_SIM_CONFIG                   = 0x0053,
+       ACX_CLEAR_STATISTICS             = 0x0054,
+       ACX_AUTO_RX_STREAMING            = 0x0055,
+       ACX_PEER_CAP                     = 0x0056
 };
 
 /* numbers of bits the length field takes (add 1 for the actual number) */
@@ -278,10 +284,57 @@ struct wl18xx_acx_clear_statistics {
        struct acx_header header;
 };
 
+enum wlcore_bandwidth {
+       WLCORE_BANDWIDTH_20MHZ,
+       WLCORE_BANDWIDTH_40MHZ,
+};
+
+struct wlcore_peer_ht_operation_mode {
+       struct acx_header header;
+
+       u8 hlid;
+       u8 bandwidth; /* enum wlcore_bandwidth */
+       u8 padding[2];
+};
+
+/*
+ * ACX_PEER_CAP
+ * this struct is very similar to wl1271_acx_ht_capabilities, with the
+ * addition of supported rates
+ */
+struct wlcore_acx_peer_cap {
+       struct acx_header header;
+
+       /* bitmask of capability bits supported by the peer */
+       __le32 ht_capabilites;
+
+       /* rates supported by the remote peer */
+       __le32 supported_rates;
+
+       /* Indicates to which link these capabilities apply. */
+       u8 hlid;
+
+       /*
+        * This the maximum A-MPDU length supported by the AP. The FW may not
+        * exceed this length when sending A-MPDUs
+        */
+       u8 ampdu_max_length;
+
+       /* This is the minimal spacing required when sending A-MPDUs to the AP*/
+       u8 ampdu_min_spacing;
+
+       u8 padding;
+} __packed;
+
 int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
                                  u32 sdio_blk_size, u32 extra_mem_blks,
                                  u32 len_field_size);
 int wl18xx_acx_set_checksum_state(struct wl1271 *wl);
 int wl18xx_acx_clear_statistics(struct wl1271 *wl);
+int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide);
+int wl18xx_acx_set_peer_cap(struct wl1271 *wl,
+                           struct ieee80211_sta_ht_cap *ht_cap,
+                           bool allow_ht_operation,
+                           u32 rate_set, u8 hlid);
 
 #endif /* __WL18XX_ACX_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c
new file mode 100644 (file)
index 0000000..1d1f6cc
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/hw_ops.h"
+
+#include "cmd.h"
+
+int wl18xx_cmd_channel_switch(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch)
+{
+       struct wl18xx_cmd_channel_switch *cmd;
+       u32 supported_rates;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "cmd channel switch");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+       cmd->channel = ch_switch->channel->hw_value;
+       cmd->switch_time = ch_switch->count;
+       cmd->stop_tx = ch_switch->block_tx;
+
+       switch (ch_switch->channel->band) {
+       case IEEE80211_BAND_2GHZ:
+               cmd->band = WLCORE_BAND_2_4GHZ;
+               break;
+       case IEEE80211_BAND_5GHZ:
+               cmd->band = WLCORE_BAND_5GHZ;
+               break;
+       default:
+               wl1271_error("invalid channel switch band: %d",
+                            ch_switch->channel->band);
+               ret = -EINVAL;
+               goto out_free;
+       }
+
+       supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
+                         wlcore_hw_sta_get_ap_rate_mask(wl, wlvif);
+       if (wlvif->p2p)
+               supported_rates &= ~CONF_TX_CCK_RATES;
+       cmd->local_supported_rates = cpu_to_le32(supported_rates);
+       cmd->channel_type = wlvif->channel_type;
+
+       ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send channel switch command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+out:
+       return ret;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/cmd.h b/drivers/net/wireless/ti/wl18xx/cmd.h
new file mode 100644 (file)
index 0000000..6687d10
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_CMD_H__
+#define __WL18XX_CMD_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/acx.h"
+
+struct wl18xx_cmd_channel_switch {
+       struct wl1271_cmd_header header;
+
+       u8 role_id;
+
+       /* The new serving channel */
+       u8 channel;
+       /* Relative time of the serving channel switch in TBTT units */
+       u8 switch_time;
+       /* Stop the role TX, should expect it after radar detection */
+       u8 stop_tx;
+
+       __le32 local_supported_rates;
+
+       u8 channel_type;
+       u8 band;
+
+       u8 padding[2];
+} __packed;
+
+int wl18xx_cmd_channel_switch(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch);
+
+#endif
index 4d426cc2027426b1ed7941b2d0ce250befdfd778..e34302e3b51d90077006870f29e70a83360b5167 100644 (file)
 #define __WL18XX_CONF_H__
 
 #define WL18XX_CONF_MAGIC      0x10e100ca
-#define WL18XX_CONF_VERSION    (WLCORE_CONF_VERSION | 0x0003)
+#define WL18XX_CONF_VERSION    (WLCORE_CONF_VERSION | 0x0006)
 #define WL18XX_CONF_MASK       0x0000ffff
 #define WL18XX_CONF_SIZE       (WLCORE_CONF_SIZE + \
                                 sizeof(struct wl18xx_priv_conf))
 
 #define NUM_OF_CHANNELS_11_ABG 150
 #define NUM_OF_CHANNELS_11_P 7
-#define WL18XX_NUM_OF_SUB_BANDS 9
 #define SRF_TABLE_LEN 16
 #define PIN_MUXING_SIZE 2
+#define WL18XX_TRACE_LOSS_GAPS_TX 10
+#define WL18XX_TRACE_LOSS_GAPS_RX 18
 
 struct wl18xx_mac_and_phy_params {
        u8 phy_standalone;
-       u8 rdl;
+       u8 spare0;
        u8 enable_clpc;
        u8 enable_tx_low_pwr_on_siso_rdl;
        u8 auto_detect;
@@ -69,18 +70,27 @@ struct wl18xx_mac_and_phy_params {
        u8 pwr_limit_reference_11_abg;
        u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P];
        u8 pwr_limit_reference_11p;
-       u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
-       u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
+       u8 spare1;
+       u8 per_chan_bo_mode_11_abg[13];
+       u8 per_chan_bo_mode_11_p[4];
        u8 primary_clock_setting_time;
        u8 clock_valid_on_wake_up;
        u8 secondary_clock_setting_time;
        u8 board_type;
        /* enable point saturation */
        u8 psat;
-       /* low/medium/high Tx power in dBm */
+       /* low/medium/high Tx power in dBm for STA-HP BG */
        s8 low_power_val;
        s8 med_power_val;
        s8 high_power_val;
+       s8 per_sub_band_tx_trace_loss[WL18XX_TRACE_LOSS_GAPS_TX];
+       s8 per_sub_band_rx_trace_loss[WL18XX_TRACE_LOSS_GAPS_RX];
+       u8 tx_rf_margin;
+       /* low/medium/high Tx power in dBm for other role */
+       s8 low_power_val_2nd;
+       s8 med_power_val_2nd;
+       s8 high_power_val_2nd;
+
        u8 padding[1];
 } __packed;
 
diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c
new file mode 100644 (file)
index 0000000..c9199d7
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "event.h"
+#include "scan.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+
+int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout)
+{
+       u32 local_event;
+
+       switch (event) {
+       case WLCORE_EVENT_PEER_REMOVE_COMPLETE:
+               local_event = PEER_REMOVE_COMPLETE_EVENT_ID;
+               break;
+
+       case WLCORE_EVENT_DFS_CONFIG_COMPLETE:
+               local_event = DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
+               break;
+
+       default:
+               /* event not implemented */
+               return 0;
+       }
+       return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
+}
+
+int wl18xx_process_mailbox_events(struct wl1271 *wl)
+{
+       struct wl18xx_event_mailbox *mbox = wl->mbox;
+       u32 vector;
+
+       vector = le32_to_cpu(mbox->events_vector);
+       wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector);
+
+       if (vector & SCAN_COMPLETE_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT, "scan results: %d",
+                            mbox->number_of_scan_results);
+
+               if (wl->scan_wlvif)
+                       wl18xx_scan_completed(wl, wl->scan_wlvif);
+       }
+
+       if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT,
+                            "PERIODIC_SCAN_REPORT_EVENT (results %d)",
+                            mbox->number_of_sched_scan_results);
+
+               wlcore_scan_sched_scan_results(wl);
+       }
+
+       if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID)
+               wlcore_event_sched_scan_completed(wl, 1);
+
+       if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID)
+               wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric);
+
+       if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)
+               wlcore_event_ba_rx_constraint(wl,
+                               le16_to_cpu(mbox->rx_ba_role_id_bitmap),
+                               le16_to_cpu(mbox->rx_ba_allowed_bitmap));
+
+       if (vector & BSS_LOSS_EVENT_ID)
+               wlcore_event_beacon_loss(wl,
+                                        le16_to_cpu(mbox->bss_loss_bitmap));
+
+       if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID)
+               wlcore_event_channel_switch(wl,
+                       le16_to_cpu(mbox->channel_switch_role_id_bitmap),
+                       true);
+
+       if (vector & DUMMY_PACKET_EVENT_ID)
+               wlcore_event_dummy_packet(wl);
+
+       /*
+        * "TX retries exceeded" has a different meaning according to mode.
+        * In AP mode the offending station is disconnected.
+        */
+       if (vector & MAX_TX_FAILURE_EVENT_ID)
+               wlcore_event_max_tx_failure(wl,
+                               le32_to_cpu(mbox->tx_retry_exceeded_bitmap));
+
+       if (vector & INACTIVE_STA_EVENT_ID)
+               wlcore_event_inactive_sta(wl,
+                               le32_to_cpu(mbox->inactive_sta_bitmap));
+
+       if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
+               wlcore_event_roc_complete(wl);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h
new file mode 100644 (file)
index 0000000..398f3d2
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_EVENT_H__
+#define __WL18XX_EVENT_H__
+
+#include "../wlcore/wlcore.h"
+
+enum {
+       SCAN_COMPLETE_EVENT_ID                   = BIT(8),
+       RADAR_DETECTED_EVENT_ID                  = BIT(9),
+       CHANNEL_SWITCH_COMPLETE_EVENT_ID         = BIT(10),
+       BSS_LOSS_EVENT_ID                        = BIT(11),
+       MAX_TX_FAILURE_EVENT_ID                  = BIT(12),
+       DUMMY_PACKET_EVENT_ID                    = BIT(13),
+       INACTIVE_STA_EVENT_ID                    = BIT(14),
+       PEER_REMOVE_COMPLETE_EVENT_ID            = BIT(15),
+       PERIODIC_SCAN_COMPLETE_EVENT_ID          = BIT(16),
+       BA_SESSION_RX_CONSTRAINT_EVENT_ID        = BIT(17),
+       REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(18),
+       DFS_CHANNELS_CONFIG_COMPLETE_EVENT       = BIT(19),
+       PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(20),
+};
+
+struct wl18xx_event_mailbox {
+       __le32 events_vector;
+
+       u8 number_of_scan_results;
+       u8 number_of_sched_scan_results;
+
+       __le16 channel_switch_role_id_bitmap;
+
+       s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
+
+       /* bitmap of removed links */
+       __le32 hlid_removed_bitmap;
+
+       /* rx ba constraint */
+       __le16 rx_ba_role_id_bitmap; /* 0xfff means any role. */
+       __le16 rx_ba_allowed_bitmap;
+
+       /* bitmap of roc completed (by role id) */
+       __le16 roc_completed_bitmap;
+
+       /* bitmap of stations (by role id) with bss loss */
+       __le16 bss_loss_bitmap;
+
+       /* bitmap of stations (by HLID) which exceeded max tx retries */
+       __le32 tx_retry_exceeded_bitmap;
+
+       /* bitmap of inactive stations (by HLID) */
+       __le32 inactive_sta_bitmap;
+} __packed;
+
+int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout);
+int wl18xx_process_mailbox_events(struct wl1271 *wl);
+
+#endif
index 8d8c1f8c63b79b8775a70c85fb4b4669380afbb7..0bd9b081fa9c9304e5b2744184b52218b82ce42f 100644 (file)
 
 #include "reg.h"
 #include "conf.h"
+#include "cmd.h"
 #include "acx.h"
 #include "tx.h"
 #include "wl18xx.h"
 #include "io.h"
+#include "scan.h"
+#include "event.h"
 #include "debugfs.h"
 
 #define WL18XX_RX_CHECKSUM_MASK      0x40
@@ -334,6 +337,8 @@ static struct wlcore_conf wl18xx_conf = {
                .tmpl_short_retry_limit      = 10,
                .tmpl_long_retry_limit       = 10,
                .tx_watchdog_timeout         = 5000,
+               .slow_link_thold             = 3,
+               .fast_link_thold             = 30,
        },
        .conn = {
                .wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
@@ -391,8 +396,10 @@ static struct wlcore_conf wl18xx_conf = {
        .scan = {
                .min_dwell_time_active        = 7500,
                .max_dwell_time_active        = 30000,
-               .min_dwell_time_passive       = 100000,
-               .max_dwell_time_passive       = 100000,
+               .min_dwell_time_active_long   = 25000,
+               .max_dwell_time_active_long   = 50000,
+               .dwell_time_passive           = 100000,
+               .dwell_time_dfs               = 150000,
                .num_probe_reqs               = 2,
                .split_scan_timeout           = 50000,
        },
@@ -489,6 +496,10 @@ static struct wlcore_conf wl18xx_conf = {
                .increase_time              = 1,
                .window_size                = 16,
        },
+       .recovery = {
+               .bug_on_recovery            = 0,
+               .no_recovery                = 0,
+       },
 };
 
 static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
@@ -501,7 +512,6 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .clock_valid_on_wake_up         = 0x00,
                .secondary_clock_setting_time   = 0x05,
                .board_type                     = BOARD_TYPE_HDK_18XX,
-               .rdl                            = 0x01,
                .auto_detect                    = 0x00,
                .dedicated_fem                  = FEM_NONE,
                .low_band_component             = COMPONENT_3_WAY_SWITCH,
@@ -517,14 +527,44 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .enable_clpc                    = 0x00,
                .enable_tx_low_pwr_on_siso_rdl  = 0x00,
                .rx_profile                     = 0x00,
-               .pwr_limit_reference_11_abg     = 0xc8,
+               .pwr_limit_reference_11_abg     = 0x64,
+               .per_chan_pwr_limit_arr_11abg   = {
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+               .pwr_limit_reference_11p        = 0x64,
+               .per_chan_bo_mode_11_abg        = { 0x00, 0x00, 0x00, 0x00,
+                                                   0x00, 0x00, 0x00, 0x00,
+                                                   0x00, 0x00, 0x00, 0x00,
+                                                   0x00 },
+               .per_chan_bo_mode_11_p          = { 0x00, 0x00, 0x00, 0x00 },
+               .per_chan_pwr_limit_arr_11p     = { 0xff, 0xff, 0xff, 0xff,
+                                                   0xff, 0xff, 0xff },
                .psat                           = 0,
-               .low_power_val                  = 0x00,
-               .med_power_val                  = 0x0a,
-               .high_power_val                 = 0x1e,
+               .low_power_val                  = 0x08,
+               .med_power_val                  = 0x12,
+               .high_power_val                 = 0x18,
+               .low_power_val_2nd              = 0x05,
+               .med_power_val_2nd              = 0x0a,
+               .high_power_val_2nd             = 0x14,
                .external_pa_dc2dc              = 0,
-               .number_of_assembled_ant2_4     = 1,
+               .number_of_assembled_ant2_4     = 2,
                .number_of_assembled_ant5       = 1,
+               .tx_rf_margin                   = 1,
        },
 };
 
@@ -595,7 +635,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
 };
 
 /* TODO: maybe move to a new header file? */
-#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw.bin"
+#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-2.bin"
 
 static int wl18xx_identify_chip(struct wl1271 *wl)
 {
@@ -608,15 +648,18 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
                wl->sr_fw_name = WL18XX_FW_NAME;
                /* wl18xx uses the same firmware for PLT */
                wl->plt_fw_name = WL18XX_FW_NAME;
-               wl->quirks |= WLCORE_QUIRK_NO_ELP |
-                             WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
+               wl->quirks |= WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
                              WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
                              WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN |
-                             WLCORE_QUIRK_TX_PAD_LAST_FRAME;
-
-               wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, WL18XX_IFTYPE_VER,
-                                     WL18XX_MAJOR_VER, WL18XX_SUBTYPE_VER,
-                                     WL18XX_MINOR_VER);
+                             WLCORE_QUIRK_TX_PAD_LAST_FRAME |
+                             WLCORE_QUIRK_REGDOMAIN_CONF |
+                             WLCORE_QUIRK_DUAL_PROBE_TMPL;
+
+               wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER,
+                                     WL18XX_IFTYPE_VER,  WL18XX_MAJOR_VER,
+                                     WL18XX_SUBTYPE_VER, WL18XX_MINOR_VER,
+                                     /* there's no separate multi-role FW */
+                                     0, 0, 0, 0);
                break;
        case CHIP_ID_185x_PG10:
                wl1271_warning("chip id 0x%x (185x PG10) is deprecated",
@@ -630,6 +673,12 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
                goto out;
        }
 
+       wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
+       wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+       wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC;
+       wl->sched_scan_templ_id_5 = CMD_TEMPL_PROBE_REQ_5_PERIODIC;
+       wl->max_channels_5 = WL18XX_MAX_CHANNELS_5GHZ;
+       wl->ba_rx_session_count_max = WL18XX_RX_BA_MAX_SESSIONS;
 out:
        return ret;
 }
@@ -843,6 +892,20 @@ static int wl18xx_boot(struct wl1271 *wl)
        if (ret < 0)
                goto out;
 
+       wl->event_mask = BSS_LOSS_EVENT_ID |
+               SCAN_COMPLETE_EVENT_ID |
+               RSSI_SNR_TRIGGER_0_EVENT_ID |
+               PERIODIC_SCAN_COMPLETE_EVENT_ID |
+               PERIODIC_SCAN_REPORT_EVENT_ID |
+               DUMMY_PACKET_EVENT_ID |
+               PEER_REMOVE_COMPLETE_EVENT_ID |
+               BA_SESSION_RX_CONSTRAINT_EVENT_ID |
+               REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
+               INACTIVE_STA_EVENT_ID |
+               MAX_TX_FAILURE_EVENT_ID |
+               CHANNEL_SWITCH_COMPLETE_EVENT_ID |
+               DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -964,7 +1027,7 @@ static int wl18xx_hw_init(struct wl1271 *wl)
 
        /* (re)init private structures. Relevant on recovery as well. */
        priv->last_fw_rls_idx = 0;
-       priv->extra_spare_vif_count = 0;
+       priv->extra_spare_key_count = 0;
 
        /* set the default amount of spare blocks in the bitmap */
        ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE);
@@ -1022,7 +1085,12 @@ static bool wl18xx_is_mimo_supported(struct wl1271 *wl)
 {
        struct wl18xx_priv *priv = wl->priv;
 
-       return priv->conf.phy.number_of_assembled_ant2_4 >= 2;
+       /* only support MIMO with multiple antennas, and when SISO
+        * is not forced through config
+        */
+       return (priv->conf.phy.number_of_assembled_ant2_4 >= 2) &&
+              (priv->conf.ht.mode != HT_MODE_WIDE) &&
+              (priv->conf.ht.mode != HT_MODE_SISO20);
 }
 
 /*
@@ -1077,6 +1145,7 @@ static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl,
 static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
 {
        u32 fuse;
+       s8 rom = 0, metal = 0, pg_ver = 0, rdl_ver = 0;
        int ret;
 
        ret = wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]);
@@ -1087,8 +1156,29 @@ static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
        if (ret < 0)
                goto out;
 
+       pg_ver = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET;
+       rom = (fuse & WL18XX_ROM_VER_MASK) >> WL18XX_ROM_VER_OFFSET;
+
+       if (rom <= 0xE)
+               metal = (fuse & WL18XX_METAL_VER_MASK) >>
+                       WL18XX_METAL_VER_OFFSET;
+       else
+               metal = (fuse & WL18XX_NEW_METAL_VER_MASK) >>
+                       WL18XX_NEW_METAL_VER_OFFSET;
+
+       ret = wlcore_read32(wl, WL18XX_REG_FUSE_DATA_2_3, &fuse);
+       if (ret < 0)
+               goto out;
+
+       rdl_ver = (fuse & WL18XX_RDL_VER_MASK) >> WL18XX_RDL_VER_OFFSET;
+       if (rdl_ver > RDL_MAX)
+               rdl_ver = RDL_NONE;
+
+       wl1271_info("wl18xx HW: RDL %d, %s, PG %x.%x (ROM %x)",
+                   rdl_ver, rdl_names[rdl_ver], pg_ver, metal, rom);
+
        if (ver)
-               *ver = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET;
+               *ver = pg_ver;
 
        ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
 
@@ -1223,8 +1313,8 @@ static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
 {
        struct wl18xx_priv *priv = wl->priv;
 
-       /* If we have VIFs requiring extra spare, indulge them */
-       if (priv->extra_spare_vif_count)
+       /* If we have keys requiring extra spare, indulge them */
+       if (priv->extra_spare_key_count)
                return WL18XX_TX_HW_EXTRA_BLOCK_SPARE;
 
        return WL18XX_TX_HW_BLOCK_SPARE;
@@ -1236,42 +1326,48 @@ static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                          struct ieee80211_key_conf *key_conf)
 {
        struct wl18xx_priv *priv = wl->priv;
-       bool change_spare = false;
+       bool change_spare = false, special_enc;
        int ret;
 
+       wl1271_debug(DEBUG_CRYPT, "extra spare keys before: %d",
+                    priv->extra_spare_key_count);
+
+       special_enc = key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
+                     key_conf->cipher == WLAN_CIPHER_SUITE_TKIP;
+
+       ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
+       if (ret < 0)
+               goto out;
+
        /*
-        * when adding the first or removing the last GEM/TKIP interface,
+        * when adding the first or removing the last GEM/TKIP key,
         * we have to adjust the number of spare blocks.
         */
-       change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
-               key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) &&
-               ((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) ||
-                (priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY));
+       if (special_enc) {
+               if (cmd == SET_KEY) {
+                       /* first key */
+                       change_spare = (priv->extra_spare_key_count == 0);
+                       priv->extra_spare_key_count++;
+               } else if (cmd == DISABLE_KEY) {
+                       /* last key */
+                       change_spare = (priv->extra_spare_key_count == 1);
+                       priv->extra_spare_key_count--;
+               }
+       }
 
-       /* no need to change spare - just regular set_key */
-       if (!change_spare)
-               return wlcore_set_key(wl, cmd, vif, sta, key_conf);
+       wl1271_debug(DEBUG_CRYPT, "extra spare keys after: %d",
+                    priv->extra_spare_key_count);
 
-       ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
-       if (ret < 0)
+       if (!change_spare)
                goto out;
 
        /* key is now set, change the spare blocks */
-       if (cmd == SET_KEY) {
+       if (priv->extra_spare_key_count)
                ret = wl18xx_set_host_cfg_bitmap(wl,
                                        WL18XX_TX_HW_EXTRA_BLOCK_SPARE);
-               if (ret < 0)
-                       goto out;
-
-               priv->extra_spare_vif_count++;
-       } else {
+       else
                ret = wl18xx_set_host_cfg_bitmap(wl,
                                        WL18XX_TX_HW_BLOCK_SPARE);
-               if (ret < 0)
-                       goto out;
-
-               priv->extra_spare_vif_count--;
-       }
 
 out:
        return ret;
@@ -1296,6 +1392,92 @@ static u32 wl18xx_pre_pkt_send(struct wl1271 *wl,
        return buf_offset;
 }
 
+static void wl18xx_sta_rc_update(struct wl1271 *wl,
+                                struct wl12xx_vif *wlvif,
+                                struct ieee80211_sta *sta,
+                                u32 changed)
+{
+       bool wide = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide);
+
+       if (!(changed & IEEE80211_RC_BW_CHANGED))
+               return;
+
+       mutex_lock(&wl->mutex);
+
+       /* sanity */
+       if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
+               goto out;
+
+       /* ignore the change before association */
+       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+               goto out;
+
+       /*
+        * If we started out as wide, we can change the operation mode. If we
+        * thought this was a 20mhz AP, we have to reconnect
+        */
+       if (wlvif->sta.role_chan_type == NL80211_CHAN_HT40MINUS ||
+           wlvif->sta.role_chan_type == NL80211_CHAN_HT40PLUS)
+               wl18xx_acx_peer_ht_operation_mode(wl, wlvif->sta.hlid, wide);
+       else
+               ieee80211_connection_loss(wl12xx_wlvif_to_vif(wlvif));
+
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+static int wl18xx_set_peer_cap(struct wl1271 *wl,
+                              struct ieee80211_sta_ht_cap *ht_cap,
+                              bool allow_ht_operation,
+                              u32 rate_set, u8 hlid)
+{
+       return wl18xx_acx_set_peer_cap(wl, ht_cap, allow_ht_operation,
+                                      rate_set, hlid);
+}
+
+static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+                                struct wl1271_link *lnk)
+{
+       u8 thold;
+       struct wl18xx_fw_status_priv *status_priv =
+               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+
+       /* suspended links are never high priority */
+       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+               return false;
+
+       /* the priority thresholds are taken from FW */
+       if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
+           !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
+               thold = status_priv->tx_fast_link_prio_threshold;
+       else
+               thold = status_priv->tx_slow_link_prio_threshold;
+
+       return lnk->allocated_pkts < thold;
+}
+
+static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
+                               struct wl1271_link *lnk)
+{
+       u8 thold;
+       struct wl18xx_fw_status_priv *status_priv =
+               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+
+       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+               thold = status_priv->tx_suspend_threshold;
+       else if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
+                !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
+               thold = status_priv->tx_fast_stop_threshold;
+       else
+               thold = status_priv->tx_slow_stop_threshold;
+
+       return lnk->allocated_pkts < thold;
+}
+
 static int wl18xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl18xx_ops = {
@@ -1305,6 +1487,8 @@ static struct wlcore_ops wl18xx_ops = {
        .plt_init       = wl18xx_plt_init,
        .trigger_cmd    = wl18xx_trigger_cmd,
        .ack_event      = wl18xx_ack_event,
+       .wait_for_event = wl18xx_wait_for_event,
+       .process_mailbox_events = wl18xx_process_mailbox_events,
        .calc_tx_blocks = wl18xx_calc_tx_blocks,
        .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks,
        .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len,
@@ -1320,16 +1504,26 @@ static struct wlcore_ops wl18xx_ops = {
        .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask,
        .get_mac        = wl18xx_get_mac,
        .debugfs_init   = wl18xx_debugfs_add_files,
+       .scan_start     = wl18xx_scan_start,
+       .scan_stop      = wl18xx_scan_stop,
+       .sched_scan_start       = wl18xx_sched_scan_start,
+       .sched_scan_stop        = wl18xx_scan_sched_scan_stop,
        .handle_static_data     = wl18xx_handle_static_data,
        .get_spare_blocks = wl18xx_get_spare_blocks,
        .set_key        = wl18xx_set_key,
+       .channel_switch = wl18xx_cmd_channel_switch,
        .pre_pkt_send   = wl18xx_pre_pkt_send,
+       .sta_rc_update  = wl18xx_sta_rc_update,
+       .set_peer_cap   = wl18xx_set_peer_cap,
+       .lnk_high_prio  = wl18xx_lnk_high_prio,
+       .lnk_low_prio   = wl18xx_lnk_low_prio,
 };
 
 /* HT cap appropriate for wide channels in 2Ghz */
 static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
        .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
-              IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40,
+              IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1343,7 +1537,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
 /* HT cap appropriate for wide channels in 5Ghz */
 static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
        .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
-              IEEE80211_HT_CAP_SUP_WIDTH_20_40,
+              IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1356,7 +1551,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
 
 /* HT cap appropriate for SISO 20 */
 static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
-       .cap = IEEE80211_HT_CAP_SGI_20,
+       .cap = IEEE80211_HT_CAP_SGI_20 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1369,7 +1565,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
 
 /* HT cap appropriate for MIMO rates in 20mhz channel */
 static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
-       .cap = IEEE80211_HT_CAP_SGI_20,
+       .cap = IEEE80211_HT_CAP_SGI_20 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1387,7 +1584,8 @@ static int wl18xx_setup(struct wl1271 *wl)
 
        wl->rtable = wl18xx_rtable;
        wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS;
-       wl->num_rx_desc = WL18XX_NUM_TX_DESCRIPTORS;
+       wl->num_rx_desc = WL18XX_NUM_RX_DESCRIPTORS;
+       wl->num_channels = 2;
        wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES;
        wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
@@ -1506,7 +1704,8 @@ static int wl18xx_probe(struct platform_device *pdev)
        int ret;
 
        hw = wlcore_alloc_hw(sizeof(struct wl18xx_priv),
-                            WL18XX_AGGR_BUFFER_SIZE);
+                            WL18XX_AGGR_BUFFER_SIZE,
+                            sizeof(struct wl18xx_event_mailbox));
        if (IS_ERR(hw)) {
                wl1271_error("can't allocate hw");
                ret = PTR_ERR(hw);
index 937b71d8783ff84a19ae52a04df4d25c1d98b6f9..6306e04cd2580676e6683ae62a64cecc3578c719 100644 (file)
 #define WL18XX_REG_FUSE_DATA_1_3       0xA0260C
 #define WL18XX_PG_VER_MASK             0x70
 #define WL18XX_PG_VER_OFFSET           4
+#define WL18XX_ROM_VER_MASK            0x3
+#define WL18XX_ROM_VER_OFFSET          0
+#define WL18XX_METAL_VER_MASK          0xC
+#define WL18XX_METAL_VER_OFFSET                2
+#define WL18XX_NEW_METAL_VER_MASK      0x180
+#define WL18XX_NEW_METAL_VER_OFFSET    7
+
+#define WL18XX_REG_FUSE_DATA_2_3       0xA02614
+#define WL18XX_RDL_VER_MASK            0x1f00
+#define WL18XX_RDL_VER_OFFSET          8
 
 #define WL18XX_REG_FUSE_BD_ADDR_1      0xA02602
 #define WL18XX_REG_FUSE_BD_ADDR_2      0xA02606
@@ -188,4 +198,23 @@ enum {
        NUM_BOARD_TYPES,
 };
 
+enum {
+       RDL_NONE        = 0,
+       RDL_1_HP        = 1,
+       RDL_2_SP        = 2,
+       RDL_3_HP        = 3,
+       RDL_4_SP        = 4,
+
+       _RDL_LAST,
+       RDL_MAX = _RDL_LAST - 1,
+};
+
+static const char * const rdl_names[] = {
+       [RDL_NONE]      = "",
+       [RDL_1_HP]      = "1853 SISO",
+       [RDL_2_SP]      = "1857 MIMO",
+       [RDL_3_HP]      = "1893 SISO",
+       [RDL_4_SP]      = "1897 MIMO",
+};
+
 #endif /* __REG_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c
new file mode 100644 (file)
index 0000000..09d9445
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/ieee80211.h>
+#include "scan.h"
+#include "../wlcore/debug.h"
+
+static void wl18xx_adjust_channels(struct wl18xx_cmd_scan_params *cmd,
+                                  struct wlcore_scan_channels *cmd_channels)
+{
+       memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive));
+       memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active));
+       cmd->dfs = cmd_channels->dfs;
+       cmd->passive_active = cmd_channels->passive_active;
+
+       memcpy(cmd->channels_2, cmd_channels->channels_2,
+              sizeof(cmd->channels_2));
+       memcpy(cmd->channels_5, cmd_channels->channels_5,
+              sizeof(cmd->channels_2));
+       /* channels_4 are not supported, so no need to copy them */
+}
+
+static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct cfg80211_scan_request *req)
+{
+       struct wl18xx_cmd_scan_params *cmd;
+       struct wlcore_scan_channels *cmd_channels = NULL;
+       int ret;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+
+       if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cmd->scan_type = SCAN_TYPE_SEARCH;
+       cmd->rssi_threshold = -127;
+       cmd->snr_threshold = 0;
+
+       cmd->bss_type = SCAN_BSS_TYPE_ANY;
+
+       cmd->ssid_from_list = 0;
+       cmd->filter = 0;
+       cmd->add_broadcast = 0;
+
+       cmd->urgency = 0;
+       cmd->protect = 0;
+
+       cmd->n_probe_reqs = wl->conf.scan.num_probe_reqs;
+       cmd->terminate_after = 0;
+
+       /* configure channels */
+       WARN_ON(req->n_ssids > 1);
+
+       cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
+       if (!cmd_channels) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       wlcore_set_scan_chan_params(wl, cmd_channels, req->channels,
+                                   req->n_channels, req->n_ssids,
+                                   SCAN_TYPE_SEARCH);
+       wl18xx_adjust_channels(cmd, cmd_channels);
+
+       /*
+        * all the cycles params (except total cycles) should
+        * remain 0 for normal scan
+        */
+       cmd->total_cycles = 1;
+
+       if (req->no_cck)
+               cmd->rate = WL18XX_SCAN_RATE_6;
+
+       cmd->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       if (req->n_ssids) {
+               cmd->ssid_len = req->ssids[0].ssid_len;
+               memcpy(cmd->ssid, req->ssids[0].ssid, cmd->ssid_len);
+       }
+
+       /* TODO: per-band ies? */
+       if (cmd->active[0]) {
+               u8 band = IEEE80211_BAND_2GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                req->ie,
+                                req->ie_len,
+                                false);
+               if (ret < 0) {
+                       wl1271_error("2.4GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       if (cmd->active[1] || cmd->dfs) {
+               u8 band = IEEE80211_BAND_5GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                req->ie,
+                                req->ie_len,
+                                false);
+               if (ret < 0) {
+                       wl1271_error("5GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+
+       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN failed");
+               goto out;
+       }
+
+out:
+       kfree(cmd_channels);
+       kfree(cmd);
+       return ret;
+}
+
+void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       wl->scan.failed = false;
+       cancel_delayed_work(&wl->scan_complete_work);
+       ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                    msecs_to_jiffies(0));
+}
+
+static
+int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
+                                 struct wl12xx_vif *wlvif,
+                                 struct cfg80211_sched_scan_request *req,
+                                 struct ieee80211_sched_scan_ies *ies)
+{
+       struct wl18xx_cmd_scan_params *cmd;
+       struct wlcore_scan_channels *cmd_channels = NULL;
+       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
+       int ret;
+       int filter_type;
+
+       wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
+
+       filter_type = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
+       if (filter_type < 0)
+               return filter_type;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+
+       if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cmd->scan_type = SCAN_TYPE_PERIODIC;
+       cmd->rssi_threshold = c->rssi_threshold;
+       cmd->snr_threshold = c->snr_threshold;
+
+       /* don't filter on BSS type */
+       cmd->bss_type = SCAN_BSS_TYPE_ANY;
+
+       cmd->ssid_from_list = 1;
+       if (filter_type == SCAN_SSID_FILTER_LIST)
+               cmd->filter = 1;
+       cmd->add_broadcast = 0;
+
+       cmd->urgency = 0;
+       cmd->protect = 0;
+
+       cmd->n_probe_reqs = c->num_probe_reqs;
+       /* don't stop scanning automatically when something is found */
+       cmd->terminate_after = 0;
+
+       cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
+       if (!cmd_channels) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       /* configure channels */
+       wlcore_set_scan_chan_params(wl, cmd_channels, req->channels,
+                                   req->n_channels, req->n_ssids,
+                                   SCAN_TYPE_PERIODIC);
+       wl18xx_adjust_channels(cmd, cmd_channels);
+
+       cmd->short_cycles_sec = 0;
+       cmd->long_cycles_sec = cpu_to_le16(req->interval);
+       cmd->short_cycles_count = 0;
+
+       cmd->total_cycles = 0;
+
+       cmd->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       /* create a PERIODIC_SCAN_REPORT_EVENT whenever we've got a match */
+       cmd->report_threshold = 1;
+       cmd->terminate_on_report = 0;
+
+       if (cmd->active[0]) {
+               u8 band = IEEE80211_BAND_2GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                ies->ie[band],
+                                ies->len[band],
+                                true);
+               if (ret < 0) {
+                       wl1271_error("2.4GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       if (cmd->active[1] || cmd->dfs) {
+               u8 band = IEEE80211_BAND_5GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                ies->ie[band],
+                                ies->len[band],
+                                true);
+               if (ret < 0) {
+                       wl1271_error("5GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+
+       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN failed");
+               goto out;
+       }
+
+out:
+       kfree(cmd_channels);
+       kfree(cmd);
+       return ret;
+}
+
+int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies)
+{
+       return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies);
+}
+
+static int __wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                              u8 scan_type)
+{
+       struct wl18xx_cmd_scan_stop *stop;
+       int ret;
+
+       wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
+
+       stop = kzalloc(sizeof(*stop), GFP_KERNEL);
+       if (!stop) {
+               wl1271_error("failed to alloc memory to send sched scan stop");
+               return -ENOMEM;
+       }
+
+       stop->role_id = wlvif->role_id;
+       stop->scan_type = scan_type;
+
+       ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send sched scan stop command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(stop);
+       return ret;
+}
+
+void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_PERIODIC);
+}
+int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req)
+{
+       return wl18xx_scan_send(wl, wlvif, req);
+}
+
+int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       return __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_SEARCH);
+}
diff --git a/drivers/net/wireless/ti/wl18xx/scan.h b/drivers/net/wireless/ti/wl18xx/scan.h
new file mode 100644 (file)
index 0000000..eadee42
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_SCAN_H__
+#define __WL18XX_SCAN_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/scan.h"
+
+struct tracking_ch_params {
+       struct conn_scan_ch_params channel;
+
+       __le32 bssid_lsb;
+       __le16 bssid_msb;
+
+       u8 padding[2];
+} __packed;
+
+/* probe request rate */
+enum
+{
+       WL18XX_SCAN_RATE_1      = 0,
+       WL18XX_SCAN_RATE_5_5    = 1,
+       WL18XX_SCAN_RATE_6      = 2,
+};
+
+#define WL18XX_MAX_CHANNELS_5GHZ 32
+
+struct wl18xx_cmd_scan_params {
+       struct wl1271_cmd_header header;
+
+       u8 role_id;
+       u8 scan_type;
+
+       s8 rssi_threshold; /* for filtering (in dBm) */
+       s8 snr_threshold;  /* for filtering (in dB) */
+
+       u8 bss_type;       /* for filtering */
+       u8 ssid_from_list; /* use ssid from configured ssid list */
+       u8 filter;         /* forward only results with matching ssids */
+
+       /*
+        * add broadcast ssid in addition to the configured ssids.
+        * the driver should add dummy entry for it (?).
+        */
+       u8 add_broadcast;
+
+       u8 urgency;
+       u8 protect;      /* ??? */
+       u8 n_probe_reqs;    /* Number of probes requests per channel */
+       u8 terminate_after; /* early terminate scan operation */
+
+       u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */
+       u8 active[SCAN_MAX_BANDS];  /* number of active scan channels */
+       u8 dfs;            /* number of dfs channels in 5ghz */
+       u8 passive_active; /* number of passive before active channels 2.4ghz */
+
+       __le16 short_cycles_sec;
+       __le16 long_cycles_sec;
+       u8 short_cycles_count;
+       u8 total_cycles; /* 0 - infinite */
+       u8 padding[2];
+
+       union {
+               struct {
+                       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+                       struct conn_scan_ch_params channels_5[WL18XX_MAX_CHANNELS_5GHZ];
+                       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+               };
+               struct tracking_ch_params channels_tracking[WL1271_SCAN_MAX_CHANNELS];
+       } ;
+
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+       u8 ssid_len;     /* For SCAN_SSID_FILTER_SPECIFIC */
+       u8 tag;
+       u8 rate;
+
+       /* send SCAN_REPORT_EVENT in periodic scans after each cycle
+       * if number of results >= report_threshold. Must be 0 for
+       * non periodic scans
+       */
+       u8 report_threshold;
+
+       /* Should periodic scan stop after a report event was created.
+       * Must be 0 for non periodic scans.
+       */
+       u8 terminate_on_report;
+
+       u8 padding1[3];
+} __packed;
+
+struct wl18xx_cmd_scan_stop {
+       struct wl1271_cmd_header header;
+
+       u8 role_id;
+       u8 scan_type;
+       u8 padding[2];
+} __packed;
+
+int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req);
+int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies);
+void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+#endif
index 5b1fb10d9fd7c4ae084d5865ebb07e185df8afa5..57c694396647f71adaea386cd4465df00a5611a8 100644 (file)
 #include "wl18xx.h"
 #include "tx.h"
 
+static
+void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
+                            struct ieee80211_tx_rate *rate)
+{
+       u8 fw_rate = wl->fw_status_2->counters.tx_last_rate;
+
+       if (fw_rate > CONF_HW_RATE_INDEX_MAX) {
+               wl1271_error("last Tx rate invalid: %d", fw_rate);
+               rate->idx = 0;
+               rate->flags = 0;
+               return;
+       }
+
+       if (fw_rate <= CONF_HW_RATE_INDEX_54MBPS) {
+               rate->idx = fw_rate;
+               rate->flags = 0;
+       } else {
+               rate->flags = IEEE80211_TX_RC_MCS;
+               rate->idx = fw_rate - CONF_HW_RATE_INDEX_MCS0;
+
+               /* SGI modifier is counted as a separate rate */
+               if (fw_rate >= CONF_HW_RATE_INDEX_MCS7_SGI)
+                       (rate->idx)--;
+               if (fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI)
+                       (rate->idx)--;
+
+               /* this also covers the 40Mhz SGI case (= MCS15) */
+               if (fw_rate == CONF_HW_RATE_INDEX_MCS7_SGI ||
+                   fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI)
+                       rate->flags |= IEEE80211_TX_RC_SHORT_GI;
+
+               if (fw_rate > CONF_HW_RATE_INDEX_MCS7_SGI && vif) {
+                       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+                       if (wlvif->channel_type == NL80211_CHAN_HT40MINUS ||
+                           wlvif->channel_type == NL80211_CHAN_HT40PLUS) {
+                               /* adjustment needed for range 0-7 */
+                               rate->idx -= 8;
+                               rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+                       }
+               }
+       }
+}
+
 static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
 {
        struct ieee80211_tx_info *info;
@@ -44,7 +87,6 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
        /* a zero bit indicates Tx success */
        tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX));
 
-
        skb = wl->tx_frames[id];
        info = IEEE80211_SKB_CB(skb);
 
@@ -56,11 +98,13 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
        /* update the TX status info */
        if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK))
                info->flags |= IEEE80211_TX_STAT_ACK;
+       /*
+        * first pass info->control.vif while it's valid, and then fill out
+        * the info->status structures
+        */
+       wl18xx_get_last_tx_rate(wl, info->control.vif, &info->status.rates[0]);
 
-       /* no real data about Tx completion */
-       info->status.rates[0].idx = -1;
-       info->status.rates[0].count = 0;
-       info->status.rates[0].flags = 0;
+       info->status.rates[0].count = 1; /* no data about retries */
        info->status.ack_signal = -1;
 
        if (!tx_success)
index 96a1e438d677fd1f14ea125338c824c25e34b228..9204e07ee432fe483a343f008ee1ce91f2752149 100644 (file)
 
 /* minimum FW required for driver */
 #define WL18XX_CHIP_VER                8
-#define WL18XX_IFTYPE_VER      2
-#define WL18XX_MAJOR_VER       0
-#define WL18XX_SUBTYPE_VER     0
-#define WL18XX_MINOR_VER       100
+#define WL18XX_IFTYPE_VER      5
+#define WL18XX_MAJOR_VER       WLCORE_FW_VER_IGNORE
+#define WL18XX_SUBTYPE_VER     WLCORE_FW_VER_IGNORE
+#define WL18XX_MINOR_VER       39
 
 #define WL18XX_CMD_MAX_SIZE          740
 
@@ -40,6 +40,8 @@
 
 #define WL18XX_NUM_MAC_ADDRESSES 3
 
+#define WL18XX_RX_BA_MAX_SESSIONS 5
+
 struct wl18xx_priv {
        /* buffer for sending commands to FW */
        u8 cmd_buf[WL18XX_CMD_MAX_SIZE];
@@ -49,8 +51,8 @@ struct wl18xx_priv {
        /* Index of last released Tx desc in FW */
        u8 last_fw_rls_idx;
 
-       /* number of VIFs requiring extra spare mem-blocks */
-       int extra_spare_vif_count;
+       /* number of keys requiring extra spare mem-blocks */
+       int extra_spare_key_count;
 };
 
 #define WL18XX_FW_MAX_TX_STATUS_DESC 33
@@ -68,7 +70,43 @@ struct wl18xx_fw_status_priv {
         */
        u8 released_tx_desc[WL18XX_FW_MAX_TX_STATUS_DESC];
 
-       u8 padding[2];
+       /* A bitmap representing the currently suspended links. The suspend
+        * is short lived, for multi-channel Tx requirements.
+        */
+       __le32 link_suspend_bitmap;
+
+       /* packet threshold for an "almost empty" AC,
+        * for Tx schedulng purposes
+        */
+       u8 tx_ac_threshold;
+
+       /* number of packets to queue up for a link in PS */
+       u8 tx_ps_threshold;
+
+       /* number of packet to queue up for a suspended link */
+       u8 tx_suspend_threshold;
+
+       /* Should have less than this number of packets in queue of a slow
+        * link to qualify as high priority link
+        */
+       u8 tx_slow_link_prio_threshold;
+
+       /* Should have less than this number of packets in queue of a fast
+        * link to qualify as high priority link
+        */
+       u8 tx_fast_link_prio_threshold;
+
+       /* Should have less than this number of packets in queue of a slow
+        * link before we stop queuing up packets for it.
+        */
+       u8 tx_slow_stop_threshold;
+
+       /* Should have less than this number of packets in queue of a fast
+        * link before we stop queuing up packets for it.
+        */
+       u8 tx_fast_stop_threshold;
+
+       u8 padding[3];
 };
 
 #define WL18XX_PHY_VERSION_MAX_LEN 20
index d7b907e67170348e70302e6a1cfe70642a8c6f7e..2b832825c3d41130e3ed1c4802dc5c1244275634 100644 (file)
@@ -33,8 +33,3 @@ config WLCORE_SDIO
 
          If you choose to build a module, it'll be called wlcore_sdio.
          Say N if unsure.
-
-config WL12XX_PLATFORM_DATA
-       bool
-       depends on WLCORE_SDIO != n || WL1251_SDIO != n
-       default y
index d9fba9e32130ad378a5b5d37b9cf99efdfac4cb2..b21398f6c3ecd14bdf0558f7fc49becaf482eeca 100644 (file)
@@ -9,7 +9,4 @@ obj-$(CONFIG_WLCORE)                    += wlcore.o
 obj-$(CONFIG_WLCORE_SPI)               += wlcore_spi.o
 obj-$(CONFIG_WLCORE_SDIO)              += wlcore_sdio.o
 
-# small builtin driver bit
-obj-$(CONFIG_WL12XX_PLATFORM_DATA)     += wl12xx_platform_data.o
-
 ccflags-y += -D__CHECK_ENDIAN__
index ce108a736bd0a3bdc5b1832fa09f02bdb82f412f..7a970cd9c5551cc538ec0177758f023589c9e281 100644 (file)
@@ -1340,6 +1340,8 @@ out:
        kfree(acx);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_acx_set_ht_capabilities);
+
 
 int wl1271_acx_set_ht_information(struct wl1271 *wl,
                                   struct wl12xx_vif *wlvif,
@@ -1433,13 +1435,22 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
        acx->win_size = wl->conf.ht.rx_ba_win_size;
        acx->ssn = ssn;
 
-       ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx,
-                                  sizeof(*acx));
+       ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx,
+                                           sizeof(*acx),
+                                           BIT(CMD_STATUS_NO_RX_BA_SESSION));
        if (ret < 0) {
                wl1271_warning("acx ba receiver session failed: %d", ret);
                goto out;
        }
 
+       /* sometimes we can't start the session */
+       if (ret == CMD_STATUS_NO_RX_BA_SESSION) {
+               wl1271_warning("no fw rx ba on tid %d", tid_index);
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = 0;
 out:
        kfree(acx);
        return ret;
@@ -1725,6 +1736,35 @@ out:
 
 }
 
+int wlcore_acx_average_rssi(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           s8 *avg_rssi)
+{
+       struct acx_roaming_stats *acx;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_ACX, "acx roaming statistics");
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       acx->role_id = wlvif->role_id;
+       ret = wl1271_cmd_interrogate(wl, ACX_ROAMING_STATISTICS_TBL,
+                                    acx, sizeof(*acx));
+       if (ret < 0) {
+               wl1271_warning("acx roaming statistics failed: %d", ret);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       *avg_rssi = acx->rssi_beacon;
+out:
+       kfree(acx);
+       return ret;
+}
+
 #ifdef CONFIG_PM
 /* Set the global behaviour of RX filters - On/Off + default action */
 int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable,
index d03215d6b3bd2bc698cb38b465e51427798d37ec..6dcfad9b04729a44d60044d41bb1ac0a9ac042cb 100644 (file)
@@ -728,8 +728,6 @@ struct wl1271_acx_ht_information {
        u8 padding[2];
 } __packed;
 
-#define RX_BA_MAX_SESSIONS 3
-
 struct wl1271_acx_ba_initiator_policy {
        struct acx_header header;
 
@@ -955,6 +953,18 @@ struct acx_rx_filter_cfg {
        u8 fields[0];
 } __packed;
 
+struct acx_roaming_stats {
+       struct acx_header header;
+
+       u8      role_id;
+       u8      pad[3];
+       u32     missed_beacons;
+       u8      snr_data;
+       u8      snr_bacon;
+       s8      rssi_data;
+       s8      rssi_beacon;
+} __packed;
+
 enum {
        ACX_WAKE_UP_CONDITIONS           = 0x0000,
        ACX_MEM_CFG                      = 0x0001,
@@ -1025,7 +1035,6 @@ enum {
        ACX_CONFIG_HANGOVER              = 0x0042,
        ACX_FEATURE_CFG                  = 0x0043,
        ACX_PROTECTION_CFG               = 0x0044,
-       ACX_CHECKSUM_CONFIG              = 0x0045,
 };
 
 
@@ -1113,6 +1122,8 @@ int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
 int wl1271_acx_fm_coex(struct wl1271 *wl);
 int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl);
 int wl12xx_acx_config_hangover(struct wl1271 *wl);
+int wlcore_acx_average_rssi(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           s8 *avg_rssi);
 
 #ifdef CONFIG_PM
 int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable,
index 375ea574eafb3b7e09b936f8b24f9a1d04b21e19..77752b03f1892184f0275172f41ef15721c4cd1c 100644 (file)
@@ -84,47 +84,57 @@ out:
 static int wlcore_validate_fw_ver(struct wl1271 *wl)
 {
        unsigned int *fw_ver = wl->chip.fw_ver;
-       unsigned int *min_ver = wl->min_fw_ver;
+       unsigned int *min_ver = (wl->fw_type == WL12XX_FW_TYPE_MULTI) ?
+               wl->min_mr_fw_ver : wl->min_sr_fw_ver;
+       char min_fw_str[32] = "";
+       int i;
 
        /* the chip must be exactly equal */
-       if (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP])
+       if ((min_ver[FW_VER_CHIP] != WLCORE_FW_VER_IGNORE) &&
+           (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP]))
                goto fail;
 
-       /* always check the next digit if all previous ones are equal */
-
-       if (min_ver[FW_VER_IF_TYPE] < fw_ver[FW_VER_IF_TYPE])
-               goto out;
-       else if (min_ver[FW_VER_IF_TYPE] > fw_ver[FW_VER_IF_TYPE])
+       /* the firmware type must be equal */
+       if ((min_ver[FW_VER_IF_TYPE] != WLCORE_FW_VER_IGNORE) &&
+           (min_ver[FW_VER_IF_TYPE] != fw_ver[FW_VER_IF_TYPE]))
                goto fail;
 
-       if (min_ver[FW_VER_MAJOR] < fw_ver[FW_VER_MAJOR])
-               goto out;
-       else if (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR])
+       /* the project number must be equal */
+       if ((min_ver[FW_VER_SUBTYPE] != WLCORE_FW_VER_IGNORE) &&
+           (min_ver[FW_VER_SUBTYPE] != fw_ver[FW_VER_SUBTYPE]))
                goto fail;
 
-       if (min_ver[FW_VER_SUBTYPE] < fw_ver[FW_VER_SUBTYPE])
-               goto out;
-       else if (min_ver[FW_VER_SUBTYPE] > fw_ver[FW_VER_SUBTYPE])
+       /* the API version must be greater or equal */
+       if ((min_ver[FW_VER_MAJOR] != WLCORE_FW_VER_IGNORE) &&
+                (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR]))
                goto fail;
 
-       if (min_ver[FW_VER_MINOR] < fw_ver[FW_VER_MINOR])
-               goto out;
-       else if (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR])
+       /* if the API version is equal... */
+       if (((min_ver[FW_VER_MAJOR] == WLCORE_FW_VER_IGNORE) ||
+            (min_ver[FW_VER_MAJOR] == fw_ver[FW_VER_MAJOR])) &&
+           /* ...the minor must be greater or equal */
+           ((min_ver[FW_VER_MINOR] != WLCORE_FW_VER_IGNORE) &&
+            (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR])))
                goto fail;
 
-out:
        return 0;
 
 fail:
-       wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is outdated.\n"
-                    "Please use at least FW %u.%u.%u.%u.%u.\n"
-                    "You can get more information at:\n"
-                    "http://wireless.kernel.org/en/users/Drivers/wl12xx",
+       for (i = 0; i < NUM_FW_VER; i++)
+               if (min_ver[i] == WLCORE_FW_VER_IGNORE)
+                       snprintf(min_fw_str, sizeof(min_fw_str),
+                                 "%s*.", min_fw_str);
+               else
+                       snprintf(min_fw_str, sizeof(min_fw_str),
+                                 "%s%u.", min_fw_str, min_ver[i]);
+
+       wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is invalid.\n"
+                    "Please use at least FW %s\n"
+                    "You can get the latest firmwares at:\n"
+                    "git://github.com/TI-OpenLink/firmwares.git",
                     fw_ver[FW_VER_CHIP], fw_ver[FW_VER_IF_TYPE],
                     fw_ver[FW_VER_MAJOR], fw_ver[FW_VER_SUBTYPE],
-                    fw_ver[FW_VER_MINOR], min_ver[FW_VER_CHIP],
-                    min_ver[FW_VER_IF_TYPE], min_ver[FW_VER_MAJOR],
-                    min_ver[FW_VER_SUBTYPE], min_ver[FW_VER_MINOR]);
+                    fw_ver[FW_VER_MINOR], min_fw_str);
        return -EINVAL;
 }
 
@@ -491,7 +501,7 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
-       wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox);
+       wl->mbox_ptr[1] = wl->mbox_ptr[0] + wl->mbox_size;
 
        wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x",
                     wl->mbox_ptr[0], wl->mbox_ptr[1]);
@@ -508,23 +518,6 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
         */
 
        /* unmask required mbox events  */
-       wl->event_mask = BSS_LOSE_EVENT_ID |
-               REGAINED_BSS_EVENT_ID |
-               SCAN_COMPLETE_EVENT_ID |
-               ROLE_STOP_COMPLETE_EVENT_ID |
-               RSSI_SNR_TRIGGER_0_EVENT_ID |
-               PSPOLL_DELIVERY_FAILURE_EVENT_ID |
-               SOFT_GEMINI_SENSE_EVENT_ID |
-               PERIODIC_SCAN_REPORT_EVENT_ID |
-               PERIODIC_SCAN_COMPLETE_EVENT_ID |
-               DUMMY_PACKET_EVENT_ID |
-               PEER_REMOVE_COMPLETE_EVENT_ID |
-               BA_SESSION_RX_CONSTRAINT_EVENT_ID |
-               REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
-               INACTIVE_STA_EVENT_ID |
-               MAX_TX_RETRY_EVENT_ID |
-               CHANNEL_SWITCH_COMPLETE_EVENT_ID;
-
        ret = wl1271_event_unmask(wl);
        if (ret < 0) {
                wl1271_error("EVENT mask setting failed");
index 27f83f72a93bb9c946535886043750fbdadee632..c9e060795d13760befc72a9190b008b65b6f1b41 100644 (file)
  * @id: command id
  * @buf: buffer containing the command, must work with dma
  * @len: length of the buffer
+ * return the cmd status code on success.
  */
-int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
-                   size_t res_len)
+static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf,
+                            size_t len, size_t res_len)
 {
        struct wl1271_cmd_header *cmd;
        unsigned long timeout;
        u32 intr;
-       int ret = 0;
+       int ret;
        u16 status;
        u16 poll_count = 0;
 
@@ -71,7 +72,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
 
        ret = wlcore_write(wl, wl->cmd_box_addr, buf, len, false);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        /*
         * TODO: we just need this because one bit is in a different
@@ -79,19 +80,18 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
         */
        ret = wl->ops->trigger_cmd(wl, wl->cmd_box_addr, buf, len);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT);
 
        ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) {
                if (time_after(jiffies, timeout)) {
                        wl1271_error("command complete timeout");
-                       ret = -ETIMEDOUT;
-                       goto fail;
+                       return -ETIMEDOUT;
                }
 
                poll_count++;
@@ -102,7 +102,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
 
                ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
                if (ret < 0)
-                       goto fail;
+                       return ret;
        }
 
        /* read back the status code of the command */
@@ -111,33 +111,66 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
 
        ret = wlcore_read(wl, wl->cmd_box_addr, cmd, res_len, false);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        status = le16_to_cpu(cmd->status);
-       if (status != CMD_STATUS_SUCCESS) {
-               wl1271_error("command execute failure %d", status);
-               ret = -EIO;
-               goto fail;
-       }
 
        ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK,
                               WL1271_ACX_INTR_CMD_COMPLETE);
+       if (ret < 0)
+               return ret;
+
+       return status;
+}
+
+/*
+ * send command to fw and return cmd status on success
+ * valid_rets contains a bitmap of allowed error codes
+ */
+int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
+                            size_t res_len, unsigned long valid_rets)
+{
+       int ret = __wlcore_cmd_send(wl, id, buf, len, res_len);
+
        if (ret < 0)
                goto fail;
 
-       return 0;
+       /* success is always a valid status */
+       valid_rets |= BIT(CMD_STATUS_SUCCESS);
 
+       if (ret >= MAX_COMMAND_STATUS ||
+           !test_bit(ret, &valid_rets)) {
+               wl1271_error("command execute failure %d", ret);
+               ret = -EIO;
+               goto fail;
+       }
+       return ret;
 fail:
        wl12xx_queue_recovery_work(wl);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_cmd_send);
+
+/*
+ * wrapper for wlcore_cmd_send that accept only CMD_STATUS_SUCCESS
+ * return 0 on success.
+ */
+int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
+                   size_t res_len)
+{
+       int ret = wlcore_cmd_send_failsafe(wl, id, buf, len, res_len, 0);
+
+       if (ret < 0)
+               return ret;
+       return 0;
+}
 
 /*
  * Poll the mailbox event field until any of the bits in the mask is set or a
  * timeout occurs (WL1271_EVENT_TIMEOUT in msecs)
  */
-static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
-                                               u32 mask, bool *timeout)
+int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
+                                        u32 mask, bool *timeout)
 {
        u32 *events_vector;
        u32 event;
@@ -187,20 +220,7 @@ out:
        kfree(events_vector);
        return ret;
 }
-
-static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
-{
-       int ret;
-       bool timeout = false;
-
-       ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask, &timeout);
-       if (ret != 0 || timeout) {
-               wl12xx_queue_recovery_work(wl);
-               return ret;
-       }
-
-       return 0;
-}
+EXPORT_SYMBOL_GPL(wlcore_cmd_wait_for_event_or_timeout);
 
 int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
                           u8 *role_id)
@@ -278,6 +298,16 @@ out:
        return ret;
 }
 
+static int wlcore_get_new_session_id(struct wl1271 *wl, u8 hlid)
+{
+       if (wl->session_ids[hlid] >= SESSION_COUNTER_MAX)
+               wl->session_ids[hlid] = 0;
+
+       wl->session_ids[hlid]++;
+
+       return wl->session_ids[hlid];
+}
+
 int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
 {
        unsigned long flags;
@@ -285,12 +315,29 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        if (link >= WL12XX_MAX_LINKS)
                return -EBUSY;
 
+       wl->session_ids[link] = wlcore_get_new_session_id(wl, link);
+
        /* these bits are used by op_tx */
        spin_lock_irqsave(&wl->wl_lock, flags);
        __set_bit(link, wl->links_map);
        __set_bit(link, wlvif->links_map);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+       /* take the last "freed packets" value from the current FW status */
+       wl->links[link].prev_freed_pkts =
+                       wl->fw_status_2->counters.tx_lnk_free_pkts[link];
+       wl->links[link].wlvif = wlvif;
+
+       /*
+        * Take saved value for total freed packets from wlvif, in case this is
+        * recovery/resume
+        */
+       if (wlvif->bss_type != BSS_TYPE_AP_BSS)
+               wl->links[link].total_freed_pkts = wlvif->total_freed_pkts;
+
        *hlid = link;
+
+       wl->active_link_count++;
        return 0;
 }
 
@@ -307,24 +354,41 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        __clear_bit(*hlid, wlvif->links_map);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
+       wl->links[*hlid].allocated_pkts = 0;
+       wl->links[*hlid].prev_freed_pkts = 0;
+       wl->links[*hlid].ba_bitmap = 0;
+       memset(wl->links[*hlid].addr, 0, ETH_ALEN);
+
        /*
         * At this point op_tx() will not add more packets to the queues. We
         * can purge them.
         */
        wl1271_tx_reset_link_queues(wl, *hlid);
+       wl->links[*hlid].wlvif = NULL;
 
-       *hlid = WL12XX_INVALID_LINK_ID;
-}
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
+           (wlvif->bss_type == BSS_TYPE_AP_BSS &&
+            *hlid == wlvif->ap.bcast_hlid)) {
+               /*
+                * save the total freed packets in the wlvif, in case this is
+                * recovery or suspend
+                */
+               wlvif->total_freed_pkts = wl->links[*hlid].total_freed_pkts;
 
-static int wl12xx_get_new_session_id(struct wl1271 *wl,
-                                    struct wl12xx_vif *wlvif)
-{
-       if (wlvif->session_counter >= SESSION_COUNTER_MAX)
-               wlvif->session_counter = 0;
+               /*
+                * increment the initial seq number on recovery to account for
+                * transmitted packets that we haven't yet got in the FW status
+                */
+               if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+                       wlvif->total_freed_pkts +=
+                                       WL1271_TX_SQN_POST_RECOVERY_PADDING;
+       }
 
-       wlvif->session_counter++;
+       wl->links[*hlid].total_freed_pkts = 0;
 
-       return wlvif->session_counter;
+       *hlid = WL12XX_INVALID_LINK_ID;
+       wl->active_link_count--;
+       WARN_ON_ONCE(wl->active_link_count < 0);
 }
 
 static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
@@ -345,7 +409,9 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
 }
 
 static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
-                                    struct wl12xx_vif *wlvif)
+                                    struct wl12xx_vif *wlvif,
+                                    enum ieee80211_band band,
+                                    int channel)
 {
        struct wl12xx_cmd_role_start *cmd;
        int ret;
@@ -359,9 +425,9 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
        wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id);
 
        cmd->role_id = wlvif->dev_role_id;
-       if (wlvif->band == IEEE80211_BAND_5GHZ)
+       if (band == IEEE80211_BAND_5GHZ)
                cmd->band = WLCORE_BAND_5GHZ;
-       cmd->channel = wlvif->channel;
+       cmd->channel = channel;
 
        if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) {
                ret = wl12xx_allocate_link(wl, wlvif, &wlvif->dev_hlid);
@@ -369,7 +435,7 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
                        goto out_free;
        }
        cmd->device.hlid = wlvif->dev_hlid;
-       cmd->device.session = wl12xx_get_new_session_id(wl, wlvif);
+       cmd->device.session = wl->session_ids[wlvif->dev_hlid];
 
        wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d",
                     cmd->role_id, cmd->device.hlid, cmd->device.session);
@@ -420,12 +486,6 @@ static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl,
                goto out_free;
        }
 
-       ret = wl1271_cmd_wait_for_event(wl, ROLE_STOP_COMPLETE_EVENT_ID);
-       if (ret < 0) {
-               wl1271_error("cmd role stop dev event completion error");
-               goto out_free;
-       }
-
        wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid);
 
 out_free:
@@ -439,6 +499,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct wl12xx_cmd_role_start *cmd;
+       u32 supported_rates;
        int ret;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -459,7 +520,14 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        cmd->sta.ssid_len = wlvif->ssid_len;
        memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
        memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
-       cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
+
+       supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
+                         wlcore_hw_sta_get_ap_rate_mask(wl, wlvif);
+       if (wlvif->p2p)
+               supported_rates &= ~CONF_TX_CCK_RATES;
+
+       cmd->sta.local_rates = cpu_to_le32(supported_rates);
+
        cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
 
        if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
@@ -468,8 +536,14 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                        goto out_free;
        }
        cmd->sta.hlid = wlvif->sta.hlid;
-       cmd->sta.session = wl12xx_get_new_session_id(wl, wlvif);
-       cmd->sta.remote_rates = cpu_to_le32(wlvif->rate_set);
+       cmd->sta.session = wl->session_ids[wlvif->sta.hlid];
+       /*
+        * We don't have the correct remote rates in this stage.  The
+        * rates will be reconfigured later, after association, if the
+        * firmware supports ACX_PEER_CAP.  Otherwise, there's nothing
+        * we can do, so use all supported_rates here.
+        */
+       cmd->sta.remote_rates = cpu_to_le32(supported_rates);
 
        wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d "
                     "basic_rate_set: 0x%x, remote_rates: 0x%x",
@@ -482,6 +556,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                goto err_hlid;
        }
 
+       wlvif->sta.role_chan_type = wlvif->channel_type;
        goto out_free;
 
 err_hlid:
@@ -500,7 +575,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl12xx_cmd_role_stop *cmd;
        int ret;
-       bool timeout = false;
 
        if (WARN_ON(wlvif->sta.hlid == WL12XX_INVALID_LINK_ID))
                return -EINVAL;
@@ -523,17 +597,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                goto out_free;
        }
 
-       /*
-        * Sometimes the firmware doesn't send this event, so we just
-        * time out without failing.  Queue recovery for other
-        * failures.
-        */
-       ret = wl1271_cmd_wait_for_event_or_timeout(wl,
-                                                  ROLE_STOP_COMPLETE_EVENT_ID,
-                                                  &timeout);
-       if (ret)
-               wl12xx_queue_recovery_work(wl);
-
        wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
 
 out_free:
@@ -574,17 +637,24 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        if (ret < 0)
                goto out_free_global;
 
+       /* use the previous security seq, if this is a recovery/resume */
+       wl->links[wlvif->ap.bcast_hlid].total_freed_pkts =
+                                               wlvif->total_freed_pkts;
+
        cmd->role_id = wlvif->role_id;
        cmd->ap.aging_period = cpu_to_le16(wl->conf.tx.ap_aging_period);
        cmd->ap.bss_index = WL1271_AP_BSS_INDEX;
        cmd->ap.global_hlid = wlvif->ap.global_hlid;
        cmd->ap.broadcast_hlid = wlvif->ap.bcast_hlid;
+       cmd->ap.global_session_id = wl->session_ids[wlvif->ap.global_hlid];
+       cmd->ap.bcast_session_id = wl->session_ids[wlvif->ap.bcast_hlid];
        cmd->ap.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
        cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int);
        cmd->ap.dtim_interval = bss_conf->dtim_period;
        cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP;
        /* FIXME: Change when adding DFS */
        cmd->ap.reset_tsf = 1;  /* By default reset AP TSF */
+       cmd->ap.wmm = wlvif->wmm_enabled;
        cmd->channel = wlvif->channel;
        cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
 
@@ -599,8 +669,10 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len);
        }
 
-       supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES |
+       supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
                wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
+       if (wlvif->p2p)
+               supported_rates &= ~CONF_TX_CCK_RATES;
 
        wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x",
                     supported_rates);
@@ -799,8 +871,11 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
  * @id: acx id
  * @buf: buffer containing acx, including all headers, must work with dma
  * @len: length of buf
+ * @valid_rets: bitmap of valid cmd status codes (i.e. return values).
+ * return the cmd status on success.
  */
-int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
+int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
+                                 size_t len, unsigned long valid_rets)
 {
        struct acx_header *acx = buf;
        int ret;
@@ -812,12 +887,26 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
        /* payload length, does not include any headers */
        acx->len = cpu_to_le16(len - sizeof(*acx));
 
-       ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0);
+       ret = wlcore_cmd_send_failsafe(wl, CMD_CONFIGURE, acx, len, 0,
+                                      valid_rets);
        if (ret < 0) {
                wl1271_warning("CONFIGURE command NOK");
                return ret;
        }
 
+       return ret;
+}
+
+/*
+ * wrapper for wlcore_cmd_configure that accepts only success status.
+ * return 0 on success
+ */
+int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
+{
+       int ret = wlcore_cmd_configure_failsafe(wl, id, buf, len, 0);
+
+       if (ret < 0)
+               return ret;
        return 0;
 }
 EXPORT_SYMBOL_GPL(wl1271_cmd_configure);
@@ -1034,8 +1123,8 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        struct sk_buff *skb;
        int ret;
        u32 rate;
-       u16 template_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
-       u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+       u16 template_id_2_4 = wl->scan_templ_id_2_4;
+       u16 template_id_5 = wl->scan_templ_id_5;
 
        skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
                                     ie_len);
@@ -1048,10 +1137,10 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);
 
-       if (!sched_scan &&
+       if (sched_scan &&
            (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) {
-               template_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4;
-               template_id_5 = CMD_TEMPL_APP_PROBE_REQ_5;
+               template_id_2_4 = wl->sched_scan_templ_id_2_4;
+               template_id_5 = wl->sched_scan_templ_id_5;
        }
 
        rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
@@ -1068,6 +1157,7 @@ out:
        dev_kfree_skb(skb);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl12xx_cmd_build_probe_req);
 
 struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
                                              struct wl12xx_vif *wlvif,
@@ -1379,7 +1469,8 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid)
+int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 hlid)
 {
        struct wl12xx_cmd_set_peer_state *cmd;
        int ret = 0;
@@ -1395,6 +1486,10 @@ int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid)
        cmd->hlid = hlid;
        cmd->state = WL1271_CMD_STA_STATE_CONNECTED;
 
+       /* wmm param is valid only for station role */
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS)
+               cmd->wmm = wlvif->wmm_enabled;
+
        ret = wl1271_cmd_send(wl, CMD_SET_PEER_STATE, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
                wl1271_error("failed to send set peer state command");
@@ -1429,6 +1524,7 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        cmd->hlid = hlid;
        cmd->sp_len = sta->max_sp;
        cmd->wmm = sta->wme ? 1 : 0;
+       cmd->session_id = wl->session_ids[hlid];
 
        for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
                if (sta->wme && (sta->uapsd_queues & BIT(i)))
@@ -1490,9 +1586,10 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid)
                goto out_free;
        }
 
-       ret = wl1271_cmd_wait_for_event_or_timeout(wl,
-                                          PEER_REMOVE_COMPLETE_EVENT_ID,
-                                          &timeout);
+       ret = wl->ops->wait_for_event(wl,
+                                     WLCORE_EVENT_PEER_REMOVE_COMPLETE,
+                                     &timeout);
+
        /*
         * We are ok with a timeout here. The event is sometimes not sent
         * due to a firmware bug. In case of another error (like SDIO timeout)
@@ -1508,6 +1605,131 @@ out:
        return ret;
 }
 
+static int wlcore_get_reg_conf_ch_idx(enum ieee80211_band band, u16 ch)
+{
+       int idx = -1;
+
+       switch (band) {
+       case IEEE80211_BAND_5GHZ:
+               if (ch >= 8 && ch <= 16)
+                       idx = ((ch-8)/4 + 18);
+               else if (ch >= 34 && ch <= 64)
+                       idx = ((ch-34)/2 + 3 + 18);
+               else if (ch >= 100 && ch <= 140)
+                       idx = ((ch-100)/4 + 15 + 18);
+               else if (ch >= 149 && ch <= 165)
+                       idx = ((ch-149)/4 + 26 + 18);
+               else
+                       idx = -1;
+               break;
+       case IEEE80211_BAND_2GHZ:
+               if (ch >= 1 && ch <= 14)
+                       idx = ch - 1;
+               else
+                       idx = -1;
+               break;
+       default:
+               wl1271_error("get reg conf ch idx - unknown band: %d",
+                            (int)band);
+       }
+
+       return idx;
+}
+
+void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
+                                    enum ieee80211_band band)
+{
+       int ch_bit_idx = 0;
+
+       if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+               return;
+
+       ch_bit_idx = wlcore_get_reg_conf_ch_idx(band, channel);
+
+       if (ch_bit_idx > 0 && ch_bit_idx <= WL1271_MAX_CHANNELS)
+               set_bit(ch_bit_idx, (long *)wl->reg_ch_conf_pending);
+}
+
+int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl)
+{
+       struct wl12xx_cmd_regdomain_dfs_config *cmd = NULL;
+       int ret = 0, i, b, ch_bit_idx;
+       struct ieee80211_channel *channel;
+       u32 tmp_ch_bitmap[2];
+       u16 ch;
+       struct wiphy *wiphy = wl->hw->wiphy;
+       struct ieee80211_supported_band *band;
+       bool timeout = false;
+
+       if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+               return 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd reg domain config");
+
+       memset(tmp_ch_bitmap, 0, sizeof(tmp_ch_bitmap));
+
+       for (b = IEEE80211_BAND_2GHZ; b <= IEEE80211_BAND_5GHZ; b++) {
+               band = wiphy->bands[b];
+               for (i = 0; i < band->n_channels; i++) {
+                       channel = &band->channels[i];
+                       ch = channel->hw_value;
+
+                       if (channel->flags & (IEEE80211_CHAN_DISABLED |
+                                             IEEE80211_CHAN_RADAR |
+                                             IEEE80211_CHAN_PASSIVE_SCAN))
+                               continue;
+
+                       ch_bit_idx = wlcore_get_reg_conf_ch_idx(b, ch);
+                       if (ch_bit_idx < 0)
+                               continue;
+
+                       set_bit(ch_bit_idx, (long *)tmp_ch_bitmap);
+               }
+       }
+
+       tmp_ch_bitmap[0] |= wl->reg_ch_conf_pending[0];
+       tmp_ch_bitmap[1] |= wl->reg_ch_conf_pending[1];
+
+       if (!memcmp(tmp_ch_bitmap, wl->reg_ch_conf_last, sizeof(tmp_ch_bitmap)))
+               goto out;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->ch_bit_map1 = cpu_to_le32(tmp_ch_bitmap[0]);
+       cmd->ch_bit_map2 = cpu_to_le32(tmp_ch_bitmap[1]);
+
+       wl1271_debug(DEBUG_CMD,
+                    "cmd reg domain bitmap1: 0x%08x, bitmap2: 0x%08x",
+                    cmd->ch_bit_map1, cmd->ch_bit_map2);
+
+       ret = wl1271_cmd_send(wl, CMD_DFS_CHANNEL_CONFIG, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send reg domain dfs config");
+               goto out;
+       }
+
+       ret = wl->ops->wait_for_event(wl,
+                                     WLCORE_EVENT_DFS_CONFIG_COMPLETE,
+                                     &timeout);
+       if (ret < 0 || timeout) {
+               wl1271_error("reg domain conf %serror",
+                            timeout ? "completion " : "");
+               ret = timeout ? -ETIMEDOUT : ret;
+               goto out;
+       }
+
+       memcpy(wl->reg_ch_conf_last, tmp_ch_bitmap, sizeof(tmp_ch_bitmap));
+       memset(wl->reg_ch_conf_pending, 0, sizeof(wl->reg_ch_conf_pending));
+
+out:
+       kfree(cmd);
+       return ret;
+}
+
 int wl12xx_cmd_config_fwlog(struct wl1271 *wl)
 {
        struct wl12xx_cmd_config_fwlog *cmd;
@@ -1593,12 +1815,12 @@ out:
 }
 
 static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                         u8 role_id)
+                         u8 role_id, enum ieee80211_band band, u8 channel)
 {
        struct wl12xx_cmd_roc *cmd;
        int ret = 0;
 
-       wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wlvif->channel, role_id);
+       wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", channel, role_id);
 
        if (WARN_ON(role_id == WL12XX_INVALID_ROLE_ID))
                return -EINVAL;
@@ -1610,8 +1832,8 @@ static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        }
 
        cmd->role_id = role_id;
-       cmd->channel = wlvif->channel;
-       switch (wlvif->band) {
+       cmd->channel = channel;
+       switch (band) {
        case IEEE80211_BAND_2GHZ:
                cmd->band = WLCORE_BAND_2_4GHZ;
                break;
@@ -1666,30 +1888,18 @@ out:
        return ret;
 }
 
-int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id)
+int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
+              enum ieee80211_band band, u8 channel)
 {
        int ret = 0;
-       bool is_first_roc;
 
        if (WARN_ON(test_bit(role_id, wl->roc_map)))
                return 0;
 
-       is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >=
-                       WL12XX_MAX_ROLES);
-
-       ret = wl12xx_cmd_roc(wl, wlvif, role_id);
+       ret = wl12xx_cmd_roc(wl, wlvif, role_id, band, channel);
        if (ret < 0)
                goto out;
 
-       if (is_first_roc) {
-               ret = wl1271_cmd_wait_for_event(wl,
-                                          REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID);
-               if (ret < 0) {
-                       wl1271_error("cmd roc event completion error");
-                       goto out;
-               }
-       }
-
        __set_bit(role_id, wl->roc_map);
 out:
        return ret;
@@ -1719,43 +1929,7 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_channel_switch(struct wl1271 *wl,
-                             struct wl12xx_vif *wlvif,
-                             struct ieee80211_channel_switch *ch_switch)
-{
-       struct wl12xx_cmd_channel_switch *cmd;
-       int ret;
-
-       wl1271_debug(DEBUG_ACX, "cmd channel switch");
-
-       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-       if (!cmd) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       cmd->role_id = wlvif->role_id;
-       cmd->channel = ch_switch->channel->hw_value;
-       cmd->switch_time = ch_switch->count;
-       cmd->stop_tx = ch_switch->block_tx;
-
-       /* FIXME: control from mac80211 in the future */
-       cmd->post_switch_tx_disable = 0;  /* Enable TX on the target channel */
-
-       ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
-       if (ret < 0) {
-               wl1271_error("failed to send channel switch command");
-               goto out_free;
-       }
-
-out_free:
-       kfree(cmd);
-
-out:
-       return ret;
-}
-
-int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl)
+int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl12xx_cmd_stop_channel_switch *cmd;
        int ret;
@@ -1768,6 +1942,8 @@ int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl)
                goto out;
        }
 
+       cmd->role_id = wlvif->role_id;
+
        ret = wl1271_cmd_send(wl, CMD_STOP_CHANNEL_SWICTH, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
                wl1271_error("failed to stop channel switch command");
@@ -1782,7 +1958,8 @@ out:
 }
 
 /* start dev role and roc on its channel */
-int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                    enum ieee80211_band band, int channel)
 {
        int ret;
 
@@ -1797,11 +1974,11 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        if (ret < 0)
                goto out;
 
-       ret = wl12xx_cmd_role_start_dev(wl, wlvif);
+       ret = wl12xx_cmd_role_start_dev(wl, wlvif, band, channel);
        if (ret < 0)
                goto out_disable;
 
-       ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id);
+       ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id, band, channel);
        if (ret < 0)
                goto out_stop;
 
index 2409f3d71f63ddd457cc571b7a76798234d16e0b..fd34123047cdd0255f5a48b7826ddd117b14814d 100644 (file)
@@ -31,6 +31,8 @@ struct acx_header;
 
 int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
                    size_t res_len);
+int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
+                            size_t res_len, unsigned long valid_rets);
 int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
                           u8 *role_id);
 int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id);
@@ -39,11 +41,14 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                    enum ieee80211_band band, int channel);
 int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
 int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
 int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
+int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
+                                 size_t len, unsigned long valid_rets);
 int wl1271_cmd_data_path(struct wl1271 *wl, bool enable);
 int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                       u8 ps_mode, u16 auto_ps_timeout);
@@ -75,22 +80,30 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                          u16 action, u8 id, u8 key_type,
                          u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
                          u16 tx_seq_16);
-int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid);
-int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id);
+int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 hlid);
+int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
+              enum ieee80211_band band, u8 channel);
 int wl12xx_croc(struct wl1271 *wl, u8 role_id);
 int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                        struct ieee80211_sta *sta, u8 hlid);
 int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid);
+void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
+                                    enum ieee80211_band band);
+int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl);
 int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
 int wl12xx_cmd_start_fwlog(struct wl1271 *wl);
 int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
 int wl12xx_cmd_channel_switch(struct wl1271 *wl,
                              struct wl12xx_vif *wlvif,
                              struct ieee80211_channel_switch *ch_switch);
-int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl);
+int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif);
 int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                         u8 *hlid);
 void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid);
+int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
+                                        u32 mask, bool *timeout);
 
 enum wl1271_commands {
        CMD_INTERROGATE = 1, /* use this to read information elements */
@@ -149,8 +162,11 @@ enum wl1271_commands {
        CMD_WFD_START_DISCOVERY = 45,
        CMD_WFD_STOP_DISCOVERY  = 46,
        CMD_WFD_ATTRIBUTE_CONFIG        = 47,
-       CMD_NOP                 = 48,
-       CMD_LAST_COMMAND,
+       CMD_GENERIC_CFG                 = 48,
+       CMD_NOP                         = 49,
+
+       /* start of 18xx specific commands */
+       CMD_DFS_CHANNEL_CONFIG          = 60,
 
        MAX_COMMAND_ID = 0xFFFF,
 };
@@ -167,8 +183,8 @@ enum cmd_templ {
        CMD_TEMPL_PS_POLL,
        CMD_TEMPL_KLV,
        CMD_TEMPL_DISCONNECT,
-       CMD_TEMPL_APP_PROBE_REQ_2_4,
-       CMD_TEMPL_APP_PROBE_REQ_5,
+       CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY,
+       CMD_TEMPL_APP_PROBE_REQ_5_LEGACY,
        CMD_TEMPL_BAR,           /* for firmware internal use only */
        CMD_TEMPL_CTS,           /*
                                  * For CTS-to-self (FastCTS) mechanism
@@ -179,6 +195,8 @@ enum cmd_templ {
        CMD_TEMPL_DEAUTH_AP,
        CMD_TEMPL_TEMPORARY,
        CMD_TEMPL_LINK_MEASUREMENT_REPORT,
+       CMD_TEMPL_PROBE_REQ_2_4_PERIODIC,
+       CMD_TEMPL_PROBE_REQ_5_PERIODIC,
 
        CMD_TEMPL_MAX = 0xff
 };
@@ -220,7 +238,8 @@ enum {
        CMD_STATUS_FW_RESET             = 22, /* Driver internal use.*/
        CMD_STATUS_TEMPLATE_OOM         = 23,
        CMD_STATUS_NO_RX_BA_SESSION     = 24,
-       MAX_COMMAND_STATUS              = 0xff
+
+       MAX_COMMAND_STATUS
 };
 
 #define CMDMBOX_HEADER_LEN 4
@@ -345,7 +364,15 @@ struct wl12xx_cmd_role_start {
 
                        u8 reset_tsf;
 
-                       u8 padding_1[4];
+                       /*
+                        * ap supports wmm (note that there is additional
+                        * per-sta wmm configuration)
+                        */
+                       u8 wmm;
+
+                       u8 bcast_session_id;
+                       u8 global_session_id;
+                       u8 padding_1[1];
                } __packed ap;
        };
 } __packed;
@@ -515,7 +542,14 @@ struct wl12xx_cmd_set_peer_state {
 
        u8 hlid;
        u8 state;
-       u8 padding[2];
+
+       /*
+        * wmm is relevant for sta role only.
+        * ap role configures the per-sta wmm params in
+        * the add_peer command.
+        */
+       u8 wmm;
+       u8 padding[1];
 } __packed;
 
 struct wl12xx_cmd_roc {
@@ -558,7 +592,7 @@ struct wl12xx_cmd_add_peer {
        u8 bss_index;
        u8 sp_len;
        u8 wmm;
-       u8 padding1;
+       u8 session_id;
 } __packed;
 
 struct wl12xx_cmd_remove_peer {
@@ -597,6 +631,13 @@ enum wl12xx_fwlogger_output {
        WL12XX_FWLOG_OUTPUT_HOST,
 };
 
+struct wl12xx_cmd_regdomain_dfs_config {
+       struct wl1271_cmd_header header;
+
+       __le32 ch_bit_map1;
+       __le32 ch_bit_map2;
+} __packed;
+
 struct wl12xx_cmd_config_fwlog {
        struct wl1271_cmd_header header;
 
@@ -626,27 +667,13 @@ struct wl12xx_cmd_stop_fwlog {
        struct wl1271_cmd_header header;
 } __packed;
 
-struct wl12xx_cmd_channel_switch {
+struct wl12xx_cmd_stop_channel_switch {
        struct wl1271_cmd_header header;
 
        u8 role_id;
-
-       /* The new serving channel */
-       u8 channel;
-       /* Relative time of the serving channel switch in TBTT units */
-       u8 switch_time;
-       /* Stop the role TX, should expect it after radar detection */
-       u8 stop_tx;
-       /* The target channel tx status 1-stopped 0-open*/
-       u8 post_switch_tx_disable;
-
        u8 padding[3];
 } __packed;
 
-struct wl12xx_cmd_stop_channel_switch {
-       struct wl1271_cmd_header header;
-} __packed;
-
 /* Used to check radio status after calibration */
 #define MAX_TLV_LENGTH         500
 #define TEST_CMD_P2G_CAL       2       /* TX BiP */
index 9e40760bafe17b43121585d63bacfaa1925611b3..2b96ff821341103ac935ade4765409b21e142409 100644 (file)
@@ -57,20 +57,49 @@ enum {
 };
 
 enum {
-       CONF_HW_RATE_INDEX_1MBPS   = 0,
-       CONF_HW_RATE_INDEX_2MBPS   = 1,
-       CONF_HW_RATE_INDEX_5_5MBPS = 2,
-       CONF_HW_RATE_INDEX_6MBPS   = 3,
-       CONF_HW_RATE_INDEX_9MBPS   = 4,
-       CONF_HW_RATE_INDEX_11MBPS  = 5,
-       CONF_HW_RATE_INDEX_12MBPS  = 6,
-       CONF_HW_RATE_INDEX_18MBPS  = 7,
-       CONF_HW_RATE_INDEX_22MBPS  = 8,
-       CONF_HW_RATE_INDEX_24MBPS  = 9,
-       CONF_HW_RATE_INDEX_36MBPS  = 10,
-       CONF_HW_RATE_INDEX_48MBPS  = 11,
-       CONF_HW_RATE_INDEX_54MBPS  = 12,
-       CONF_HW_RATE_INDEX_MAX     = CONF_HW_RATE_INDEX_54MBPS,
+       CONF_HW_RATE_INDEX_1MBPS      = 0,
+       CONF_HW_RATE_INDEX_2MBPS      = 1,
+       CONF_HW_RATE_INDEX_5_5MBPS    = 2,
+       CONF_HW_RATE_INDEX_11MBPS     = 3,
+       CONF_HW_RATE_INDEX_6MBPS      = 4,
+       CONF_HW_RATE_INDEX_9MBPS      = 5,
+       CONF_HW_RATE_INDEX_12MBPS     = 6,
+       CONF_HW_RATE_INDEX_18MBPS     = 7,
+       CONF_HW_RATE_INDEX_24MBPS     = 8,
+       CONF_HW_RATE_INDEX_36MBPS     = 9,
+       CONF_HW_RATE_INDEX_48MBPS     = 10,
+       CONF_HW_RATE_INDEX_54MBPS     = 11,
+       CONF_HW_RATE_INDEX_MCS0       = 12,
+       CONF_HW_RATE_INDEX_MCS1       = 13,
+       CONF_HW_RATE_INDEX_MCS2       = 14,
+       CONF_HW_RATE_INDEX_MCS3       = 15,
+       CONF_HW_RATE_INDEX_MCS4       = 16,
+       CONF_HW_RATE_INDEX_MCS5       = 17,
+       CONF_HW_RATE_INDEX_MCS6       = 18,
+       CONF_HW_RATE_INDEX_MCS7       = 19,
+       CONF_HW_RATE_INDEX_MCS7_SGI   = 20,
+       CONF_HW_RATE_INDEX_MCS0_40MHZ = 21,
+       CONF_HW_RATE_INDEX_MCS1_40MHZ = 22,
+       CONF_HW_RATE_INDEX_MCS2_40MHZ = 23,
+       CONF_HW_RATE_INDEX_MCS3_40MHZ = 24,
+       CONF_HW_RATE_INDEX_MCS4_40MHZ = 25,
+       CONF_HW_RATE_INDEX_MCS5_40MHZ = 26,
+       CONF_HW_RATE_INDEX_MCS6_40MHZ = 27,
+       CONF_HW_RATE_INDEX_MCS7_40MHZ = 28,
+       CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI = 29,
+
+       /* MCS8+ rates overlap with 40Mhz rates */
+       CONF_HW_RATE_INDEX_MCS8       = 21,
+       CONF_HW_RATE_INDEX_MCS9       = 22,
+       CONF_HW_RATE_INDEX_MCS10      = 23,
+       CONF_HW_RATE_INDEX_MCS11      = 24,
+       CONF_HW_RATE_INDEX_MCS12      = 25,
+       CONF_HW_RATE_INDEX_MCS13      = 26,
+       CONF_HW_RATE_INDEX_MCS14      = 27,
+       CONF_HW_RATE_INDEX_MCS15      = 28,
+       CONF_HW_RATE_INDEX_MCS15_SGI  = 29,
+
+       CONF_HW_RATE_INDEX_MAX        = CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI,
 };
 
 #define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff
@@ -415,11 +444,11 @@ struct conf_rx_settings {
 #define CONF_TX_RATE_MASK_BASIC_P2P    CONF_HW_BIT_RATE_6MBPS
 
 /*
- * Rates supported for data packets when operating as AP. Note the absence
+ * Rates supported for data packets when operating as STA/AP. Note the absence
  * of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop
  * one. The rate dropped is not mandatory under any operating mode.
  */
-#define CONF_TX_AP_ENABLED_RATES       (CONF_HW_BIT_RATE_1MBPS | \
+#define CONF_TX_ENABLED_RATES       (CONF_HW_BIT_RATE_1MBPS |    \
        CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS |      \
        CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS |        \
        CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS |      \
@@ -677,6 +706,18 @@ struct conf_tx_settings {
 
        /* Time in ms for Tx watchdog timer to expire */
        u32 tx_watchdog_timeout;
+
+       /*
+        * when a slow link has this much packets pending, it becomes a low
+        * priority link, scheduling-wise
+        */
+       u8 slow_link_thold;
+
+       /*
+        * when a fast link has this much packets pending, it becomes a low
+        * priority link, scheduling-wise
+        */
+       u8 fast_link_thold;
 } __packed;
 
 enum {
@@ -1047,6 +1088,7 @@ struct conf_roam_trigger_settings {
 struct conf_scan_settings {
        /*
         * The minimum time to wait on each channel for active scans
+        * This value will be used whenever there's a connected interface.
         *
         * Range: u32 tu/1000
         */
@@ -1054,24 +1096,37 @@ struct conf_scan_settings {
 
        /*
         * The maximum time to wait on each channel for active scans
+        * This value will be currently used whenever there's a
+        * connected interface. It shouldn't exceed 30000 (~30ms) to avoid
+        * possible interference of voip traffic going on while scanning.
         *
         * Range: u32 tu/1000
         */
        u32 max_dwell_time_active;
 
-       /*
-        * The minimum time to wait on each channel for passive scans
+       /* The minimum time to wait on each channel for active scans
+        * when it's possible to have longer scan dwell times.
+        * Currently this is used whenever we're idle on all interfaces.
+        * Longer dwell times improve detection of networks within a
+        * single scan.
         *
         * Range: u32 tu/1000
         */
-       u32 min_dwell_time_passive;
+       u32 min_dwell_time_active_long;
 
-       /*
-        * The maximum time to wait on each channel for passive scans
+       /* The maximum time to wait on each channel for active scans
+        * when it's possible to have longer scan dwell times.
+        * See min_dwell_time_active_long
         *
         * Range: u32 tu/1000
         */
-       u32 max_dwell_time_passive;
+       u32 max_dwell_time_active_long;
+
+       /* time to wait on the channel for passive scans (in TU/1000) */
+       u32 dwell_time_passive;
+
+       /* time to wait on the channel for DFS scans (in TU/1000) */
+       u32 dwell_time_dfs;
 
        /*
         * Number of probe requests to transmit on each active scan channel
@@ -1276,12 +1331,20 @@ struct conf_hangover_settings {
        u8 window_size;
 } __packed;
 
+struct conf_recovery_settings {
+       /* BUG() on fw recovery */
+       u8 bug_on_recovery;
+
+       /* Prevent HW recovery. FW will remain stuck. */
+       u8 no_recovery;
+} __packed;
+
 /*
  * The conf version consists of 4 bytes.  The two MSB are the wlcore
  * version, the two LSB are the lower driver's private conf
  * version.
  */
-#define WLCORE_CONF_VERSION    (0x0002 << 16)
+#define WLCORE_CONF_VERSION    (0x0005 << 16)
 #define WLCORE_CONF_MASK       0xffff0000
 #define WLCORE_CONF_SIZE       (sizeof(struct wlcore_conf_header) +    \
                                 sizeof(struct wlcore_conf))
@@ -1309,6 +1372,7 @@ struct wlcore_conf {
        struct conf_fwlog fwlog;
        struct conf_rate_policy_settings rate;
        struct conf_hangover_settings hangover;
+       struct conf_recovery_settings recovery;
 } __packed;
 
 struct wlcore_conf_file {
index db4bf5a68ce208c1fa3e98fbe848221051ce0ec5..0420bd45e4ee4f9e16c2de51f4240995cbf45254 100644 (file)
@@ -89,25 +89,24 @@ extern u32 wl12xx_debug_level;
        } while (0)
 #endif
 
-/* TODO: use pr_debug_hex_dump when it becomes available */
-#define wl1271_dump(level, prefix, buf, len)   \
-       do { \
-               if (level & wl12xx_debug_level) \
-                       print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
-                                      DUMP_PREFIX_OFFSET, 16, 1,       \
-                                      buf,                             \
-                                      min_t(size_t, len, DEBUG_DUMP_LIMIT), \
-                                      0);                              \
+#define wl1271_dump(level, prefix, buf, len)                                 \
+       do {                                                                  \
+               if (level & wl12xx_debug_level)                               \
+                       print_hex_dump_debug(DRIVER_PREFIX prefix,            \
+                                       DUMP_PREFIX_OFFSET, 16, 1,            \
+                                       buf,                                  \
+                                       min_t(size_t, len, DEBUG_DUMP_LIMIT), \
+                                       0);                                   \
        } while (0)
 
-#define wl1271_dump_ascii(level, prefix, buf, len)     \
-       do { \
-               if (level & wl12xx_debug_level) \
-                       print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
-                                      DUMP_PREFIX_OFFSET, 16, 1,       \
-                                      buf,                             \
-                                      min_t(size_t, len, DEBUG_DUMP_LIMIT), \
-                                      true);                           \
+#define wl1271_dump_ascii(level, prefix, buf, len)                           \
+       do {                                                                  \
+               if (level & wl12xx_debug_level)                               \
+                       print_hex_dump_debug(DRIVER_PREFIX prefix,            \
+                                       DUMP_PREFIX_OFFSET, 16, 1,            \
+                                       buf,                                  \
+                                       min_t(size_t, len, DEBUG_DUMP_LIMIT), \
+                                       true);                                \
        } while (0)
 
 #endif /* __DEBUG_H__ */
index c86bb00c24884d355e93af45917b30defe86f220..c3e1f79c785697d95dfc9ccfac1e49410f263d8f 100644 (file)
@@ -490,7 +490,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        DRIVER_STATE_PRINT_HEX(chip.id);
        DRIVER_STATE_PRINT_STR(chip.fw_ver_str);
        DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str);
-       DRIVER_STATE_PRINT_INT(sched_scanning);
+       DRIVER_STATE_PRINT_INT(recovery_count);
 
 #undef DRIVER_STATE_PRINT_INT
 #undef DRIVER_STATE_PRINT_LONG
@@ -560,7 +560,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
                if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
                    wlvif->bss_type == BSS_TYPE_IBSS) {
                        VIF_STATE_PRINT_INT(sta.hlid);
-                       VIF_STATE_PRINT_INT(sta.ba_rx_bitmap);
                        VIF_STATE_PRINT_INT(sta.basic_rate_idx);
                        VIF_STATE_PRINT_INT(sta.ap_rate_idx);
                        VIF_STATE_PRINT_INT(sta.p2p_rate_idx);
@@ -577,6 +576,10 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
                        VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]);
                }
                VIF_STATE_PRINT_INT(last_tx_hlid);
+               VIF_STATE_PRINT_INT(tx_queue_count[0]);
+               VIF_STATE_PRINT_INT(tx_queue_count[1]);
+               VIF_STATE_PRINT_INT(tx_queue_count[2]);
+               VIF_STATE_PRINT_INT(tx_queue_count[3]);
                VIF_STATE_PRINT_LHEX(links_map[0]);
                VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len);
                VIF_STATE_PRINT_INT(band);
@@ -589,15 +592,13 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
                VIF_STATE_PRINT_INT(beacon_int);
                VIF_STATE_PRINT_INT(default_key);
                VIF_STATE_PRINT_INT(aid);
-               VIF_STATE_PRINT_INT(session_counter);
                VIF_STATE_PRINT_INT(psm_entry_retry);
                VIF_STATE_PRINT_INT(power_level);
                VIF_STATE_PRINT_INT(rssi_thold);
                VIF_STATE_PRINT_INT(last_rssi_event);
                VIF_STATE_PRINT_INT(ba_support);
                VIF_STATE_PRINT_INT(ba_allowed);
-               VIF_STATE_PRINT_LLHEX(tx_security_seq);
-               VIF_STATE_PRINT_INT(tx_security_last_seq_lsb);
+               VIF_STATE_PRINT_LLHEX(total_freed_pkts);
        }
 
 #undef VIF_STATE_PRINT_INT
@@ -993,7 +994,7 @@ static ssize_t sleep_auth_write(struct file *file,
                return -EINVAL;
        }
 
-       if (value < 0 || value > WL1271_PSM_MAX) {
+       if (value > WL1271_PSM_MAX) {
                wl1271_warning("sleep_auth must be between 0 and %d",
                               WL1271_PSM_MAX);
                return -ERANGE;
index 48907054d493134354ad22b0305cbc45a89bde2f..67f61689b49edcba1e54198db538eb277aa4bda0 100644 (file)
 #include "scan.h"
 #include "wl12xx_80211.h"
 
-static void wl1271_event_rssi_trigger(struct wl1271 *wl,
-                                     struct wl12xx_vif *wlvif,
-                                     struct event_mailbox *mbox)
+void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr)
 {
-       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
        enum nl80211_cqm_rssi_threshold_event event;
-       s8 metric = mbox->rssi_snr_trigger_metric[0];
+       s8 metric = metric_arr[0];
 
        wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);
 
-       if (metric <= wlvif->rssi_thold)
-               event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
-       else
-               event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
-
-       if (event != wlvif->last_rssi_event)
-               ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
-       wlvif->last_rssi_event = event;
+       /* TODO: check actual multi-role support */
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (metric <= wlvif->rssi_thold)
+                       event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
+               else
+                       event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+
+               vif = wl12xx_wlvif_to_vif(wlvif);
+               if (event != wlvif->last_rssi_event)
+                       ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
+               wlvif->last_rssi_event = event;
+       }
 }
+EXPORT_SYMBOL_GPL(wlcore_event_rssi_trigger);
 
 static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 
        if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
-               if (!wlvif->sta.ba_rx_bitmap)
+               u8 hlid = wlvif->sta.hlid;
+               if (!wl->links[hlid].ba_bitmap)
                        return;
-               ieee80211_stop_rx_ba_session(vif, wlvif->sta.ba_rx_bitmap,
+               ieee80211_stop_rx_ba_session(vif, wl->links[hlid].ba_bitmap,
                                             vif->bss_conf.bssid);
        } else {
                u8 hlid;
@@ -74,8 +79,7 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        }
 }
 
-static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
-                                              u8 enable)
+void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable)
 {
        struct wl12xx_vif *wlvif;
 
@@ -87,201 +91,176 @@ static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
                        wl1271_recalc_rx_streaming(wl, wlvif);
                }
        }
-
 }
+EXPORT_SYMBOL_GPL(wlcore_event_soft_gemini_sense);
 
-static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
+void wlcore_event_sched_scan_completed(struct wl1271 *wl,
+                                      u8 status)
 {
-       wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
-       wl1271_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector);
-       wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask);
+       wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)",
+                    status);
+
+       if (wl->sched_vif) {
+               ieee80211_sched_scan_stopped(wl->hw);
+               wl->sched_vif = NULL;
+       }
 }
+EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed);
 
-static int wl1271_event_process(struct wl1271 *wl)
+void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
+                                  unsigned long roles_bitmap,
+                                  unsigned long allowed_bitmap)
 {
-       struct event_mailbox *mbox = wl->mbox;
-       struct ieee80211_vif *vif;
        struct wl12xx_vif *wlvif;
-       u32 vector;
-       bool disconnect_sta = false;
-       unsigned long sta_bitmap = 0;
-       int ret;
 
-       wl1271_event_mbox_dump(mbox);
+       wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx",
+                    __func__, roles_bitmap, allowed_bitmap);
 
-       vector = le32_to_cpu(mbox->events_vector);
-       vector &= ~(le32_to_cpu(mbox->events_mask));
-       wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
+                   !test_bit(wlvif->role_id , &roles_bitmap))
+                       continue;
 
-       if (vector & SCAN_COMPLETE_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "status: 0x%x",
-                            mbox->scheduled_scan_status);
-
-               wl1271_scan_stm(wl, wl->scan_vif);
-       }
-
-       if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT "
-                            "(status 0x%0x)", mbox->scheduled_scan_status);
-
-               wl1271_scan_sched_scan_results(wl);
+               wlvif->ba_allowed = !!test_bit(wlvif->role_id,
+                                              &allowed_bitmap);
+               if (!wlvif->ba_allowed)
+                       wl1271_stop_ba_event(wl, wlvif);
        }
+}
+EXPORT_SYMBOL_GPL(wlcore_event_ba_rx_constraint);
 
-       if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT "
-                            "(status 0x%0x)", mbox->scheduled_scan_status);
-               if (wl->sched_scanning) {
-                       ieee80211_sched_scan_stopped(wl->hw);
-                       wl->sched_scanning = false;
-               }
-       }
+void wlcore_event_channel_switch(struct wl1271 *wl,
+                                unsigned long roles_bitmap,
+                                bool success)
+{
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
 
-       if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
-               wl12xx_event_soft_gemini_sense(wl,
-                                              mbox->soft_gemini_sense_info);
+       wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d",
+                    __func__, roles_bitmap, success);
 
-       /*
-        * We are HW_MONITOR device. On beacon loss - queue
-        * connection loss work. Cancel it on REGAINED event.
-        */
-       if (vector & BSS_LOSE_EVENT_ID) {
-               /* TODO: check for multi-role */
-               int delay = wl->conf.conn.synch_fail_thold *
-                                       wl->conf.conn.bss_lose_timeout;
-               wl1271_info("Beacon loss detected.");
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
+                   !test_bit(wlvif->role_id , &roles_bitmap))
+                       continue;
 
-               /*
-                * if the work is already queued, it should take place. We
-                * don't want to delay the connection loss indication
-                * any more.
-                */
-               ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work,
-                                            msecs_to_jiffies(delay));
+               if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
+                                       &wlvif->flags))
+                       continue;
 
-               wl12xx_for_each_wlvif_sta(wl, wlvif) {
-                       vif = wl12xx_wlvif_to_vif(wlvif);
+               vif = wl12xx_wlvif_to_vif(wlvif);
 
-                       ieee80211_cqm_rssi_notify(
-                                       vif,
-                                       NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
-                                       GFP_KERNEL);
-               }
+               ieee80211_chswitch_done(vif, success);
+               cancel_delayed_work(&wlvif->channel_switch_work);
        }
+}
+EXPORT_SYMBOL_GPL(wlcore_event_channel_switch);
 
-       if (vector & REGAINED_BSS_EVENT_ID) {
-               /* TODO: check for multi-role */
-               wl1271_info("Beacon regained.");
-               cancel_delayed_work(&wl->connection_loss_work);
-
-               /* sanity check - we can't lose and gain the beacon together */
-               WARN(vector & BSS_LOSE_EVENT_ID,
-                    "Concurrent beacon loss and gain from FW");
-       }
+void wlcore_event_dummy_packet(struct wl1271 *wl)
+{
+       wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
+       wl1271_tx_dummy_packet(wl);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_dummy_packet);
 
-       if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
-               /* TODO: check actual multi-role support */
-               wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
-               wl12xx_for_each_wlvif_sta(wl, wlvif) {
-                       wl1271_event_rssi_trigger(wl, wlvif, mbox);
+static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap)
+{
+       u32 num_packets = wl->conf.tx.max_tx_retries;
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
+       struct ieee80211_sta *sta;
+       const u8 *addr;
+       int h;
+
+       for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
+               bool found = false;
+               /* find the ap vif connected to this sta */
+               wl12xx_for_each_wlvif_ap(wl, wlvif) {
+                       if (!test_bit(h, wlvif->ap.sta_hlid_map))
+                               continue;
+                       found = true;
+                       break;
                }
-       }
+               if (!found)
+                       continue;
 
-       if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) {
-               u8 role_id = mbox->role_id;
-               wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. "
-                            "ba_allowed = 0x%x, role_id=%d",
-                            mbox->rx_ba_allowed, role_id);
-
-               wl12xx_for_each_wlvif(wl, wlvif) {
-                       if (role_id != 0xff && role_id != wlvif->role_id)
-                               continue;
+               vif = wl12xx_wlvif_to_vif(wlvif);
+               addr = wl->links[h].addr;
 
-                       wlvif->ba_allowed = !!mbox->rx_ba_allowed;
-                       if (!wlvif->ba_allowed)
-                               wl1271_stop_ba_event(wl, wlvif);
+               rcu_read_lock();
+               sta = ieee80211_find_sta(vif, addr);
+               if (sta) {
+                       wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
+                       ieee80211_report_low_ack(sta, num_packets);
                }
+               rcu_read_unlock();
        }
+}
 
-       if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. "
-                                         "status = 0x%x",
-                                         mbox->channel_switch_status);
-               /*
-                * That event uses for two cases:
-                * 1) channel switch complete with status=0
-                * 2) channel switch failed status=1
-                */
-
-               /* TODO: configure only the relevant vif */
-               wl12xx_for_each_wlvif_sta(wl, wlvif) {
-                       bool success;
-
-                       if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
-                                               &wlvif->flags))
-                               continue;
-
-                       success = mbox->channel_switch_status ? false : true;
-                       vif = wl12xx_wlvif_to_vif(wlvif);
+void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap)
+{
+       wl1271_debug(DEBUG_EVENT, "MAX_TX_FAILURE_EVENT_ID");
+       wlcore_disconnect_sta(wl, sta_bitmap);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_max_tx_failure);
 
-                       ieee80211_chswitch_done(vif, success);
-               }
-       }
+void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap)
+{
+       wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
+       wlcore_disconnect_sta(wl, sta_bitmap);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_inactive_sta);
 
-       if ((vector & DUMMY_PACKET_EVENT_ID)) {
-               wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
-               ret = wl1271_tx_dummy_packet(wl);
-               if (ret < 0)
-                       return ret;
-       }
+void wlcore_event_roc_complete(struct wl1271 *wl)
+{
+       wl1271_debug(DEBUG_EVENT, "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID");
+       if (wl->roc_vif)
+               ieee80211_ready_on_channel(wl->hw);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_roc_complete);
 
+void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap)
+{
        /*
-        * "TX retries exceeded" has a different meaning according to mode.
-        * In AP mode the offending station is disconnected.
+        * We are HW_MONITOR device. On beacon loss - queue
+        * connection loss work. Cancel it on REGAINED event.
         */
-       if (vector & MAX_TX_RETRY_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID");
-               sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded);
-               disconnect_sta = true;
-       }
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
+       int delay = wl->conf.conn.synch_fail_thold *
+                               wl->conf.conn.bss_lose_timeout;
 
-       if (vector & INACTIVE_STA_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
-               sta_bitmap |= le16_to_cpu(mbox->sta_aging_status);
-               disconnect_sta = true;
-       }
+       wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap);
 
-       if (disconnect_sta) {
-               u32 num_packets = wl->conf.tx.max_tx_retries;
-               struct ieee80211_sta *sta;
-               const u8 *addr;
-               int h;
-
-               for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
-                       bool found = false;
-                       /* find the ap vif connected to this sta */
-                       wl12xx_for_each_wlvif_ap(wl, wlvif) {
-                               if (!test_bit(h, wlvif->ap.sta_hlid_map))
-                                       continue;
-                               found = true;
-                               break;
-                       }
-                       if (!found)
-                               continue;
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
+                   !test_bit(wlvif->role_id , &roles_bitmap))
+                       continue;
 
-                       vif = wl12xx_wlvif_to_vif(wlvif);
-                       addr = wl->links[h].addr;
+               vif = wl12xx_wlvif_to_vif(wlvif);
 
-                       rcu_read_lock();
-                       sta = ieee80211_find_sta(vif, addr);
-                       if (sta) {
-                               wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
-                               ieee80211_report_low_ack(sta, num_packets);
-                       }
-                       rcu_read_unlock();
+               /* don't attempt roaming in case of p2p */
+               if (wlvif->p2p) {
+                       ieee80211_connection_loss(vif);
+                       continue;
                }
+
+               /*
+                * if the work is already queued, it should take place.
+                * We don't want to delay the connection loss
+                * indication any more.
+                */
+               ieee80211_queue_delayed_work(wl->hw,
+                                            &wlvif->connection_loss_work,
+                                            msecs_to_jiffies(delay));
+
+               ieee80211_cqm_rssi_notify(
+                               vif,
+                               NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
+                               GFP_KERNEL);
        }
-       return 0;
 }
+EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss);
 
 int wl1271_event_unmask(struct wl1271 *wl)
 {
@@ -305,12 +284,12 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
 
        /* first we read the mbox descriptor */
        ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox,
-                         sizeof(*wl->mbox), false);
+                         wl->mbox_size, false);
        if (ret < 0)
                return ret;
 
        /* process the descriptor */
-       ret = wl1271_event_process(wl);
+       ret = wl->ops->process_mailbox_events(wl);
        if (ret < 0)
                return ret;
 
index 8adf18d6c58f74c9bbdaa90474a83466ece2b50b..acc7a59d3828f1c40f62e09c7344b72d5942d9e3 100644 (file)
@@ -46,33 +46,17 @@ enum {
        RSSI_SNR_TRIGGER_5_EVENT_ID              = BIT(5),
        RSSI_SNR_TRIGGER_6_EVENT_ID              = BIT(6),
        RSSI_SNR_TRIGGER_7_EVENT_ID              = BIT(7),
-       MEASUREMENT_START_EVENT_ID               = BIT(8),
-       MEASUREMENT_COMPLETE_EVENT_ID            = BIT(9),
-       SCAN_COMPLETE_EVENT_ID                   = BIT(10),
-       WFD_DISCOVERY_COMPLETE_EVENT_ID          = BIT(11),
-       AP_DISCOVERY_COMPLETE_EVENT_ID           = BIT(12),
-       RESERVED1                                = BIT(13),
-       PSPOLL_DELIVERY_FAILURE_EVENT_ID         = BIT(14),
-       ROLE_STOP_COMPLETE_EVENT_ID              = BIT(15),
-       RADAR_DETECTED_EVENT_ID                  = BIT(16),
-       CHANNEL_SWITCH_COMPLETE_EVENT_ID         = BIT(17),
-       BSS_LOSE_EVENT_ID                        = BIT(18),
-       REGAINED_BSS_EVENT_ID                    = BIT(19),
-       MAX_TX_RETRY_EVENT_ID                    = BIT(20),
-       DUMMY_PACKET_EVENT_ID                    = BIT(21),
-       SOFT_GEMINI_SENSE_EVENT_ID               = BIT(22),
-       CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID        = BIT(23),
-       SOFT_GEMINI_AVALANCHE_EVENT_ID           = BIT(24),
-       PLT_RX_CALIBRATION_COMPLETE_EVENT_ID     = BIT(25),
-       INACTIVE_STA_EVENT_ID                    = BIT(26),
-       PEER_REMOVE_COMPLETE_EVENT_ID            = BIT(27),
-       PERIODIC_SCAN_COMPLETE_EVENT_ID          = BIT(28),
-       PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(29),
-       BA_SESSION_RX_CONSTRAINT_EVENT_ID        = BIT(30),
-       REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(31),
+
        EVENT_MBOX_ALL_EVENT_ID                  = 0x7fffffff,
 };
 
+/* events the driver might want to wait for */
+enum wlcore_wait_event {
+       WLCORE_EVENT_ROLE_STOP_COMPLETE,
+       WLCORE_EVENT_PEER_REMOVE_COMPLETE,
+       WLCORE_EVENT_DFS_CONFIG_COMPLETE
+};
+
 enum {
        EVENT_ENTER_POWER_SAVE_FAIL = 0,
        EVENT_ENTER_POWER_SAVE_SUCCESS,
@@ -80,61 +64,24 @@ enum {
 
 #define NUM_OF_RSSI_SNR_TRIGGERS 8
 
-struct event_mailbox {
-       __le32 events_vector;
-       __le32 events_mask;
-       __le32 reserved_1;
-       __le32 reserved_2;
-
-       u8 number_of_scan_results;
-       u8 scan_tag;
-       u8 completed_scan_status;
-       u8 reserved_3;
-
-       u8 soft_gemini_sense_info;
-       u8 soft_gemini_protective_info;
-       s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
-       u8 change_auto_mode_timeout;
-       u8 scheduled_scan_status;
-       u8 reserved4;
-       /* tuned channel (roc) */
-       u8 roc_channel;
-
-       __le16 hlid_removed_bitmap;
-
-       /* bitmap of aged stations (by HLID) */
-       __le16 sta_aging_status;
-
-       /* bitmap of stations (by HLID) which exceeded max tx retries */
-       __le16 sta_tx_retry_exceeded;
-
-       /* discovery completed results */
-       u8 discovery_tag;
-       u8 number_of_preq_results;
-       u8 number_of_prsp_results;
-       u8 reserved_5;
-
-       /* rx ba constraint */
-       u8 role_id; /* 0xFF means any role. */
-       u8 rx_ba_allowed;
-       u8 reserved_6[2];
-
-       /* Channel switch results */
-
-       u8 channel_switch_role_id;
-       u8 channel_switch_status;
-       u8 reserved_7[2];
-
-       u8 ps_poll_delivery_failure_role_ids;
-       u8 stopped_role_ids;
-       u8 started_role_ids;
-
-       u8 reserved_8[9];
-} __packed;
-
 struct wl1271;
 
 int wl1271_event_unmask(struct wl1271 *wl);
 int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
 
+void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable);
+void wlcore_event_sched_scan_completed(struct wl1271 *wl,
+                                      u8 status);
+void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
+                                  unsigned long roles_bitmap,
+                                  unsigned long allowed_bitmap);
+void wlcore_event_channel_switch(struct wl1271 *wl,
+                                unsigned long roles_bitmap,
+                                bool success);
+void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap);
+void wlcore_event_dummy_packet(struct wl1271 *wl);
+void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap);
+void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap);
+void wlcore_event_roc_complete(struct wl1271 *wl);
+void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr);
 #endif
index 2673d783ec1ecdda33d6dcd6f265f12dc6930c5e..7fd260c02a0a15aa2a0b8c92867a09fc9af96739 100644 (file)
@@ -201,4 +201,45 @@ wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len)
        return buf_offset;
 }
 
+static inline void
+wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                       struct ieee80211_sta *sta, u32 changed)
+{
+       if (wl->ops->sta_rc_update)
+               wl->ops->sta_rc_update(wl, wlvif, sta, changed);
+}
+
+static inline int
+wlcore_hw_set_peer_cap(struct wl1271 *wl,
+                      struct ieee80211_sta_ht_cap *ht_cap,
+                      bool allow_ht_operation,
+                      u32 rate_set, u8 hlid)
+{
+       if (wl->ops->set_peer_cap)
+               return wl->ops->set_peer_cap(wl, ht_cap, allow_ht_operation,
+                                            rate_set, hlid);
+
+       return 0;
+}
+
+static inline bool
+wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+                       struct wl1271_link *lnk)
+{
+       if (!wl->ops->lnk_high_prio)
+               BUG_ON(1);
+
+       return wl->ops->lnk_high_prio(wl, hlid, lnk);
+}
+
+static inline bool
+wlcore_hw_lnk_low_prio(struct wl1271 *wl, u8 hlid,
+                      struct wl1271_link *lnk)
+{
+       if (!wl->ops->lnk_low_prio)
+               BUG_ON(1);
+
+       return wl->ops->lnk_low_prio(wl, hlid, lnk);
+}
+
 #endif
index 32d157f62f3116f32dfc2b562aad770c0e9da907..5c6f11e157d9b0016633dfe932f34984fedeb65c 100644 (file)
@@ -41,14 +41,14 @@ int wl1271_init_templates_config(struct wl1271 *wl)
 
        /* send empty templates for fw memory reservation */
        ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                     CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL,
+                                     wl->scan_templ_id_2_4, NULL,
                                      WL1271_CMD_TEMPL_MAX_SIZE,
                                      0, WL1271_RATE_AUTOMATIC);
        if (ret < 0)
                return ret;
 
        ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                     CMD_TEMPL_CFG_PROBE_REQ_5,
+                                     wl->scan_templ_id_5,
                                      NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0,
                                      WL1271_RATE_AUTOMATIC);
        if (ret < 0)
@@ -56,14 +56,16 @@ int wl1271_init_templates_config(struct wl1271 *wl)
 
        if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) {
                ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                             CMD_TEMPL_APP_PROBE_REQ_2_4, NULL,
+                                             wl->sched_scan_templ_id_2_4,
+                                             NULL,
                                              WL1271_CMD_TEMPL_MAX_SIZE,
                                              0, WL1271_RATE_AUTOMATIC);
                if (ret < 0)
                        return ret;
 
                ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                             CMD_TEMPL_APP_PROBE_REQ_5, NULL,
+                                             wl->sched_scan_templ_id_5,
+                                             NULL,
                                              WL1271_CMD_TEMPL_MAX_SIZE,
                                              0, WL1271_RATE_AUTOMATIC);
                if (ret < 0)
@@ -463,7 +465,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
                supported_rates = CONF_TX_OFDM_RATES;
        else
-               supported_rates = CONF_TX_AP_ENABLED_RATES;
+               supported_rates = CONF_TX_ENABLED_RATES;
 
        /* unconditionally enable HT rates */
        supported_rates |= CONF_TX_MCS_RATES;
@@ -575,9 +577,6 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
                /* Configure for power according to debugfs */
                if (sta_auth != WL1271_PSM_ILLEGAL)
                        ret = wl1271_acx_sleep_auth(wl, sta_auth);
-               /* Configure for power always on */
-               else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
-                       ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
                /* Configure for ELP power saving */
                else
                        ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
@@ -679,6 +678,10 @@ int wl1271_hw_init(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
+       ret = wlcore_cmd_regdomain_config_locked(wl);
+       if (ret < 0)
+               return ret;
+
        /* Bluetooth WLAN coexistence */
        ret = wl1271_init_pta(wl);
        if (ret < 0)
index f48530fec14fb3ba9563e1763c2c38fa46b9488d..af7d9f9b3b4db2140006fb0639af3ff028af79e5 100644 (file)
@@ -105,13 +105,13 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr,
 {
        int ret;
 
-       ret = wlcore_raw_read(wl, addr, &wl->buffer_32,
-                             sizeof(wl->buffer_32), false);
+       ret = wlcore_raw_read(wl, addr, wl->buffer_32,
+                             sizeof(*wl->buffer_32), false);
        if (ret < 0)
                return ret;
 
        if (val)
-               *val = le32_to_cpu(wl->buffer_32);
+               *val = le32_to_cpu(*wl->buffer_32);
 
        return 0;
 }
@@ -119,9 +119,9 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr,
 static inline int __must_check wlcore_raw_write32(struct wl1271 *wl, int addr,
                                                  u32 val)
 {
-       wl->buffer_32 = cpu_to_le32(val);
-       return wlcore_raw_write(wl, addr, &wl->buffer_32,
-                               sizeof(wl->buffer_32), false);
+       *wl->buffer_32 = cpu_to_le32(val);
+       return wlcore_raw_write(wl, addr, wl->buffer_32,
+                               sizeof(*wl->buffer_32), false);
 }
 
 static inline int __must_check wlcore_read(struct wl1271 *wl, int addr,
index ea9d8e011bc9d45f5414b43cb6d1e3af492a89c3..44222de73983ccb53f77b31d2dc0eb5aadb89bff 100644 (file)
@@ -56,8 +56,8 @@
 #define WL1271_BOOT_RETRIES 3
 
 static char *fwlog_param;
-static bool bug_on_recovery;
-static bool no_recovery;
+static int bug_on_recovery = -1;
+static int no_recovery     = -1;
 
 static void __wl1271_op_remove_interface(struct wl1271 *wl,
                                         struct ieee80211_vif *vif,
@@ -79,22 +79,22 @@ static int wl12xx_set_authorized(struct wl1271 *wl,
        if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
                return 0;
 
-       ret = wl12xx_cmd_set_peer_state(wl, wlvif->sta.hlid);
+       ret = wl12xx_cmd_set_peer_state(wl, wlvif, wlvif->sta.hlid);
        if (ret < 0)
                return ret;
 
-       wl12xx_croc(wl, wlvif->role_id);
-
        wl1271_info("Association completed.");
        return 0;
 }
 
-static int wl1271_reg_notify(struct wiphy *wiphy,
-                            struct regulatory_request *request)
+static void wl1271_reg_notify(struct wiphy *wiphy,
+                             struct regulatory_request *request)
 {
        struct ieee80211_supported_band *band;
        struct ieee80211_channel *ch;
        int i;
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct wl1271 *wl = hw->priv;
 
        band = wiphy->bands[IEEE80211_BAND_5GHZ];
        for (i = 0; i < band->n_channels; i++) {
@@ -108,7 +108,7 @@ static int wl1271_reg_notify(struct wiphy *wiphy,
 
        }
 
-       return 0;
+       wlcore_regdomain_config(wl);
 }
 
 static int wl1271_set_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
@@ -303,6 +303,7 @@ out:
 static void wlcore_adjust_conf(struct wl1271 *wl)
 {
        /* Adjust settings according to optional module parameters */
+
        if (fwlog_param) {
                if (!strcmp(fwlog_param, "continuous")) {
                        wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
@@ -318,16 +319,21 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
                        wl1271_error("Unknown fwlog parameter %s", fwlog_param);
                }
        }
+
+       if (bug_on_recovery != -1)
+               wl->conf.recovery.bug_on_recovery = (u8) bug_on_recovery;
+
+       if (no_recovery != -1)
+               wl->conf.recovery.no_recovery = (u8) no_recovery;
 }
 
 static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
                                        struct wl12xx_vif *wlvif,
                                        u8 hlid, u8 tx_pkts)
 {
-       bool fw_ps, single_sta;
+       bool fw_ps;
 
        fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
-       single_sta = (wl->active_sta_count == 1);
 
        /*
         * Wake up from high level PS if the STA is asleep with too little
@@ -338,10 +344,15 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
 
        /*
         * Start high-level PS if the STA is asleep with enough blocks in FW.
-        * Make an exception if this is the only connected station. In this
-        * case FW-memory congestion is not a problem.
+        * Make an exception if this is the only connected link. In this
+        * case FW-memory congestion is less of a problem.
+        * Note that a single connected STA means 3 active links, since we must
+        * account for the global and broadcast AP links. The "fw_ps" check
+        * assures us the third link is a STA connected to the AP. Otherwise
+        * the FW would not set the PSM bit.
         */
-       else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
+       else if (wl->active_link_count > 3 && fw_ps &&
+                tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
@@ -349,11 +360,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                                           struct wl12xx_vif *wlvif,
                                           struct wl_fw_status_2 *status)
 {
-       struct wl1271_link *lnk;
        u32 cur_fw_ps_map;
-       u8 hlid, cnt;
-
-       /* TODO: also use link_fast_bitmap here */
+       u8 hlid;
 
        cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
        if (wl->ap_fw_ps_map != cur_fw_ps_map) {
@@ -365,17 +373,9 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                wl->ap_fw_ps_map = cur_fw_ps_map;
        }
 
-       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) {
-               lnk = &wl->links[hlid];
-               cnt = status->counters.tx_lnk_free_pkts[hlid] -
-                       lnk->prev_freed_pkts;
-
-               lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[hlid];
-               lnk->allocated_pkts -= cnt;
-
+       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS)
                wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
-                                           lnk->allocated_pkts);
-       }
+                                           wl->links[hlid].allocated_pkts);
 }
 
 static int wlcore_fw_status(struct wl1271 *wl,
@@ -389,6 +389,7 @@ static int wlcore_fw_status(struct wl1271 *wl,
        int i;
        size_t status_len;
        int ret;
+       struct wl1271_link *lnk;
 
        status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
                sizeof(*status_2) + wl->fw_status_priv_len;
@@ -414,6 +415,25 @@ static int wlcore_fw_status(struct wl1271 *wl,
                wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
        }
 
+
+       for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) {
+               u8 diff;
+               lnk = &wl->links[i];
+
+               /* prevent wrap-around in freed-packets counter */
+               diff = (status_2->counters.tx_lnk_free_pkts[i] -
+                      lnk->prev_freed_pkts) & 0xff;
+
+               if (diff == 0)
+                       continue;
+
+               lnk->allocated_pkts -= diff;
+               lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i];
+
+               /* accumulate the prev_freed_pkts counter */
+               lnk->total_freed_pkts += diff;
+       }
+
        /* prevent wrap-around in total blocks counter */
        if (likely(wl->tx_blocks_freed <=
                   le32_to_cpu(status_2->total_released_blks)))
@@ -466,6 +486,8 @@ static int wlcore_fw_status(struct wl1271 *wl,
        wl->time_offset = (timespec_to_ns(&ts) >> 10) -
                (s64)le32_to_cpu(status_2->fw_localtime);
 
+       wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap);
+
        return 0;
 }
 
@@ -629,6 +651,25 @@ static irqreturn_t wlcore_irq(int irq, void *cookie)
        unsigned long flags;
        struct wl1271 *wl = cookie;
 
+       /* complete the ELP completion */
+       spin_lock_irqsave(&wl->wl_lock, flags);
+       set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
+       if (wl->elp_compl) {
+               complete(wl->elp_compl);
+               wl->elp_compl = NULL;
+       }
+
+       if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
+               /* don't enqueue a work right now. mark it as pending */
+               set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
+               wl1271_debug(DEBUG_IRQ, "should not enqueue work");
+               disable_irq_nosync(wl->irq);
+               pm_wakeup_event(wl->dev, 0);
+               spin_unlock_irqrestore(&wl->wl_lock, flags);
+               return IRQ_HANDLED;
+       }
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
+
        /* TX might be handled here, avoid redundant work */
        set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
        cancel_work_sync(&wl->tx_work);
@@ -802,11 +843,13 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        /*
         * Make sure the chip is awake and the logger isn't active.
-        * Do not send a stop fwlog command if the fw is hanged.
+        * Do not send a stop fwlog command if the fw is hanged or if
+        * dbgpins are used (due to some fw bug).
         */
        if (wl1271_ps_elp_wakeup(wl))
                goto out;
-       if (!wl->watchdog_recovery)
+       if (!wl->watchdog_recovery &&
+           wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS)
                wl12xx_cmd_stop_fwlog(wl);
 
        /* Read the first memory block address */
@@ -874,7 +917,8 @@ static void wlcore_print_recovery(struct wl1271 *wl)
        if (ret < 0)
                return;
 
-       wl1271_info("pc: 0x%x, hint_sts: 0x%08x", pc, hint_sts);
+       wl1271_info("pc: 0x%x, hint_sts: 0x%08x count: %d",
+                               pc, hint_sts, ++wl->recovery_count);
 
        wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
 }
@@ -897,34 +941,17 @@ static void wl1271_recovery_work(struct work_struct *work)
                wlcore_print_recovery(wl);
        }
 
-       BUG_ON(bug_on_recovery &&
+       BUG_ON(wl->conf.recovery.bug_on_recovery &&
               !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
 
-       if (no_recovery) {
+       if (wl->conf.recovery.no_recovery) {
                wl1271_info("No recovery (chosen on module load). Fw will remain stuck.");
                goto out_unlock;
        }
 
-       /*
-        * Advance security sequence number to overcome potential progress
-        * in the firmware during recovery. This doens't hurt if the network is
-        * not encrypted.
-        */
-       wl12xx_for_each_wlvif(wl, wlvif) {
-               if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
-                   test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
-                       wlvif->tx_security_seq +=
-                               WL1271_TX_SQN_POST_RECOVERY_PADDING;
-       }
-
        /* Prevent spurious TX during FW restart */
        wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
 
-       if (wl->sched_scanning) {
-               ieee80211_sched_scan_stopped(wl->hw);
-               wl->sched_scanning = false;
-       }
-
        /* reboot the chipset */
        while (!list_empty(&wl->wlvif_list)) {
                wlvif = list_first_entry(&wl->wlvif_list,
@@ -1141,7 +1168,6 @@ int wl1271_plt_stop(struct wl1271 *wl)
        cancel_work_sync(&wl->recovery_work);
        cancel_delayed_work_sync(&wl->elp_work);
        cancel_delayed_work_sync(&wl->tx_watchdog_work);
-       cancel_delayed_work_sync(&wl->connection_loss_work);
 
        mutex_lock(&wl->mutex);
        wl1271_power_off(wl);
@@ -1169,9 +1195,13 @@ static void wl1271_op_tx(struct ieee80211_hw *hw,
        int q, mapping;
        u8 hlid;
 
-       if (vif)
-               wlvif = wl12xx_vif_to_data(vif);
+       if (!vif) {
+               wl1271_debug(DEBUG_TX, "DROP skb with no vif");
+               ieee80211_free_txskb(hw, skb);
+               return;
+       }
 
+       wlvif = wl12xx_vif_to_data(vif);
        mapping = skb_get_queue_mapping(skb);
        q = wl1271_tx_get_queue(mapping);
 
@@ -1185,9 +1215,9 @@ static void wl1271_op_tx(struct ieee80211_hw *hw,
         * allow these packets through.
         */
        if (hlid == WL12XX_INVALID_LINK_ID ||
-           (wlvif && !test_bit(hlid, wlvif->links_map)) ||
-            (wlcore_is_queue_stopped(wl, q) &&
-             !wlcore_is_queue_stopped_by_reason(wl, q,
+           (!test_bit(hlid, wlvif->links_map)) ||
+            (wlcore_is_queue_stopped_locked(wl, wlvif, q) &&
+             !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
                        WLCORE_QUEUE_STOP_REASON_WATERMARK))) {
                wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
                ieee80211_free_txskb(hw, skb);
@@ -1199,16 +1229,17 @@ static void wl1271_op_tx(struct ieee80211_hw *hw,
        skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
 
        wl->tx_queue_count[q]++;
+       wlvif->tx_queue_count[q]++;
 
        /*
         * The workqueue is slow to process the tx_queue and we need stop
         * the queue here, otherwise the queue will get too long.
         */
-       if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK &&
-           !wlcore_is_queue_stopped_by_reason(wl, q,
+       if (wlvif->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK &&
+           !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
                                        WLCORE_QUEUE_STOP_REASON_WATERMARK)) {
                wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
-               wlcore_stop_queue_locked(wl, q,
+               wlcore_stop_queue_locked(wl, wlvif, q,
                                         WLCORE_QUEUE_STOP_REASON_WATERMARK);
        }
 
@@ -1843,11 +1874,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        cancel_work_sync(&wl->tx_work);
        cancel_delayed_work_sync(&wl->elp_work);
        cancel_delayed_work_sync(&wl->tx_watchdog_work);
-       cancel_delayed_work_sync(&wl->connection_loss_work);
 
        /* let's notify MAC80211 about the remaining pending TX frames */
-       wl12xx_tx_reset(wl);
        mutex_lock(&wl->mutex);
+       wl12xx_tx_reset(wl);
 
        wl1271_power_off(wl);
        /*
@@ -1870,14 +1900,17 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        wl->time_offset = 0;
        wl->ap_fw_ps_map = 0;
        wl->ap_ps_map = 0;
-       wl->sched_scanning = false;
        wl->sleep_auth = WL1271_PSM_ILLEGAL;
        memset(wl->roles_map, 0, sizeof(wl->roles_map));
        memset(wl->links_map, 0, sizeof(wl->links_map));
        memset(wl->roc_map, 0, sizeof(wl->roc_map));
+       memset(wl->session_ids, 0, sizeof(wl->session_ids));
        wl->active_sta_count = 0;
+       wl->active_link_count = 0;
 
        /* The system link is always allocated */
+       wl->links[WL12XX_SYSTEM_HLID].allocated_pkts = 0;
+       wl->links[WL12XX_SYSTEM_HLID].prev_freed_pkts = 0;
        __set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
 
        /*
@@ -1903,6 +1936,12 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        wl->tx_res_if = NULL;
        kfree(wl->target_mem_map);
        wl->target_mem_map = NULL;
+
+       /*
+        * FW channels must be re-calibrated after recovery,
+        * clear the last Reg-Domain channel configuration.
+        */
+       memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last));
 }
 
 static void wlcore_op_stop(struct ieee80211_hw *hw)
@@ -1918,6 +1957,71 @@ static void wlcore_op_stop(struct ieee80211_hw *hw)
        mutex_unlock(&wl->mutex);
 }
 
+static void wlcore_channel_switch_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       struct ieee80211_vif *vif;
+       struct wl12xx_vif *wlvif;
+       int ret;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wlvif = container_of(dwork, struct wl12xx_vif, channel_switch_work);
+       wl = wlvif->wl;
+
+       wl1271_info("channel switch failed (role_id: %d).", wlvif->role_id);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /* check the channel switch is still ongoing */
+       if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags))
+               goto out;
+
+       vif = wl12xx_wlvif_to_vif(wlvif);
+       ieee80211_chswitch_done(vif, false);
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       wl12xx_cmd_stop_channel_switch(wl, wlvif);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+static void wlcore_connection_loss_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       struct ieee80211_vif *vif;
+       struct wl12xx_vif *wlvif;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wlvif = container_of(dwork, struct wl12xx_vif, connection_loss_work);
+       wl = wlvif->wl;
+
+       wl1271_info("Connection loss work (role_id: %d).", wlvif->role_id);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /* Call mac80211 connection loss */
+       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+               goto out;
+
+       vif = wl12xx_wlvif_to_vif(wlvif);
+       ieee80211_connection_loss(vif);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
 {
        u8 policy = find_first_zero_bit(wl->rate_policies_map,
@@ -2037,15 +2141,15 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
                for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
                        wl12xx_allocate_rate_policy(wl,
                                                &wlvif->ap.ucast_rate_idx[i]);
-               wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES;
+               wlvif->basic_rate_set = CONF_TX_ENABLED_RATES;
                /*
                 * TODO: check if basic_rate shouldn't be
                 * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
                 * instead (the same thing for STA above).
                */
-               wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES;
+               wlvif->basic_rate = CONF_TX_ENABLED_RATES;
                /* TODO: this seems to be used only for STA, check it */
-               wlvif->rate_set = CONF_TX_AP_ENABLED_RATES;
+               wlvif->rate_set = CONF_TX_ENABLED_RATES;
        }
 
        wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
@@ -2065,6 +2169,10 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
                  wl1271_rx_streaming_enable_work);
        INIT_WORK(&wlvif->rx_streaming_disable_work,
                  wl1271_rx_streaming_disable_work);
+       INIT_DELAYED_WORK(&wlvif->channel_switch_work,
+                         wlcore_channel_switch_work);
+       INIT_DELAYED_WORK(&wlvif->connection_loss_work,
+                         wlcore_connection_loss_work);
        INIT_LIST_HEAD(&wlvif->list);
 
        setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
@@ -2072,7 +2180,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
        return 0;
 }
 
-static bool wl12xx_init_fw(struct wl1271 *wl)
+static int wl12xx_init_fw(struct wl1271 *wl)
 {
        int retries = WL1271_BOOT_RETRIES;
        bool booted = false;
@@ -2138,7 +2246,7 @@ power_off:
 
        wl->state = WLCORE_STATE_ON;
 out:
-       return booted;
+       return ret;
 }
 
 static bool wl12xx_dev_role_started(struct wl12xx_vif *wlvif)
@@ -2198,6 +2306,81 @@ static void wl12xx_force_active_psm(struct wl1271 *wl)
        }
 }
 
+struct wlcore_hw_queue_iter_data {
+       unsigned long hw_queue_map[BITS_TO_LONGS(WLCORE_NUM_MAC_ADDRESSES)];
+       /* current vif */
+       struct ieee80211_vif *vif;
+       /* is the current vif among those iterated */
+       bool cur_running;
+};
+
+static void wlcore_hw_queue_iter(void *data, u8 *mac,
+                                struct ieee80211_vif *vif)
+{
+       struct wlcore_hw_queue_iter_data *iter_data = data;
+
+       if (WARN_ON_ONCE(vif->hw_queue[0] == IEEE80211_INVAL_HW_QUEUE))
+               return;
+
+       if (iter_data->cur_running || vif == iter_data->vif) {
+               iter_data->cur_running = true;
+               return;
+       }
+
+       __set_bit(vif->hw_queue[0] / NUM_TX_QUEUES, iter_data->hw_queue_map);
+}
+
+static int wlcore_allocate_hw_queue_base(struct wl1271 *wl,
+                                        struct wl12xx_vif *wlvif)
+{
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct wlcore_hw_queue_iter_data iter_data = {};
+       int i, q_base;
+
+       iter_data.vif = vif;
+
+       /* mark all bits taken by active interfaces */
+       ieee80211_iterate_active_interfaces_atomic(wl->hw,
+                                       IEEE80211_IFACE_ITER_RESUME_ALL,
+                                       wlcore_hw_queue_iter, &iter_data);
+
+       /* the current vif is already running in mac80211 (resume/recovery) */
+       if (iter_data.cur_running) {
+               wlvif->hw_queue_base = vif->hw_queue[0];
+               wl1271_debug(DEBUG_MAC80211,
+                            "using pre-allocated hw queue base %d",
+                            wlvif->hw_queue_base);
+
+               /* interface type might have changed type */
+               goto adjust_cab_queue;
+       }
+
+       q_base = find_first_zero_bit(iter_data.hw_queue_map,
+                                    WLCORE_NUM_MAC_ADDRESSES);
+       if (q_base >= WLCORE_NUM_MAC_ADDRESSES)
+               return -EBUSY;
+
+       wlvif->hw_queue_base = q_base * NUM_TX_QUEUES;
+       wl1271_debug(DEBUG_MAC80211, "allocating hw queue base: %d",
+                    wlvif->hw_queue_base);
+
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
+               wl->queue_stop_reasons[wlvif->hw_queue_base + i] = 0;
+               /* register hw queues in mac80211 */
+               vif->hw_queue[i] = wlvif->hw_queue_base + i;
+       }
+
+adjust_cab_queue:
+       /* the last places are reserved for cab queues per interface */
+       if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               vif->cab_queue = NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES +
+                                wlvif->hw_queue_base / NUM_TX_QUEUES;
+       else
+               vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+
+       return 0;
+}
+
 static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif)
 {
@@ -2206,7 +2389,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
        struct vif_counter_data vif_count;
        int ret = 0;
        u8 role_type;
-       bool booted = false;
 
        vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
                             IEEE80211_VIF_SUPPORTS_CQM_RSSI;
@@ -2244,6 +2426,10 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
+       ret = wlcore_allocate_hw_queue_base(wl, wlvif);
+       if (ret < 0)
+               goto out;
+
        if (wl12xx_need_fw_change(wl, vif_count, true)) {
                wl12xx_force_active_psm(wl);
                set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
@@ -2263,11 +2449,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                 */
                memcpy(wl->addresses[0].addr, vif->addr, ETH_ALEN);
 
-               booted = wl12xx_init_fw(wl);
-               if (!booted) {
-                       ret = -EINVAL;
+               ret = wl12xx_init_fw(wl);
+               if (ret < 0)
                        goto out;
-               }
        }
 
        ret = wl12xx_cmd_role_enable(wl, vif->addr,
@@ -2314,7 +2498,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
        wl1271_info("down");
 
        if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
-           wl->scan_vif == vif) {
+           wl->scan_wlvif == wlvif) {
                /*
                 * Rearm the tx watchdog just before idling scan. This
                 * prevents just-finished scans from triggering the watchdog
@@ -2323,11 +2507,21 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
 
                wl->scan.state = WL1271_SCAN_STATE_IDLE;
                memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
-               wl->scan_vif = NULL;
+               wl->scan_wlvif = NULL;
                wl->scan.req = NULL;
                ieee80211_scan_completed(wl->hw, true);
        }
 
+       if (wl->sched_vif == wlvif) {
+               ieee80211_sched_scan_stopped(wl->hw);
+               wl->sched_vif = NULL;
+       }
+
+       if (wl->roc_vif == vif) {
+               wl->roc_vif = NULL;
+               ieee80211_remain_on_channel_expired(wl->hw);
+       }
+
        if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
                /* disable active roles */
                ret = wl1271_ps_elp_wakeup(wl);
@@ -2347,6 +2541,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
                wl1271_ps_elp_sleep(wl);
        }
 deinit:
+       wl12xx_tx_reset_wlvif(wl, wlvif);
+
        /* clear all hlids (except system_hlid) */
        wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
 
@@ -2370,7 +2566,6 @@ deinit:
 
        dev_kfree_skb(wlvif->probereq);
        wlvif->probereq = NULL;
-       wl12xx_tx_reset_wlvif(wl, wlvif);
        if (wl->last_wlvif == wlvif)
                wl->last_wlvif = NULL;
        list_del(&wlvif->list);
@@ -2396,9 +2591,6 @@ deinit:
                /* Configure for power according to debugfs */
                if (sta_auth != WL1271_PSM_ILLEGAL)
                        wl1271_acx_sleep_auth(wl, sta_auth);
-               /* Configure for power always on */
-               else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
-                       wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
                /* Configure for ELP power saving */
                else
                        wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
@@ -2410,6 +2602,7 @@ unlock:
        del_timer_sync(&wlvif->rx_streaming_timer);
        cancel_work_sync(&wlvif->rx_streaming_enable_work);
        cancel_work_sync(&wlvif->rx_streaming_disable_work);
+       cancel_delayed_work_sync(&wlvif->connection_loss_work);
 
        mutex_lock(&wl->mutex);
 }
@@ -2468,8 +2661,7 @@ static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
        return ret;
 }
 
-static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                         bool set_assoc)
+static int wlcore_join(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
        bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
@@ -2489,18 +2681,111 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        /* clear encryption type */
        wlvif->encryption_type = KEY_NONE;
 
-       if (set_assoc)
-               set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
-
        if (is_ibss)
                ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
-       else
+       else {
+               if (wl->quirks & WLCORE_QUIRK_START_STA_FAILS) {
+                       /*
+                        * TODO: this is an ugly workaround for wl12xx fw
+                        * bug - we are not able to tx/rx after the first
+                        * start_sta, so make dummy start+stop calls,
+                        * and then call start_sta again.
+                        * this should be fixed in the fw.
+                        */
+                       wl12xx_cmd_role_start_sta(wl, wlvif);
+                       wl12xx_cmd_role_stop_sta(wl, wlvif);
+               }
+
                ret = wl12xx_cmd_role_start_sta(wl, wlvif);
+       }
+
+       return ret;
+}
+
+static int wl1271_ssid_set(struct wl12xx_vif *wlvif, struct sk_buff *skb,
+                           int offset)
+{
+       u8 ssid_len;
+       const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
+                                        skb->len - offset);
+
+       if (!ptr) {
+               wl1271_error("No SSID in IEs!");
+               return -ENOENT;
+       }
+
+       ssid_len = ptr[1];
+       if (ssid_len > IEEE80211_MAX_SSID_LEN) {
+               wl1271_error("SSID is too long!");
+               return -EINVAL;
+       }
+
+       wlvif->ssid_len = ssid_len;
+       memcpy(wlvif->ssid, ptr+2, ssid_len);
+       return 0;
+}
+
+static int wlcore_set_ssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct sk_buff *skb;
+       int ieoffset;
+
+       /* we currently only support setting the ssid from the ap probe req */
+       if (wlvif->bss_type != BSS_TYPE_STA_BSS)
+               return -EINVAL;
+
+       skb = ieee80211_ap_probereq_get(wl->hw, vif);
+       if (!skb)
+               return -EINVAL;
+
+       ieoffset = offsetof(struct ieee80211_mgmt,
+                           u.probe_req.variable);
+       wl1271_ssid_set(wlvif, skb, ieoffset);
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct ieee80211_bss_conf *bss_conf,
+                           u32 sta_rate_set)
+{
+       int ieoffset;
+       int ret;
+
+       wlvif->aid = bss_conf->aid;
+       wlvif->channel_type = cfg80211_get_chandef_type(&bss_conf->chandef);
+       wlvif->beacon_int = bss_conf->beacon_int;
+       wlvif->wmm_enabled = bss_conf->qos;
+
+       set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
+
+       /*
+        * with wl1271, we don't need to update the
+        * beacon_int and dtim_period, because the firmware
+        * updates it by itself when the first beacon is
+        * received after a join.
+        */
+       ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
        if (ret < 0)
-               goto out;
+               return ret;
 
-       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
-               goto out;
+       /*
+        * Get a template for hardware connection maintenance
+        */
+       dev_kfree_skb(wlvif->probereq);
+       wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
+                                                       wlvif,
+                                                       NULL);
+       ieoffset = offsetof(struct ieee80211_mgmt,
+                           u.probe_req.variable);
+       wl1271_ssid_set(wlvif, wlvif->probereq, ieoffset);
+
+       /* enable the connection monitoring feature */
+       ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
+       if (ret < 0)
+               return ret;
 
        /*
         * The join command disable the keep-alive mode, shut down its process,
@@ -2510,35 +2795,83 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
         */
        ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wl1271_acx_keep_alive_config(wl, wlvif,
                                           wlvif->sta.klv_template_id,
                                           ACX_KEEP_ALIVE_TPL_VALID);
        if (ret < 0)
-               goto out;
+               return ret;
+
+       /*
+        * The default fw psm configuration is AUTO, while mac80211 default
+        * setting is off (ACTIVE), so sync the fw with the correct value.
+        */
+       ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE);
+       if (ret < 0)
+               return ret;
+
+       if (sta_rate_set) {
+               wlvif->rate_set =
+                       wl1271_tx_enabled_rates_get(wl,
+                                                   sta_rate_set,
+                                                   wlvif->band);
+               ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+               if (ret < 0)
+                       return ret;
+       }
 
-out:
        return ret;
 }
 
-static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
+       bool sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
+
+       /* make sure we are connected (sta) joined */
+       if (sta &&
+           !test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+               return false;
+
+       /* make sure we are joined (ibss) */
+       if (!sta &&
+           test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))
+               return false;
+
+       if (sta) {
+               /* use defaults when not associated */
+               wlvif->aid = 0;
+
+               /* free probe-request template */
+               dev_kfree_skb(wlvif->probereq);
+               wlvif->probereq = NULL;
+
+               /* disable connection monitor features */
+               ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
+               if (ret < 0)
+                       return ret;
+
+               /* Disable the keep-alive feature */
+               ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
+               if (ret < 0)
+                       return ret;
+       }
 
        if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
                struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 
-               wl12xx_cmd_stop_channel_switch(wl);
+               wl12xx_cmd_stop_channel_switch(wl, wlvif);
                ieee80211_chswitch_done(vif, false);
+               cancel_delayed_work(&wlvif->channel_switch_work);
        }
 
        /* invalidate keep-alive template */
@@ -2546,17 +2879,7 @@ static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                                     wlvif->sta.klv_template_id,
                                     ACX_KEEP_ALIVE_TPL_INVALID);
 
-       /* to stop listening to a channel, we disconnect */
-       ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
-       if (ret < 0)
-               goto out;
-
-       /* reset TX security counters on a clean disconnect */
-       wlvif->tx_security_last_seq_lsb = 0;
-       wlvif->tx_security_seq = 0;
-
-out:
-       return ret;
+       return 0;
 }
 
 static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
@@ -2565,147 +2888,10 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        wlvif->rate_set = wlvif->basic_rate_set;
 }
 
-static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                                 bool idle)
-{
-       int ret;
-       bool cur_idle = !test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
-
-       if (idle == cur_idle)
-               return 0;
-
-       if (idle) {
-               /* no need to croc if we weren't busy (e.g. during boot) */
-               if (wl12xx_dev_role_started(wlvif)) {
-                       ret = wl12xx_stop_dev(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-               }
-               wlvif->rate_set =
-                       wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
-               ret = wl1271_acx_sta_rate_policies(wl, wlvif);
-               if (ret < 0)
-                       goto out;
-               clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
-       } else {
-               /* The current firmware only supports sched_scan in idle */
-               if (wl->sched_scanning) {
-                       wl1271_scan_sched_scan_stop(wl, wlvif);
-                       ieee80211_sched_scan_stopped(wl->hw);
-               }
-
-               ret = wl12xx_start_dev(wl, wlvif);
-               if (ret < 0)
-                       goto out;
-               set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
-       }
-
-out:
-       return ret;
-}
-
 static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                             struct ieee80211_conf *conf, u32 changed)
 {
-       bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
-       int channel, ret;
-
-       channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
-
-       /* if the channel changes while joined, join again */
-       if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
-           ((wlvif->band != conf->channel->band) ||
-            (wlvif->channel != channel) ||
-            (wlvif->channel_type != conf->channel_type))) {
-               /* send all pending packets */
-               ret = wlcore_tx_work_locked(wl);
-               if (ret < 0)
-                       return ret;
-
-               wlvif->band = conf->channel->band;
-               wlvif->channel = channel;
-               wlvif->channel_type = conf->channel_type;
-
-               if (is_ap) {
-                       wl1271_set_band_rate(wl, wlvif);
-                       ret = wl1271_init_ap_rates(wl, wlvif);
-                       if (ret < 0)
-                               wl1271_error("AP rate policy change failed %d",
-                                            ret);
-               } else {
-                       /*
-                        * FIXME: the mac80211 should really provide a fixed
-                        * rate to use here. for now, just use the smallest
-                        * possible rate for the band as a fixed rate for
-                        * association frames and other control messages.
-                        */
-                       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
-                               wl1271_set_band_rate(wl, wlvif);
-
-                       wlvif->basic_rate =
-                               wl1271_tx_min_rate_get(wl,
-                                                      wlvif->basic_rate_set);
-                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
-                       if (ret < 0)
-                               wl1271_warning("rate policy for channel "
-                                              "failed %d", ret);
-
-                       /*
-                        * change the ROC channel. do it only if we are
-                        * not idle. otherwise, CROC will be called
-                        * anyway.
-                        */
-                       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED,
-                                     &wlvif->flags) &&
-                           wl12xx_dev_role_started(wlvif) &&
-                           !(conf->flags & IEEE80211_CONF_IDLE)) {
-                               ret = wl12xx_stop_dev(wl, wlvif);
-                               if (ret < 0)
-                                       return ret;
-
-                               ret = wl12xx_start_dev(wl, wlvif);
-                               if (ret < 0)
-                                       return ret;
-                       }
-               }
-       }
-
-       if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) {
-
-               if ((conf->flags & IEEE80211_CONF_PS) &&
-                   test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
-                   !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
-
-                       int ps_mode;
-                       char *ps_mode_str;
-
-                       if (wl->conf.conn.forced_ps) {
-                               ps_mode = STATION_POWER_SAVE_MODE;
-                               ps_mode_str = "forced";
-                       } else {
-                               ps_mode = STATION_AUTO_PS_MODE;
-                               ps_mode_str = "auto";
-                       }
-
-                       wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str);
-
-                       ret = wl1271_ps_set_mode(wl, wlvif, ps_mode);
-
-                       if (ret < 0)
-                               wl1271_warning("enter %s ps failed %d",
-                                              ps_mode_str, ret);
-
-               } else if (!(conf->flags & IEEE80211_CONF_PS) &&
-                          test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
-
-                       wl1271_debug(DEBUG_PSM, "auto ps disabled");
-
-                       ret = wl1271_ps_set_mode(wl, wlvif,
-                                                STATION_ACTIVE_MODE);
-                       if (ret < 0)
-                               wl1271_warning("exit auto ps failed %d", ret);
-               }
-       }
+       int ret;
 
        if (conf->power_level != wlvif->power_level) {
                ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
@@ -2723,37 +2909,17 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
        struct wl1271 *wl = hw->priv;
        struct wl12xx_vif *wlvif;
        struct ieee80211_conf *conf = &hw->conf;
-       int channel, ret = 0;
-
-       channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
+       int ret = 0;
 
-       wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
+       wl1271_debug(DEBUG_MAC80211, "mac80211 config psm %s power %d %s"
                     " changed 0x%x",
-                    channel,
                     conf->flags & IEEE80211_CONF_PS ? "on" : "off",
                     conf->power_level,
                     conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
                         changed);
 
-       /*
-        * mac80211 will go to idle nearly immediately after transmitting some
-        * frames, such as the deauth. To make sure those frames reach the air,
-        * wait here until the TX queue is fully flushed.
-        */
-       if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
-           ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
-            (conf->flags & IEEE80211_CONF_IDLE)))
-               wl1271_tx_flush(wl);
-
        mutex_lock(&wl->mutex);
 
-       /* we support configuring the channel and band even while off */
-       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-               wl->band = conf->channel->band;
-               wl->channel = channel;
-               wl->channel_type = conf->channel_type;
-       }
-
        if (changed & IEEE80211_CONF_CHANGE_POWER)
                wl->power_level = conf->power_level;
 
@@ -3073,10 +3239,7 @@ static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                 * stop the queues and flush to ensure the next packets are
                 * in sync with FW spare block accounting
                 */
-               mutex_lock(&wl->mutex);
                wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
-               mutex_unlock(&wl->mutex);
-
                wl1271_tx_flush(wl);
        }
 
@@ -3114,6 +3277,7 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
        u32 tx_seq_32 = 0;
        u16 tx_seq_16 = 0;
        u8 key_type;
+       u8 hlid;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
 
@@ -3123,6 +3287,22 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                     key_conf->keylen, key_conf->flags);
        wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
 
+       if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               if (sta) {
+                       struct wl1271_station *wl_sta = (void *)sta->drv_priv;
+                       hlid = wl_sta->hlid;
+               } else {
+                       hlid = wlvif->ap.bcast_hlid;
+               }
+       else
+               hlid = wlvif->sta.hlid;
+
+       if (hlid != WL12XX_INVALID_LINK_ID) {
+               u64 tx_seq = wl->links[hlid].total_freed_pkts;
+               tx_seq_32 = WL1271_TX_SECURITY_HI32(tx_seq);
+               tx_seq_16 = WL1271_TX_SECURITY_LO16(tx_seq);
+       }
+
        switch (key_conf->cipher) {
        case WLAN_CIPHER_SUITE_WEP40:
        case WLAN_CIPHER_SUITE_WEP104:
@@ -3132,22 +3312,14 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                break;
        case WLAN_CIPHER_SUITE_TKIP:
                key_type = KEY_TKIP;
-
                key_conf->hw_key_idx = key_conf->keyidx;
-               tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
-               tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
                key_type = KEY_AES;
-
                key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
-               tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
-               tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
                break;
        case WL1271_CIPHER_SUITE_GEM:
                key_type = KEY_GEM;
-               tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
-               tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
                break;
        default:
                wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
@@ -3202,6 +3374,33 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
 }
 EXPORT_SYMBOL_GPL(wlcore_set_key);
 
+void wlcore_regdomain_config(struct wl1271 *wl)
+{
+       int ret;
+
+       if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+               return;
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wlcore_cmd_regdomain_config_locked(wl);
+       if (ret < 0) {
+               wl12xx_queue_recovery_work(wl);
+               goto out;
+       }
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
                             struct cfg80211_scan_request *req)
@@ -3241,7 +3440,7 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
                goto out_sleep;
        }
 
-       ret = wl1271_scan(hw->priv, vif, ssid, len, req);
+       ret = wlcore_scan(hw->priv, vif, ssid, len, req);
 out_sleep:
        wl1271_ps_elp_sleep(wl);
 out:
@@ -3254,6 +3453,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan");
@@ -3271,7 +3471,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
                goto out;
 
        if (wl->scan.state != WL1271_SCAN_STATE_DONE) {
-               ret = wl1271_scan_stop(wl);
+               ret = wl->ops->scan_stop(wl, wlvif);
                if (ret < 0)
                        goto out_sleep;
        }
@@ -3284,7 +3484,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
 
        wl->scan.state = WL1271_SCAN_STATE_IDLE;
        memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
-       wl->scan_vif = NULL;
+       wl->scan_wlvif = NULL;
        wl->scan.req = NULL;
        ieee80211_scan_completed(wl->hw, true);
 
@@ -3318,15 +3518,11 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
-       if (ret < 0)
-               goto out_sleep;
-
-       ret = wl1271_scan_sched_scan_start(wl, wlvif);
+       ret = wl->ops->sched_scan_start(wl, wlvif, req, ies);
        if (ret < 0)
                goto out_sleep;
 
-       wl->sched_scanning = true;
+       wl->sched_vif = wlvif;
 
 out_sleep:
        wl1271_ps_elp_sleep(wl);
@@ -3353,7 +3549,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       wl1271_scan_sched_scan_stop(wl, wlvif);
+       wl->ops->sched_scan_stop(wl, wlvif);
 
        wl1271_ps_elp_sleep(wl);
 out:
@@ -3418,30 +3614,6 @@ out:
        return ret;
 }
 
-static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb,
-                           int offset)
-{
-       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       u8 ssid_len;
-       const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
-                                        skb->len - offset);
-
-       if (!ptr) {
-               wl1271_error("No SSID in IEs!");
-               return -ENOENT;
-       }
-
-       ssid_len = ptr[1];
-       if (ssid_len > IEEE80211_MAX_SSID_LEN) {
-               wl1271_error("SSID is too long!");
-               return -EINVAL;
-       }
-
-       wlvif->ssid_len = ssid_len;
-       memcpy(wlvif->ssid, ptr+2, ssid_len);
-       return 0;
-}
-
 static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset)
 {
        int len;
@@ -3622,7 +3794,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
 
        wl1271_debug(DEBUG_MASTER, "beacon updated");
 
-       ret = wl1271_ssid_set(vif, beacon, ieoffset);
+       ret = wl1271_ssid_set(wlvif, beacon, ieoffset);
        if (ret < 0) {
                dev_kfree_skb(beacon);
                goto out;
@@ -3639,6 +3811,12 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
                goto out;
        }
 
+       wlvif->wmm_enabled =
+               cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+                                       WLAN_OUI_TYPE_MICROSOFT_WMM,
+                                       beacon->data + ieoffset,
+                                       beacon->len - ieoffset);
+
        /*
         * In case we already have a probe-resp beacon set explicitly
         * by usermode, don't use the beacon data.
@@ -3692,7 +3870,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
        bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
        int ret = 0;
 
-       if ((changed & BSS_CHANGED_BEACON_INT)) {
+       if (changed & BSS_CHANGED_BEACON_INT) {
                wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
                        bss_conf->beacon_int);
 
@@ -3705,7 +3883,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
                wl1271_ap_set_probe_resp_tmpl(wl, rate, vif);
        }
 
-       if ((changed & BSS_CHANGED_BEACON)) {
+       if (changed & BSS_CHANGED_BEACON) {
                ret = wlcore_set_beacon_template(wl, vif, is_ap);
                if (ret < 0)
                        goto out;
@@ -3726,7 +3904,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret = 0;
 
-       if ((changed & BSS_CHANGED_BASIC_RATES)) {
+       if (changed & BSS_CHANGED_BASIC_RATES) {
                u32 rates = bss_conf->basic_rates;
 
                wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
@@ -3757,7 +3935,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
        if (ret < 0)
                goto out;
 
-       if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
+       if (changed & BSS_CHANGED_BEACON_ENABLED) {
                if (bss_conf->enable_beacon) {
                        if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
                                ret = wl12xx_cmd_role_start_ap(wl, wlvif);
@@ -3804,6 +3982,79 @@ out:
        return;
 }
 
+static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct ieee80211_bss_conf *bss_conf,
+                           u32 sta_rate_set)
+{
+       u32 rates;
+       int ret;
+
+       wl1271_debug(DEBUG_MAC80211,
+            "changed_bssid: %pM, aid: %d, bcn_int: %d, brates: 0x%x sta_rate_set: 0x%x",
+            bss_conf->bssid, bss_conf->aid,
+            bss_conf->beacon_int,
+            bss_conf->basic_rates, sta_rate_set);
+
+       wlvif->beacon_int = bss_conf->beacon_int;
+       rates = bss_conf->basic_rates;
+       wlvif->basic_rate_set =
+               wl1271_tx_enabled_rates_get(wl, rates,
+                                           wlvif->band);
+       wlvif->basic_rate =
+               wl1271_tx_min_rate_get(wl,
+                                      wlvif->basic_rate_set);
+
+       if (sta_rate_set)
+               wlvif->rate_set =
+                       wl1271_tx_enabled_rates_get(wl,
+                                               sta_rate_set,
+                                               wlvif->band);
+
+       /* we only support sched_scan while not connected */
+       if (wl->sched_vif == wlvif)
+               wl->ops->sched_scan_stop(wl, wlvif);
+
+       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       ret = wl12xx_cmd_build_null_data(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       ret = wl1271_build_qos_null_data(wl, wl12xx_wlvif_to_vif(wlvif));
+       if (ret < 0)
+               return ret;
+
+       wlcore_set_ssid(wl, wlvif);
+
+       set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+
+       return 0;
+}
+
+static int wlcore_clear_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       int ret;
+
+       /* revert back to minimum rates for the current band */
+       wl1271_set_band_rate(wl, wlvif);
+       wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
+
+       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+           test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) {
+               ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
+               if (ret < 0)
+                       return ret;
+       }
+
+       clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+       return 0;
+}
 /* STA/IBSS mode changes */
 static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                                        struct ieee80211_vif *vif,
@@ -3811,7 +4062,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                                        u32 changed)
 {
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       bool do_join = false, set_assoc = false;
+       bool do_join = false;
        bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
        bool ibss_joined = false;
        u32 sta_rate_set = 0;
@@ -3832,9 +4083,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                        set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
                        ibss_joined = true;
                } else {
-                       if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED,
-                                              &wlvif->flags))
-                               wl1271_unjoin(wl, wlvif);
+                       wlcore_unset_assoc(wl, wlvif);
+                       wl12xx_cmd_role_stop_sta(wl, wlvif);
                }
        }
 
@@ -3852,13 +4102,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                do_join = true;
        }
 
-       if (changed & BSS_CHANGED_IDLE && !is_ibss) {
-               ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle);
-               if (ret < 0)
-                       wl1271_warning("idle mode change failed %d", ret);
-       }
-
-       if ((changed & BSS_CHANGED_CQM)) {
+       if (changed & BSS_CHANGED_CQM) {
                bool enable = false;
                if (bss_conf->cqm_rssi_thold)
                        enable = true;
@@ -3870,150 +4114,39 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
        }
 
-       if (changed & BSS_CHANGED_BSSID)
-               if (!is_zero_ether_addr(bss_conf->bssid)) {
-                       ret = wl12xx_cmd_build_null_data(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-
-                       ret = wl1271_build_qos_null_data(wl, vif);
-                       if (ret < 0)
-                               goto out;
-               }
-
-       if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) {
+       if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT |
+                      BSS_CHANGED_ASSOC)) {
                rcu_read_lock();
                sta = ieee80211_find_sta(vif, bss_conf->bssid);
-               if (!sta)
-                       goto sta_not_found;
-
-               /* save the supp_rates of the ap */
-               sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
-               if (sta->ht_cap.ht_supported)
-                       sta_rate_set |=
-                         (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
-                         (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
-               sta_ht_cap = sta->ht_cap;
-               sta_exists = true;
-
-sta_not_found:
+               if (sta) {
+                       u8 *rx_mask = sta->ht_cap.mcs.rx_mask;
+
+                       /* save the supp_rates of the ap */
+                       sta_rate_set = sta->supp_rates[wlvif->band];
+                       if (sta->ht_cap.ht_supported)
+                               sta_rate_set |=
+                                       (rx_mask[0] << HW_HT_RATES_OFFSET) |
+                                       (rx_mask[1] << HW_MIMO_RATES_OFFSET);
+                       sta_ht_cap = sta->ht_cap;
+                       sta_exists = true;
+               }
+
                rcu_read_unlock();
        }
 
-       if ((changed & BSS_CHANGED_ASSOC)) {
-               if (bss_conf->assoc) {
-                       u32 rates;
-                       int ieoffset;
-                       wlvif->aid = bss_conf->aid;
-                       wlvif->channel_type =
-                               cfg80211_get_chandef_type(&bss_conf->chandef);
-                       wlvif->beacon_int = bss_conf->beacon_int;
-                       do_join = true;
-                       set_assoc = true;
-
-                       /*
-                        * use basic rates from AP, and determine lowest rate
-                        * to use with control frames.
-                        */
-                       rates = bss_conf->basic_rates;
-                       wlvif->basic_rate_set =
-                               wl1271_tx_enabled_rates_get(wl, rates,
-                                                           wlvif->band);
-                       wlvif->basic_rate =
-                               wl1271_tx_min_rate_get(wl,
-                                                      wlvif->basic_rate_set);
-                       if (sta_rate_set)
-                               wlvif->rate_set =
-                                       wl1271_tx_enabled_rates_get(wl,
-                                                               sta_rate_set,
-                                                               wlvif->band);
-                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-
-                       /*
-                        * with wl1271, we don't need to update the
-                        * beacon_int and dtim_period, because the firmware
-                        * updates it by itself when the first beacon is
-                        * received after a join.
-                        */
-                       ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
+       if (changed & BSS_CHANGED_BSSID) {
+               if (!is_zero_ether_addr(bss_conf->bssid)) {
+                       ret = wlcore_set_bssid(wl, wlvif, bss_conf,
+                                              sta_rate_set);
                        if (ret < 0)
                                goto out;
 
-                       /*
-                        * Get a template for hardware connection maintenance
-                        */
-                       dev_kfree_skb(wlvif->probereq);
-                       wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
-                                                                       wlvif,
-                                                                       NULL);
-                       ieoffset = offsetof(struct ieee80211_mgmt,
-                                           u.probe_req.variable);
-                       wl1271_ssid_set(vif, wlvif->probereq, ieoffset);
-
-                       /* enable the connection monitoring feature */
-                       ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
-                       if (ret < 0)
-                               goto out;
+                       /* Need to update the BSSID (for filtering etc) */
+                       do_join = true;
                } else {
-                       /* use defaults when not associated */
-                       bool was_assoc =
-                           !!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED,
-                                                &wlvif->flags);
-                       bool was_ifup =
-                           !!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT,
-                                                &wlvif->flags);
-                       wlvif->aid = 0;
-
-                       /* free probe-request template */
-                       dev_kfree_skb(wlvif->probereq);
-                       wlvif->probereq = NULL;
-
-                       /* revert back to minimum rates for the current band */
-                       wl1271_set_band_rate(wl, wlvif);
-                       wlvif->basic_rate =
-                               wl1271_tx_min_rate_get(wl,
-                                                      wlvif->basic_rate_set);
-                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-
-                       /* disable connection monitor features */
-                       ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
-
-                       /* Disable the keep-alive feature */
-                       ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
+                       ret = wlcore_clear_bssid(wl, wlvif);
                        if (ret < 0)
                                goto out;
-
-                       /* restore the bssid filter and go to dummy bssid */
-                       if (was_assoc) {
-                               /*
-                                * we might have to disable roc, if there was
-                                * no IF_OPER_UP notification.
-                                */
-                               if (!was_ifup) {
-                                       ret = wl12xx_croc(wl, wlvif->role_id);
-                                       if (ret < 0)
-                                               goto out;
-                               }
-                               /*
-                                * (we also need to disable roc in case of
-                                * roaming on the same channel. until we will
-                                * have a better flow...)
-                                */
-                               if (test_bit(wlvif->dev_role_id, wl->roc_map)) {
-                                       ret = wl12xx_croc(wl,
-                                                         wlvif->dev_role_id);
-                                       if (ret < 0)
-                                               goto out;
-                               }
-
-                               wl1271_unjoin(wl, wlvif);
-                               if (!bss_conf->idle)
-                                       wl12xx_start_dev(wl, wlvif);
-                       }
                }
        }
 
@@ -4043,71 +4176,87 @@ sta_not_found:
                goto out;
 
        if (do_join) {
-               ret = wl1271_join(wl, wlvif, set_assoc);
+               ret = wlcore_join(wl, wlvif);
                if (ret < 0) {
                        wl1271_warning("cmd join failed %d", ret);
                        goto out;
                }
+       }
 
-               /* ROC until connected (after EAPOL exchange) */
-               if (!is_ibss) {
-                       ret = wl12xx_roc(wl, wlvif, wlvif->role_id);
+       if (changed & BSS_CHANGED_ASSOC) {
+               if (bss_conf->assoc) {
+                       ret = wlcore_set_assoc(wl, wlvif, bss_conf,
+                                              sta_rate_set);
                        if (ret < 0)
                                goto out;
 
                        if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
                                wl12xx_set_authorized(wl, wlvif);
+               } else {
+                       wlcore_unset_assoc(wl, wlvif);
                }
-               /*
-                * stop device role if started (we might already be in
-                * STA/IBSS role).
-                */
-               if (wl12xx_dev_role_started(wlvif)) {
-                       ret = wl12xx_stop_dev(wl, wlvif);
+       }
+
+       if (changed & BSS_CHANGED_PS) {
+               if ((bss_conf->ps) &&
+                   test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
+                   !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
+                       int ps_mode;
+                       char *ps_mode_str;
+
+                       if (wl->conf.conn.forced_ps) {
+                               ps_mode = STATION_POWER_SAVE_MODE;
+                               ps_mode_str = "forced";
+                       } else {
+                               ps_mode = STATION_AUTO_PS_MODE;
+                               ps_mode_str = "auto";
+                       }
+
+                       wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str);
+
+                       ret = wl1271_ps_set_mode(wl, wlvif, ps_mode);
                        if (ret < 0)
-                               goto out;
+                               wl1271_warning("enter %s ps failed %d",
+                                              ps_mode_str, ret);
+               } else if (!bss_conf->ps &&
+                          test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
+                       wl1271_debug(DEBUG_PSM, "auto ps disabled");
+
+                       ret = wl1271_ps_set_mode(wl, wlvif,
+                                                STATION_ACTIVE_MODE);
+                       if (ret < 0)
+                               wl1271_warning("exit auto ps failed %d", ret);
                }
        }
 
        /* Handle new association with HT. Do this after join. */
-       if (sta_exists) {
-               if ((changed & BSS_CHANGED_HT) &&
-                   (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) {
-                       ret = wl1271_acx_set_ht_capabilities(wl,
-                                                            &sta_ht_cap,
-                                                            true,
-                                                            wlvif->sta.hlid);
-                       if (ret < 0) {
-                               wl1271_warning("Set ht cap true failed %d",
-                                              ret);
-                               goto out;
-                       }
+       if (sta_exists &&
+           (changed & BSS_CHANGED_HT)) {
+               bool enabled =
+                       bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
+
+               ret = wlcore_hw_set_peer_cap(wl,
+                                            &sta_ht_cap,
+                                            enabled,
+                                            wlvif->rate_set,
+                                            wlvif->sta.hlid);
+               if (ret < 0) {
+                       wl1271_warning("Set ht cap failed %d", ret);
+                       goto out;
+
                }
-               /* handle new association without HT and disassociation */
-               else if (changed & BSS_CHANGED_ASSOC) {
-                       ret = wl1271_acx_set_ht_capabilities(wl,
-                                                            &sta_ht_cap,
-                                                            false,
-                                                            wlvif->sta.hlid);
+
+               if (enabled) {
+                       ret = wl1271_acx_set_ht_information(wl, wlvif,
+                                               bss_conf->ht_operation_mode);
                        if (ret < 0) {
-                               wl1271_warning("Set ht cap false failed %d",
+                               wl1271_warning("Set ht information failed %d",
                                               ret);
                                goto out;
                        }
                }
        }
 
-       /* Handle HT information change. Done after join. */
-       if ((changed & BSS_CHANGED_HT) &&
-           (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) {
-               ret = wl1271_acx_set_ht_information(wl, wlvif,
-                                       bss_conf->ht_operation_mode);
-               if (ret < 0) {
-                       wl1271_warning("Set ht information failed %d", ret);
-                       goto out;
-               }
-       }
-
        /* Handle arp filtering. Done after join. */
        if ((changed & BSS_CHANGED_ARP_FILTER) ||
            (!is_ibss && (changed & BSS_CHANGED_QOS))) {
@@ -4115,8 +4264,7 @@ sta_not_found:
                wlvif->sta.qos = bss_conf->qos;
                WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
 
-               if (bss_conf->arp_addr_cnt == 1 &&
-                   bss_conf->arp_filter_enabled) {
+               if (bss_conf->arp_addr_cnt == 1 && bss_conf->assoc) {
                        wlvif->ip_addr = addr;
                        /*
                         * The template should have been configured only upon
@@ -4157,15 +4305,15 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
        bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
        int ret;
 
-       wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
-                    (int)changed);
+       wl1271_debug(DEBUG_MAC80211, "mac80211 bss info role %d changed 0x%x",
+                    wlvif->role_id, (int)changed);
 
        /*
         * make sure to cancel pending disconnections if our association
         * state changed
         */
        if (!is_ap && (changed & BSS_CHANGED_ASSOC))
-               cancel_delayed_work_sync(&wl->connection_loss_work);
+               cancel_delayed_work_sync(&wlvif->connection_loss_work);
 
        if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) &&
            !bss_conf->enable_beacon)
@@ -4188,10 +4336,80 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
        else
                wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
 
-       wl1271_ps_elp_sleep(wl);
+       wl1271_ps_elp_sleep(wl);
+
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+static int wlcore_op_add_chanctx(struct ieee80211_hw *hw,
+                                struct ieee80211_chanctx_conf *ctx)
+{
+       wl1271_debug(DEBUG_MAC80211, "mac80211 add chanctx %d (type %d)",
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def));
+       return 0;
+}
+
+static void wlcore_op_remove_chanctx(struct ieee80211_hw *hw,
+                                    struct ieee80211_chanctx_conf *ctx)
+{
+       wl1271_debug(DEBUG_MAC80211, "mac80211 remove chanctx %d (type %d)",
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def));
+}
+
+static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
+                                    struct ieee80211_chanctx_conf *ctx,
+                                    u32 changed)
+{
+       wl1271_debug(DEBUG_MAC80211,
+                    "mac80211 change chanctx %d (type %d) changed 0x%x",
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def), changed);
+}
+
+static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_chanctx_conf *ctx)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int channel = ieee80211_frequency_to_channel(
+               ctx->def.chan->center_freq);
+
+       wl1271_debug(DEBUG_MAC80211,
+                    "mac80211 assign chanctx (role %d) %d (type %d)",
+                    wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def));
+
+       mutex_lock(&wl->mutex);
+
+       wlvif->band = ctx->def.chan->band;
+       wlvif->channel = channel;
+       wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def);
+
+       /* update default rates according to the band */
+       wl1271_set_band_rate(wl, wlvif);
+
+       mutex_unlock(&wl->mutex);
+
+       return 0;
+}
+
+static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
+                                          struct ieee80211_vif *vif,
+                                          struct ieee80211_chanctx_conf *ctx)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
 
-out:
-       mutex_unlock(&wl->mutex);
+       wl1271_debug(DEBUG_MAC80211,
+                    "mac80211 unassign chanctx (role %d) %d (type %d)",
+                    wlvif->role_id,
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def));
+
+       wl1271_tx_flush(wl);
 }
 
 static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
@@ -4309,6 +4527,9 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
                return -EBUSY;
        }
 
+       /* use the previous security seq, if this is a recovery/resume */
+       wl->links[wl_sta->hlid].total_freed_pkts = wl_sta->total_freed_pkts;
+
        set_bit(wl_sta->hlid, wlvif->ap.sta_hlid_map);
        memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
        wl->active_sta_count++;
@@ -4317,14 +4538,37 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
 
 void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
 {
+       struct wl1271_station *wl_sta;
+       struct ieee80211_sta *sta;
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+
        if (!test_bit(hlid, wlvif->ap.sta_hlid_map))
                return;
 
        clear_bit(hlid, wlvif->ap.sta_hlid_map);
-       memset(wl->links[hlid].addr, 0, ETH_ALEN);
-       wl->links[hlid].ba_bitmap = 0;
        __clear_bit(hlid, &wl->ap_ps_map);
        __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+
+       /*
+        * save the last used PN in the private part of iee80211_sta,
+        * in case of recovery/suspend
+        */
+       rcu_read_lock();
+       sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
+       if (sta) {
+               wl_sta = (void *)sta->drv_priv;
+               wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts;
+
+               /*
+                * increment the initial seq number on recovery to account for
+                * transmitted packets that we haven't yet got in the FW status
+                */
+               if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+                       wl_sta->total_freed_pkts +=
+                                       WL1271_TX_SQN_POST_RECOVERY_PADDING;
+       }
+       rcu_read_unlock();
+
        wl12xx_free_link(wl, wlvif, &hlid);
        wl->active_sta_count--;
 
@@ -4382,6 +4626,45 @@ static int wl12xx_sta_remove(struct wl1271 *wl,
        return ret;
 }
 
+static void wlcore_roc_if_possible(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif)
+{
+       if (find_first_bit(wl->roc_map,
+                          WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)
+               return;
+
+       if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID))
+               return;
+
+       wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
+}
+
+static void wlcore_update_inconn_sta(struct wl1271 *wl,
+                                    struct wl12xx_vif *wlvif,
+                                    struct wl1271_station *wl_sta,
+                                    bool in_connection)
+{
+       if (in_connection) {
+               if (WARN_ON(wl_sta->in_connection))
+                       return;
+               wl_sta->in_connection = true;
+               if (!wlvif->inconn_count++)
+                       wlcore_roc_if_possible(wl, wlvif);
+       } else {
+               if (!wl_sta->in_connection)
+                       return;
+
+               wl_sta->in_connection = false;
+               wlvif->inconn_count--;
+               if (WARN_ON(wlvif->inconn_count < 0))
+                       return;
+
+               if (!wlvif->inconn_count)
+                       if (test_bit(wlvif->role_id, wl->roc_map))
+                               wl12xx_croc(wl, wlvif->role_id);
+       }
+}
+
 static int wl12xx_update_sta_state(struct wl1271 *wl,
                                   struct wl12xx_vif *wlvif,
                                   struct ieee80211_sta *sta,
@@ -4389,19 +4672,22 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
                                   enum ieee80211_sta_state new_state)
 {
        struct wl1271_station *wl_sta;
-       u8 hlid;
        bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
        bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
        int ret;
 
        wl_sta = (struct wl1271_station *)sta->drv_priv;
-       hlid = wl_sta->hlid;
 
        /* Add station (AP mode) */
        if (is_ap &&
            old_state == IEEE80211_STA_NOTEXIST &&
-           new_state == IEEE80211_STA_NONE)
-               return wl12xx_sta_add(wl, wlvif, sta);
+           new_state == IEEE80211_STA_NONE) {
+               ret = wl12xx_sta_add(wl, wlvif, sta);
+               if (ret)
+                       return ret;
+
+               wlcore_update_inconn_sta(wl, wlvif, wl_sta, true);
+       }
 
        /* Remove station (AP mode) */
        if (is_ap &&
@@ -4409,35 +4695,59 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
            new_state == IEEE80211_STA_NOTEXIST) {
                /* must not fail */
                wl12xx_sta_remove(wl, wlvif, sta);
-               return 0;
+
+               wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
        }
 
        /* Authorize station (AP mode) */
        if (is_ap &&
            new_state == IEEE80211_STA_AUTHORIZED) {
-               ret = wl12xx_cmd_set_peer_state(wl, hlid);
+               ret = wl12xx_cmd_set_peer_state(wl, wlvif, wl_sta->hlid);
                if (ret < 0)
                        return ret;
 
                ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
-                                                    hlid);
-               return ret;
+                                                    wl_sta->hlid);
+               if (ret)
+                       return ret;
+
+               wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
        }
 
        /* Authorize station */
        if (is_sta &&
            new_state == IEEE80211_STA_AUTHORIZED) {
                set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
-               return wl12xx_set_authorized(wl, wlvif);
+               ret = wl12xx_set_authorized(wl, wlvif);
+               if (ret)
+                       return ret;
        }
 
        if (is_sta &&
            old_state == IEEE80211_STA_AUTHORIZED &&
            new_state == IEEE80211_STA_ASSOC) {
                clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
-               return 0;
+               clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags);
+       }
+
+       /* clear ROCs on failure or authorization */
+       if (is_sta &&
+           (new_state == IEEE80211_STA_AUTHORIZED ||
+            new_state == IEEE80211_STA_NOTEXIST)) {
+               if (test_bit(wlvif->role_id, wl->roc_map))
+                       wl12xx_croc(wl, wlvif->role_id);
        }
 
+       if (is_sta &&
+           old_state == IEEE80211_STA_NOTEXIST &&
+           new_state == IEEE80211_STA_NONE) {
+               if (find_first_bit(wl->roc_map,
+                                  WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) {
+                       WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID);
+                       wl12xx_roc(wl, wlvif, wlvif->role_id,
+                                  wlvif->band, wlvif->channel);
+               }
+       }
        return 0;
 }
 
@@ -4502,18 +4812,18 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
 
        if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
                hlid = wlvif->sta.hlid;
-               ba_bitmap = &wlvif->sta.ba_rx_bitmap;
        } else if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
                struct wl1271_station *wl_sta;
 
                wl_sta = (struct wl1271_station *)sta->drv_priv;
                hlid = wl_sta->hlid;
-               ba_bitmap = &wl->links[hlid].ba_bitmap;
        } else {
                ret = -EINVAL;
                goto out;
        }
 
+       ba_bitmap = &wl->links[hlid].ba_bitmap;
+
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
                goto out;
@@ -4528,7 +4838,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
                        break;
                }
 
-               if (wl->ba_rx_session_count >= RX_BA_MAX_SESSIONS) {
+               if (wl->ba_rx_session_count >= wl->ba_rx_session_count_max) {
                        ret = -EBUSY;
                        wl1271_error("exceeded max RX BA sessions");
                        break;
@@ -4575,7 +4885,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
         * Falling break here on purpose for all TX APDU commands.
         */
        case IEEE80211_AMPDU_TX_START:
-       case IEEE80211_AMPDU_TX_STOP:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
        case IEEE80211_AMPDU_TX_OPERATIONAL:
                ret = -EINVAL;
                break;
@@ -4665,12 +4977,23 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
 
        /* TODO: change mac80211 to pass vif as param */
        wl12xx_for_each_wlvif_sta(wl, wlvif) {
-               ret = wl12xx_cmd_channel_switch(wl, wlvif, ch_switch);
+               unsigned long delay_usec;
+
+               ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
+               if (ret)
+                       goto out_sleep;
+
+               set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
 
-               if (!ret)
-                       set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
+               /* indicate failure 5 seconds after channel switch time */
+               delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) *
+                            ch_switch->count;
+               ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work,
+                               usecs_to_jiffies(delay_usec) +
+                               msecs_to_jiffies(5000));
        }
 
+out_sleep:
        wl1271_ps_elp_sleep(wl);
 
 out:
@@ -4684,6 +5007,177 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop)
        wl1271_tx_flush(wl);
 }
 
+static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      struct ieee80211_channel *chan,
+                                      int duration)
+{
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       struct wl1271 *wl = hw->priv;
+       int channel, ret = 0;
+
+       channel = ieee80211_frequency_to_channel(chan->center_freq);
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 roc %d (%d)",
+                    channel, wlvif->role_id);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /* return EBUSY if we can't ROC right now */
+       if (WARN_ON(wl->roc_vif ||
+                   find_first_bit(wl->roc_map,
+                                  WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wl12xx_start_dev(wl, wlvif, chan->band, channel);
+       if (ret < 0)
+               goto out_sleep;
+
+       wl->roc_vif = vif;
+       ieee80211_queue_delayed_work(hw, &wl->roc_complete_work,
+                                    msecs_to_jiffies(duration));
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+       return ret;
+}
+
+static int __wlcore_roc_completed(struct wl1271 *wl)
+{
+       struct wl12xx_vif *wlvif;
+       int ret;
+
+       /* already completed */
+       if (unlikely(!wl->roc_vif))
+               return 0;
+
+       wlvif = wl12xx_vif_to_data(wl->roc_vif);
+
+       if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
+               return -EBUSY;
+
+       ret = wl12xx_stop_dev(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       wl->roc_vif = NULL;
+
+       return 0;
+}
+
+static int wlcore_roc_completed(struct wl1271 *wl)
+{
+       int ret;
+
+       wl1271_debug(DEBUG_MAC80211, "roc complete");
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = __wlcore_roc_completed(wl);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+
+       return ret;
+}
+
+static void wlcore_roc_complete_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       int ret;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wl = container_of(dwork, struct wl1271, roc_complete_work);
+
+       ret = wlcore_roc_completed(wl);
+       if (!ret)
+               ieee80211_remain_on_channel_expired(wl->hw);
+}
+
+static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+       struct wl1271 *wl = hw->priv;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 croc");
+
+       /* TODO: per-vif */
+       wl1271_tx_flush(wl);
+
+       /*
+        * we can't just flush_work here, because it might deadlock
+        * (as we might get called from the same workqueue)
+        */
+       cancel_delayed_work_sync(&wl->roc_complete_work);
+       wlcore_roc_completed(wl);
+
+       return 0;
+}
+
+static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_sta *sta,
+                                   u32 changed)
+{
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       struct wl1271 *wl = hw->priv;
+
+       wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
+}
+
+static int wlcore_op_get_rssi(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              struct ieee80211_sta *sta,
+                              s8 *rssi_dbm)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int ret = 0;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi");
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out_sleep;
+
+       ret = wlcore_acx_average_rssi(wl, wlvif, rssi_dbm);
+       if (ret < 0)
+               goto out_sleep;
+
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+
+out:
+       mutex_unlock(&wl->mutex);
+
+       return ret;
+}
+
 static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
 {
        struct wl1271 *wl = hw->priv;
@@ -4747,20 +5241,20 @@ static struct ieee80211_rate wl1271_rates[] = {
 
 /* can't be const, mac80211 writes to this */
 static struct ieee80211_channel wl1271_channels[] = {
-       { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
-       { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
-       { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
-       { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
-       { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
-       { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
-       { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
-       { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
-       { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
-       { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
-       { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
-       { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
-       { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
-       { .hw_value = 14, .center_freq = 2484, .max_power = 25 },
+       { .hw_value = 1, .center_freq = 2412, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 2, .center_freq = 2417, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 3, .center_freq = 2422, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 4, .center_freq = 2427, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 5, .center_freq = 2432, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 6, .center_freq = 2437, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 7, .center_freq = 2442, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 8, .center_freq = 2447, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 9, .center_freq = 2452, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 10, .center_freq = 2457, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 11, .center_freq = 2462, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 12, .center_freq = 2467, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 13, .center_freq = 2472, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 14, .center_freq = 2484, .max_power = WLCORE_MAX_TXPWR },
 };
 
 /* can't be const, mac80211 writes to this */
@@ -4801,40 +5295,40 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = {
 
 /* 5 GHz band channels for WL1273 */
 static struct ieee80211_channel wl1271_channels_5ghz[] = {
-       { .hw_value = 7, .center_freq = 5035, .max_power = 25 },
-       { .hw_value = 8, .center_freq = 5040, .max_power = 25 },
-       { .hw_value = 9, .center_freq = 5045, .max_power = 25 },
-       { .hw_value = 11, .center_freq = 5055, .max_power = 25 },
-       { .hw_value = 12, .center_freq = 5060, .max_power = 25 },
-       { .hw_value = 16, .center_freq = 5080, .max_power = 25 },
-       { .hw_value = 34, .center_freq = 5170, .max_power = 25 },
-       { .hw_value = 36, .center_freq = 5180, .max_power = 25 },
-       { .hw_value = 38, .center_freq = 5190, .max_power = 25 },
-       { .hw_value = 40, .center_freq = 5200, .max_power = 25 },
-       { .hw_value = 42, .center_freq = 5210, .max_power = 25 },
-       { .hw_value = 44, .center_freq = 5220, .max_power = 25 },
-       { .hw_value = 46, .center_freq = 5230, .max_power = 25 },
-       { .hw_value = 48, .center_freq = 5240, .max_power = 25 },
-       { .hw_value = 52, .center_freq = 5260, .max_power = 25 },
-       { .hw_value = 56, .center_freq = 5280, .max_power = 25 },
-       { .hw_value = 60, .center_freq = 5300, .max_power = 25 },
-       { .hw_value = 64, .center_freq = 5320, .max_power = 25 },
-       { .hw_value = 100, .center_freq = 5500, .max_power = 25 },
-       { .hw_value = 104, .center_freq = 5520, .max_power = 25 },
-       { .hw_value = 108, .center_freq = 5540, .max_power = 25 },
-       { .hw_value = 112, .center_freq = 5560, .max_power = 25 },
-       { .hw_value = 116, .center_freq = 5580, .max_power = 25 },
-       { .hw_value = 120, .center_freq = 5600, .max_power = 25 },
-       { .hw_value = 124, .center_freq = 5620, .max_power = 25 },
-       { .hw_value = 128, .center_freq = 5640, .max_power = 25 },
-       { .hw_value = 132, .center_freq = 5660, .max_power = 25 },
-       { .hw_value = 136, .center_freq = 5680, .max_power = 25 },
-       { .hw_value = 140, .center_freq = 5700, .max_power = 25 },
-       { .hw_value = 149, .center_freq = 5745, .max_power = 25 },
-       { .hw_value = 153, .center_freq = 5765, .max_power = 25 },
-       { .hw_value = 157, .center_freq = 5785, .max_power = 25 },
-       { .hw_value = 161, .center_freq = 5805, .max_power = 25 },
-       { .hw_value = 165, .center_freq = 5825, .max_power = 25 },
+       { .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 36, .center_freq = 5180, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 38, .center_freq = 5190, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 40, .center_freq = 5200, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 42, .center_freq = 5210, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 44, .center_freq = 5220, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 46, .center_freq = 5230, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 48, .center_freq = 5240, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 52, .center_freq = 5260, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 56, .center_freq = 5280, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 60, .center_freq = 5300, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 64, .center_freq = 5320, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 100, .center_freq = 5500, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 104, .center_freq = 5520, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 108, .center_freq = 5540, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 112, .center_freq = 5560, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 116, .center_freq = 5580, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 120, .center_freq = 5600, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 124, .center_freq = 5620, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 128, .center_freq = 5640, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 132, .center_freq = 5660, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 136, .center_freq = 5680, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 140, .center_freq = 5700, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 149, .center_freq = 5745, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 153, .center_freq = 5765, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 157, .center_freq = 5785, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 161, .center_freq = 5805, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 165, .center_freq = 5825, .max_power = WLCORE_MAX_TXPWR },
 };
 
 static struct ieee80211_supported_band wl1271_band_5ghz = {
@@ -4875,6 +5369,15 @@ static const struct ieee80211_ops wl1271_ops = {
        .set_bitrate_mask = wl12xx_set_bitrate_mask,
        .channel_switch = wl12xx_op_channel_switch,
        .flush = wlcore_op_flush,
+       .remain_on_channel = wlcore_op_remain_on_channel,
+       .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
+       .add_chanctx = wlcore_op_add_chanctx,
+       .remove_chanctx = wlcore_op_remove_chanctx,
+       .change_chanctx = wlcore_op_change_chanctx,
+       .assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
+       .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
+       .sta_rc_update = wlcore_op_sta_rc_update,
+       .get_rssi = wlcore_op_get_rssi,
        CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
 };
 
@@ -5044,34 +5547,6 @@ static struct bin_attribute fwlog_attr = {
        .read = wl1271_sysfs_read_fwlog,
 };
 
-static void wl1271_connection_loss_work(struct work_struct *work)
-{
-       struct delayed_work *dwork;
-       struct wl1271 *wl;
-       struct ieee80211_vif *vif;
-       struct wl12xx_vif *wlvif;
-
-       dwork = container_of(work, struct delayed_work, work);
-       wl = container_of(dwork, struct wl1271, connection_loss_work);
-
-       wl1271_info("Connection loss work.");
-
-       mutex_lock(&wl->mutex);
-
-       if (unlikely(wl->state != WLCORE_STATE_ON))
-               goto out;
-
-       /* Call mac80211 connection loss */
-       wl12xx_for_each_wlvif_sta(wl, wlvif) {
-               if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
-                       goto out;
-               vif = wl12xx_wlvif_to_vif(wlvif);
-               ieee80211_connection_loss(vif);
-       }
-out:
-       mutex_unlock(&wl->mutex);
-}
-
 static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
 {
        int i;
@@ -5117,7 +5592,7 @@ static int wl12xx_get_hw_info(struct wl1271 *wl)
 
        ret = wl12xx_set_power_on(wl);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id);
        if (ret < 0)
@@ -5207,10 +5682,9 @@ static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
        },
 };
 
-static const struct ieee80211_iface_combination
+static struct ieee80211_iface_combination
 wlcore_iface_combinations[] = {
        {
-         .num_different_channels = 1,
          .max_interfaces = 3,
          .limits = wlcore_iface_limits,
          .n_limits = ARRAY_SIZE(wlcore_iface_limits),
@@ -5219,6 +5693,7 @@ wlcore_iface_combinations[] = {
 
 static int wl1271_init_ieee80211(struct wl1271 *wl)
 {
+       int i;
        static const u32 cipher_suites[] = {
                WLAN_CIPHER_SUITE_WEP40,
                WLAN_CIPHER_SUITE_WEP104,
@@ -5249,7 +5724,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                IEEE80211_HW_AP_LINK_PS |
                IEEE80211_HW_AMPDU_AGGREGATION |
                IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
-               IEEE80211_HW_SCAN_WHILE_IDLE;
+               IEEE80211_HW_SCAN_WHILE_IDLE |
+               IEEE80211_HW_QUEUE_CONTROL;
 
        wl->hw->wiphy->cipher_suites = cipher_suites;
        wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
@@ -5271,6 +5747,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
                sizeof(struct ieee80211_header);
 
+       wl->hw->wiphy->max_remain_on_channel_duration = 5000;
+
        wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
                                WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 
@@ -5278,6 +5756,22 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
                     ARRAY_SIZE(wl1271_channels_5ghz) >
                     WL1271_MAX_CHANNELS);
+       /*
+       * clear channel flags from the previous usage
+       * and restore max_power & max_antenna_gain values.
+       */
+       for (i = 0; i < ARRAY_SIZE(wl1271_channels); i++) {
+               wl1271_band_2ghz.channels[i].flags = 0;
+               wl1271_band_2ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
+               wl1271_band_2ghz.channels[i].max_antenna_gain = 0;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(wl1271_channels_5ghz); i++) {
+               wl1271_band_5ghz.channels[i].flags = 0;
+               wl1271_band_5ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
+               wl1271_band_5ghz.channels[i].max_antenna_gain = 0;
+       }
+
        /*
         * We keep local copies of the band structs because we need to
         * modify them on a per-device basis.
@@ -5298,7 +5792,14 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
                &wl->bands[IEEE80211_BAND_5GHZ];
 
-       wl->hw->queues = 4;
+       /*
+        * allow 4 queues per mac address we support +
+        * 1 cab queue per mac + one global offchannel Tx queue
+        */
+       wl->hw->queues = (NUM_TX_QUEUES + 1) * WLCORE_NUM_MAC_ADDRESSES + 1;
+
+       /* the last queue is the offchannel queue */
+       wl->hw->offchannel_tx_hw_queue = wl->hw->queues - 1;
        wl->hw->max_rates = 1;
 
        wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
@@ -5311,6 +5812,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
 
        /* allowed interface combinations */
+       wlcore_iface_combinations[0].num_different_channels = wl->num_channels;
        wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
        wl->hw->wiphy->n_iface_combinations =
                ARRAY_SIZE(wlcore_iface_combinations);
@@ -5327,7 +5829,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
 
 #define WL1271_DEFAULT_CHANNEL 0
 
-struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
+struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
+                                    u32 mbox_size)
 {
        struct ieee80211_hw *hw;
        struct wl1271 *wl;
@@ -5369,9 +5872,8 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
        INIT_WORK(&wl->tx_work, wl1271_tx_work);
        INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
        INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
+       INIT_DELAYED_WORK(&wl->roc_complete_work, wlcore_roc_complete_work);
        INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
-       INIT_DELAYED_WORK(&wl->connection_loss_work,
-                         wl1271_connection_loss_work);
 
        wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
        if (!wl->freezable_wq) {
@@ -5387,14 +5889,15 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
        wl->flags = 0;
        wl->sg_enabled = true;
        wl->sleep_auth = WL1271_PSM_ILLEGAL;
+       wl->recovery_count = 0;
        wl->hw_pg_ver = -1;
        wl->ap_ps_map = 0;
        wl->ap_fw_ps_map = 0;
        wl->quirks = 0;
        wl->platform_quirks = 0;
-       wl->sched_scanning = false;
        wl->system_hlid = WL12XX_SYSTEM_HLID;
        wl->active_sta_count = 0;
+       wl->active_link_count = 0;
        wl->fwlog_size = 0;
        init_waitqueue_head(&wl->fwlog_waitq);
 
@@ -5434,14 +5937,24 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
                goto err_dummy_packet;
        }
 
-       wl->mbox = kmalloc(sizeof(*wl->mbox), GFP_KERNEL | GFP_DMA);
+       wl->mbox_size = mbox_size;
+       wl->mbox = kmalloc(wl->mbox_size, GFP_KERNEL | GFP_DMA);
        if (!wl->mbox) {
                ret = -ENOMEM;
                goto err_fwlog;
        }
 
+       wl->buffer_32 = kmalloc(sizeof(*wl->buffer_32), GFP_KERNEL);
+       if (!wl->buffer_32) {
+               ret = -ENOMEM;
+               goto err_mbox;
+       }
+
        return hw;
 
+err_mbox:
+       kfree(wl->mbox);
+
 err_fwlog:
        free_page((unsigned long)wl->fwlog);
 
@@ -5480,6 +5993,8 @@ int wlcore_free_hw(struct wl1271 *wl)
        device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
 
        device_remove_file(wl->dev, &dev_attr_bt_coex_state);
+       kfree(wl->buffer_32);
+       kfree(wl->mbox);
        free_page((unsigned long)wl->fwlog);
        dev_kfree_skb(wl->dummy_packet);
        free_pages((unsigned long)wl->aggr_buf, get_order(wl->aggr_buf_size));
@@ -5503,40 +6018,12 @@ int wlcore_free_hw(struct wl1271 *wl)
 }
 EXPORT_SYMBOL_GPL(wlcore_free_hw);
 
-static irqreturn_t wl12xx_hardirq(int irq, void *cookie)
-{
-       struct wl1271 *wl = cookie;
-       unsigned long flags;
-
-       wl1271_debug(DEBUG_IRQ, "IRQ");
-
-       /* complete the ELP completion */
-       spin_lock_irqsave(&wl->wl_lock, flags);
-       set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
-       if (wl->elp_compl) {
-               complete(wl->elp_compl);
-               wl->elp_compl = NULL;
-       }
-
-       if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
-               /* don't enqueue a work right now. mark it as pending */
-               set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
-               wl1271_debug(DEBUG_IRQ, "should not enqueue work");
-               disable_irq_nosync(wl->irq);
-               pm_wakeup_event(wl->dev, 0);
-               spin_unlock_irqrestore(&wl->wl_lock, flags);
-               return IRQ_HANDLED;
-       }
-       spin_unlock_irqrestore(&wl->wl_lock, flags);
-
-       return IRQ_WAKE_THREAD;
-}
-
 static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 {
        struct wl1271 *wl = context;
        struct platform_device *pdev = wl->pdev;
-       struct wl12xx_platform_data *pdata = pdev->dev.platform_data;
+       struct wlcore_platdev_data *pdev_data = pdev->dev.platform_data;
+       struct wl12xx_platform_data *pdata = pdev_data->pdata;
        unsigned long irqflags;
        int ret;
 
@@ -5565,17 +6052,15 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 
        wl->irq = platform_get_irq(pdev, 0);
        wl->platform_quirks = pdata->platform_quirks;
-       wl->set_power = pdata->set_power;
-       wl->if_ops = pdata->ops;
+       wl->if_ops = pdev_data->if_ops;
 
        if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
                irqflags = IRQF_TRIGGER_RISING;
        else
                irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
 
-       ret = request_threaded_irq(wl->irq, wl12xx_hardirq, wlcore_irq,
-                                  irqflags,
-                                  pdev->name, wl);
+       ret = request_threaded_irq(wl->irq, NULL, wlcore_irq,
+                                  irqflags, pdev->name, wl);
        if (ret < 0) {
                wl1271_error("request_irq() failed: %d", ret);
                goto out_free_nvs;
@@ -5712,10 +6197,10 @@ module_param_named(fwlog, fwlog_param, charp, 0);
 MODULE_PARM_DESC(fwlog,
                 "FW logger options: continuous, ondemand, dbgpins or disable");
 
-module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR);
+module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
 MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
 
-module_param(no_recovery, bool, S_IRUSR | S_IWUSR);
+module_param(no_recovery, int, S_IRUSR | S_IWUSR);
 MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck.");
 
 MODULE_LICENSE("GPL");
index 4d1414a673fb6025f42d20e4b7da16fcaf2f6265..9b7b6e2e4fbcef50adaab008cfeb1867bf4a266d 100644 (file)
@@ -151,9 +151,6 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
                        wl12xx_queue_recovery_work(wl);
                        ret = -ETIMEDOUT;
                        goto err;
-               } else if (ret < 0) {
-                       wl1271_error("ELP wakeup completion error.");
-                       goto err;
                }
        }
 
@@ -242,11 +239,12 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
        struct ieee80211_tx_info *info;
        unsigned long flags;
        int filtered[NUM_TX_QUEUES];
+       struct wl1271_link *lnk = &wl->links[hlid];
 
        /* filter all frames currently in the low level queues for this hlid */
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                filtered[i] = 0;
-               while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
+               while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
                        filtered[i]++;
 
                        if (WARN_ON(wl12xx_is_dummy_packet(wl, skb)))
@@ -260,8 +258,11 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
        }
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-       for (i = 0; i < NUM_TX_QUEUES; i++)
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
                wl->tx_queue_count[i] -= filtered[i];
+               if (lnk->wlvif)
+                       lnk->wlvif->tx_queue_count[i] -= filtered[i];
+       }
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
        wl1271_handle_tx_low_watermark(wl);
index 9ee0ec6fd1db3d666769747e2f52c42bcdbe53e1..6791a1a6afba06702b434b1ae11a196377a0d229 100644 (file)
@@ -92,11 +92,16 @@ static void wl1271_rx_status(struct wl1271 *wl,
                status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED |
                                RX_FLAG_DECRYPTED;
 
-               if (unlikely(desc_err_code == WL1271_RX_DESC_MIC_FAIL)) {
+               if (unlikely(desc_err_code & WL1271_RX_DESC_MIC_FAIL)) {
                        status->flag |= RX_FLAG_MMIC_ERROR;
-                       wl1271_warning("Michael MIC error");
+                       wl1271_warning("Michael MIC error. Desc: 0x%x",
+                                      desc_err_code);
                }
        }
+
+       if (beacon)
+               wlcore_set_pending_regdomain_ch(wl, (u16)desc->channel,
+                                               status->band);
 }
 
 static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
@@ -108,7 +113,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
        u8 *buf;
        u8 beacon = 0;
        u8 is_data = 0;
-       u8 reserved = 0;
+       u8 reserved = 0, offset_to_data = 0;
        u16 seq_num;
        u32 pkt_data_len;
 
@@ -128,6 +133,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
 
        if (rx_align == WLCORE_RX_BUF_UNALIGNED)
                reserved = RX_BUF_ALIGN;
+       else if (rx_align == WLCORE_RX_BUF_PADDED)
+               offset_to_data = RX_BUF_ALIGN;
 
        /* the data read starts with the descriptor */
        desc = (struct wl1271_rx_descriptor *) data;
@@ -139,19 +146,15 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
                return 0;
        }
 
-       switch (desc->status & WL1271_RX_DESC_STATUS_MASK) {
        /* discard corrupted packets */
-       case WL1271_RX_DESC_DRIVER_RX_Q_FAIL:
-       case WL1271_RX_DESC_DECRYPT_FAIL:
-               wl1271_warning("corrupted packet in RX with status: 0x%x",
-                              desc->status & WL1271_RX_DESC_STATUS_MASK);
-               return -EINVAL;
-       case WL1271_RX_DESC_SUCCESS:
-       case WL1271_RX_DESC_MIC_FAIL:
-               break;
-       default:
-               wl1271_error("invalid RX descriptor status: 0x%x",
-                            desc->status & WL1271_RX_DESC_STATUS_MASK);
+       if (desc->status & WL1271_RX_DESC_DECRYPT_FAIL) {
+               hdr = (void *)(data + sizeof(*desc) + offset_to_data);
+               wl1271_warning("corrupted packet in RX: status: 0x%x len: %d",
+                              desc->status & WL1271_RX_DESC_STATUS_MASK,
+                              pkt_data_len);
+               wl1271_dump((DEBUG_RX|DEBUG_CMD), "PKT: ", data + sizeof(*desc),
+                           min(pkt_data_len,
+                               ieee80211_hdrlen(hdr->frame_control)));
                return -EINVAL;
        }
 
index 71eba18999152f6462600f703ca345a91f37e56d..3363f60fb7da6dfb05ad672b359ac5d037fbb401 100644 (file)
  * Bits 3-5 - process_id tag (AP mode FW)
  * Bits 6-7 - reserved
  */
-#define WL1271_RX_DESC_STATUS_MASK      0x03
+#define WL1271_RX_DESC_STATUS_MASK      0x07
 
 #define WL1271_RX_DESC_SUCCESS          0x00
 #define WL1271_RX_DESC_DECRYPT_FAIL     0x01
 #define WL1271_RX_DESC_MIC_FAIL         0x02
-#define WL1271_RX_DESC_DRIVER_RX_Q_FAIL 0x03
 
 #define RX_MEM_BLOCK_MASK            0xFF
 #define RX_BUF_SIZE_MASK             0xFFF00
index d00501493dfec06d9aa314c675e1834f12bb98d2..f407101e525b60a9c4bff3c402865d12f3d6afde 100644 (file)
@@ -35,7 +35,6 @@ void wl1271_scan_complete_work(struct work_struct *work)
 {
        struct delayed_work *dwork;
        struct wl1271 *wl;
-       struct ieee80211_vif *vif;
        struct wl12xx_vif *wlvif;
        int ret;
 
@@ -52,8 +51,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
        if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
                goto out;
 
-       vif = wl->scan_vif;
-       wlvif = wl12xx_vif_to_data(vif);
+       wlvif = wl->scan_wlvif;
 
        /*
         * Rearm the tx watchdog just before idling scan. This
@@ -64,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
        wl->scan.state = WL1271_SCAN_STATE_IDLE;
        memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
        wl->scan.req = NULL;
-       wl->scan_vif = NULL;
+       wl->scan_wlvif = NULL;
 
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
@@ -82,6 +80,8 @@ void wl1271_scan_complete_work(struct work_struct *work)
                wl12xx_queue_recovery_work(wl);
        }
 
+       wlcore_cmd_regdomain_config_locked(wl);
+
        ieee80211_scan_completed(wl->hw, false);
 
 out:
@@ -89,371 +89,99 @@ out:
 
 }
 
-
-static int wl1271_get_scan_channels(struct wl1271 *wl,
-                                   struct cfg80211_scan_request *req,
-                                   struct basic_scan_channel_params *channels,
-                                   enum ieee80211_band band, bool passive)
-{
-       struct conf_scan_settings *c = &wl->conf.scan;
-       int i, j;
-       u32 flags;
-
-       for (i = 0, j = 0;
-            i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
-            i++) {
-               flags = req->channels[i]->flags;
-
-               if (!test_bit(i, wl->scan.scanned_ch) &&
-                   !(flags & IEEE80211_CHAN_DISABLED) &&
-                   (req->channels[i]->band == band) &&
-                   /*
-                    * In passive scans, we scan all remaining
-                    * channels, even if not marked as such.
-                    * In active scans, we only scan channels not
-                    * marked as passive.
-                    */
-                   (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) {
-                       wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
-                                    req->channels[i]->band,
-                                    req->channels[i]->center_freq);
-                       wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
-                                    req->channels[i]->hw_value,
-                                    req->channels[i]->flags);
-                       wl1271_debug(DEBUG_SCAN,
-                                    "max_antenna_gain %d, max_power %d",
-                                    req->channels[i]->max_antenna_gain,
-                                    req->channels[i]->max_power);
-                       wl1271_debug(DEBUG_SCAN, "beacon_found %d",
-                                    req->channels[i]->beacon_found);
-
-                       if (!passive) {
-                               channels[j].min_duration =
-                                       cpu_to_le32(c->min_dwell_time_active);
-                               channels[j].max_duration =
-                                       cpu_to_le32(c->max_dwell_time_active);
-                       } else {
-                               channels[j].min_duration =
-                                       cpu_to_le32(c->min_dwell_time_passive);
-                               channels[j].max_duration =
-                                       cpu_to_le32(c->max_dwell_time_passive);
-                       }
-                       channels[j].early_termination = 0;
-                       channels[j].tx_power_att = req->channels[i]->max_power;
-                       channels[j].channel = req->channels[i]->hw_value;
-
-                       memset(&channels[j].bssid_lsb, 0xff, 4);
-                       memset(&channels[j].bssid_msb, 0xff, 2);
-
-                       /* Mark the channels we already used */
-                       set_bit(i, wl->scan.scanned_ch);
-
-                       j++;
-               }
-       }
-
-       return j;
-}
-
-#define WL1271_NOTHING_TO_SCAN 1
-
-static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif,
-                           enum ieee80211_band band,
-                           bool passive, u32 basic_rate)
+static void wlcore_started_vifs_iter(void *data, u8 *mac,
+                                    struct ieee80211_vif *vif)
 {
-       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       struct wl1271_cmd_scan *cmd;
-       struct wl1271_cmd_trigger_scan_to *trigger;
-       int ret;
-       u16 scan_options = 0;
-
-       /* skip active scans if we don't have SSIDs */
-       if (!passive && wl->scan.req->n_ssids == 0)
-               return WL1271_NOTHING_TO_SCAN;
-
-       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-       trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
-       if (!cmd || !trigger) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       if (wl->conf.scan.split_scan_timeout)
-               scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN;
-
-       if (passive)
-               scan_options |= WL1271_SCAN_OPT_PASSIVE;
-
-       cmd->params.role_id = wlvif->role_id;
-
-       if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) {
-               ret = -EINVAL;
-               goto out;
-       }
-
-       cmd->params.scan_options = cpu_to_le16(scan_options);
-
-       cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
-                                                   cmd->channels,
-                                                   band, passive);
-       if (cmd->params.n_ch == 0) {
-               ret = WL1271_NOTHING_TO_SCAN;
-               goto out;
-       }
-
-       cmd->params.tx_rate = cpu_to_le32(basic_rate);
-       cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
-       cmd->params.tid_trigger = CONF_TX_AC_ANY_TID;
-       cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
-
-       if (band == IEEE80211_BAND_2GHZ)
-               cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
-       else
-               cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
-
-       if (wl->scan.ssid_len && wl->scan.ssid) {
-               cmd->params.ssid_len = wl->scan.ssid_len;
-               memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
-       }
-
-       memcpy(cmd->addr, vif->addr, ETH_ALEN);
-
-       ret = wl12xx_cmd_build_probe_req(wl, wlvif,
-                                        cmd->params.role_id, band,
-                                        wl->scan.ssid, wl->scan.ssid_len,
-                                        wl->scan.req->ie,
-                                        wl->scan.req->ie_len, false);
-       if (ret < 0) {
-               wl1271_error("PROBE request template failed");
-               goto out;
-       }
-
-       trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout);
-       ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
-                             sizeof(*trigger), 0);
-       if (ret < 0) {
-               wl1271_error("trigger scan to failed for hw scan");
-               goto out;
-       }
-
-       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+       int *count = (int *)data;
 
-       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
-       if (ret < 0) {
-               wl1271_error("SCAN failed");
-               goto out;
-       }
-
-out:
-       kfree(cmd);
-       kfree(trigger);
-       return ret;
+       if (!vif->bss_conf.idle)
+               (*count)++;
 }
 
-void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif)
+static int wlcore_count_started_vifs(struct wl1271 *wl)
 {
-       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       int ret = 0;
-       enum ieee80211_band band;
-       u32 rate, mask;
-
-       switch (wl->scan.state) {
-       case WL1271_SCAN_STATE_IDLE:
-               break;
-
-       case WL1271_SCAN_STATE_2GHZ_ACTIVE:
-               band = IEEE80211_BAND_2GHZ;
-               mask = wlvif->bitrate_masks[band];
-               if (wl->scan.req->no_cck) {
-                       mask &= ~CONF_TX_CCK_RATES;
-                       if (!mask)
-                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
-               }
-               rate = wl1271_tx_min_rate_get(wl, mask);
-               ret = wl1271_scan_send(wl, vif, band, false, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
-
-       case WL1271_SCAN_STATE_2GHZ_PASSIVE:
-               band = IEEE80211_BAND_2GHZ;
-               mask = wlvif->bitrate_masks[band];
-               if (wl->scan.req->no_cck) {
-                       mask &= ~CONF_TX_CCK_RATES;
-                       if (!mask)
-                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
-               }
-               rate = wl1271_tx_min_rate_get(wl, mask);
-               ret = wl1271_scan_send(wl, vif, band, true, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       if (wl->enable_11a)
-                               wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
-                       else
-                               wl->scan.state = WL1271_SCAN_STATE_DONE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
+       int count = 0;
 
-       case WL1271_SCAN_STATE_5GHZ_ACTIVE:
-               band = IEEE80211_BAND_5GHZ;
-               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
-               ret = wl1271_scan_send(wl, vif, band, false, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
-
-       case WL1271_SCAN_STATE_5GHZ_PASSIVE:
-               band = IEEE80211_BAND_5GHZ;
-               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
-               ret = wl1271_scan_send(wl, vif, band, true, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       wl->scan.state = WL1271_SCAN_STATE_DONE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
-
-       case WL1271_SCAN_STATE_DONE:
-               wl->scan.failed = false;
-               cancel_delayed_work(&wl->scan_complete_work);
-               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
-                                            msecs_to_jiffies(0));
-               break;
-
-       default:
-               wl1271_error("invalid scan state");
-               break;
-       }
-
-       if (ret < 0) {
-               cancel_delayed_work(&wl->scan_complete_work);
-               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
-                                            msecs_to_jiffies(0));
-       }
-}
-
-int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
-               const u8 *ssid, size_t ssid_len,
-               struct cfg80211_scan_request *req)
-{
-       /*
-        * cfg80211 should guarantee that we don't get more channels
-        * than what we have registered.
-        */
-       BUG_ON(req->n_channels > WL1271_MAX_CHANNELS);
-
-       if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
-               return -EBUSY;
-
-       wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
-
-       if (ssid_len && ssid) {
-               wl->scan.ssid_len = ssid_len;
-               memcpy(wl->scan.ssid, ssid, ssid_len);
-       } else {
-               wl->scan.ssid_len = 0;
-       }
-
-       wl->scan_vif = vif;
-       wl->scan.req = req;
-       memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
-
-       /* we assume failure so that timeout scenarios are handled correctly */
-       wl->scan.failed = true;
-       ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
-                                    msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
-
-       wl1271_scan_stm(wl, vif);
-
-       return 0;
-}
-
-int wl1271_scan_stop(struct wl1271 *wl)
-{
-       struct wl1271_cmd_header *cmd = NULL;
-       int ret = 0;
-
-       if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
-               return -EINVAL;
-
-       wl1271_debug(DEBUG_CMD, "cmd scan stop");
-
-       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-       if (!cmd) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd,
-                             sizeof(*cmd), 0);
-       if (ret < 0) {
-               wl1271_error("cmd stop_scan failed");
-               goto out;
-       }
-out:
-       kfree(cmd);
-       return ret;
+       ieee80211_iterate_active_interfaces_atomic(wl->hw,
+                                       IEEE80211_IFACE_ITER_RESUME_ALL,
+                                       wlcore_started_vifs_iter, &count);
+       return count;
 }
 
 static int
-wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
-                                   struct cfg80211_sched_scan_request *req,
-                                   struct conn_scan_ch_params *channels,
-                                   u32 band, bool radar, bool passive,
-                                   int start, int max_channels,
-                                   u8 *n_pactive_ch)
+wlcore_scan_get_channels(struct wl1271 *wl,
+                        struct ieee80211_channel *req_channels[],
+                        u32 n_channels,
+                        u32 n_ssids,
+                        struct conn_scan_ch_params *channels,
+                        u32 band, bool radar, bool passive,
+                        int start, int max_channels,
+                        u8 *n_pactive_ch,
+                        int scan_type)
 {
-       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
        int i, j;
        u32 flags;
-       bool force_passive = !req->n_ssids;
-       u32 min_dwell_time_active, max_dwell_time_active, delta_per_probe;
+       bool force_passive = !n_ssids;
+       u32 min_dwell_time_active, max_dwell_time_active;
        u32 dwell_time_passive, dwell_time_dfs;
 
-       if (band == IEEE80211_BAND_5GHZ)
-               delta_per_probe = c->dwell_time_delta_per_probe_5;
-       else
-               delta_per_probe = c->dwell_time_delta_per_probe;
+       /* configure dwell times according to scan type */
+       if (scan_type == SCAN_TYPE_SEARCH) {
+               struct conf_scan_settings *c = &wl->conf.scan;
+               bool active_vif_exists = !!wlcore_count_started_vifs(wl);
+
+               min_dwell_time_active = active_vif_exists ?
+                       c->min_dwell_time_active :
+                       c->min_dwell_time_active_long;
+               max_dwell_time_active = active_vif_exists ?
+                       c->max_dwell_time_active :
+                       c->max_dwell_time_active_long;
+               dwell_time_passive = c->dwell_time_passive;
+               dwell_time_dfs = c->dwell_time_dfs;
+       } else {
+               struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
+               u32 delta_per_probe;
 
-       min_dwell_time_active = c->base_dwell_time +
-                req->n_ssids * c->num_probe_reqs * delta_per_probe;
+               if (band == IEEE80211_BAND_5GHZ)
+                       delta_per_probe = c->dwell_time_delta_per_probe_5;
+               else
+                       delta_per_probe = c->dwell_time_delta_per_probe;
 
-       max_dwell_time_active = min_dwell_time_active + c->max_dwell_time_delta;
+               min_dwell_time_active = c->base_dwell_time +
+                        n_ssids * c->num_probe_reqs * delta_per_probe;
 
+               max_dwell_time_active = min_dwell_time_active +
+                                       c->max_dwell_time_delta;
+               dwell_time_passive = c->dwell_time_passive;
+               dwell_time_dfs = c->dwell_time_dfs;
+       }
        min_dwell_time_active = DIV_ROUND_UP(min_dwell_time_active, 1000);
        max_dwell_time_active = DIV_ROUND_UP(max_dwell_time_active, 1000);
-       dwell_time_passive = DIV_ROUND_UP(c->dwell_time_passive, 1000);
-       dwell_time_dfs = DIV_ROUND_UP(c->dwell_time_dfs, 1000);
+       dwell_time_passive = DIV_ROUND_UP(dwell_time_passive, 1000);
+       dwell_time_dfs = DIV_ROUND_UP(dwell_time_dfs, 1000);
 
        for (i = 0, j = start;
-            i < req->n_channels && j < max_channels;
+            i < n_channels && j < max_channels;
             i++) {
-               flags = req->channels[i]->flags;
+               flags = req_channels[i]->flags;
 
                if (force_passive)
                        flags |= IEEE80211_CHAN_PASSIVE_SCAN;
 
-               if ((req->channels[i]->band == band) &&
+               if ((req_channels[i]->band == band) &&
                    !(flags & IEEE80211_CHAN_DISABLED) &&
                    (!!(flags & IEEE80211_CHAN_RADAR) == radar) &&
                    /* if radar is set, we ignore the passive flag */
                    (radar ||
                     !!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) {
                        wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
-                                    req->channels[i]->band,
-                                    req->channels[i]->center_freq);
+                                    req_channels[i]->band,
+                                    req_channels[i]->center_freq);
                        wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
-                                    req->channels[i]->hw_value,
-                                    req->channels[i]->flags);
+                                    req_channels[i]->hw_value,
+                                    req_channels[i]->flags);
                        wl1271_debug(DEBUG_SCAN, "max_power %d",
-                                    req->channels[i]->max_power);
+                                    req_channels[i]->max_power);
                        wl1271_debug(DEBUG_SCAN, "min_dwell_time %d max dwell time %d",
                                     min_dwell_time_active,
                                     max_dwell_time_active);
@@ -473,10 +201,11 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
                        channels[j].max_duration =
                                cpu_to_le16(max_dwell_time_active);
 
-                       channels[j].tx_power_att = req->channels[i]->max_power;
-                       channels[j].channel = req->channels[i]->hw_value;
+                       channels[j].tx_power_att = req_channels[i]->max_power;
+                       channels[j].channel = req_channels[i]->hw_value;
 
-                       if ((band == IEEE80211_BAND_2GHZ) &&
+                       if (n_pactive_ch &&
+                           (band == IEEE80211_BAND_2GHZ) &&
                            (channels[j].channel >= 12) &&
                            (channels[j].channel <= 14) &&
                            (flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
@@ -500,51 +229,80 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
        return j - start;
 }
 
-static bool
-wl1271_scan_sched_scan_channels(struct wl1271 *wl,
-                               struct cfg80211_sched_scan_request *req,
-                               struct wl1271_cmd_sched_scan_config *cfg)
+bool
+wlcore_set_scan_chan_params(struct wl1271 *wl,
+                           struct wlcore_scan_channels *cfg,
+                           struct ieee80211_channel *channels[],
+                           u32 n_channels,
+                           u32 n_ssids,
+                           int scan_type)
 {
        u8 n_pactive_ch = 0;
 
        cfg->passive[0] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
-                                                   IEEE80211_BAND_2GHZ,
-                                                   false, true, 0,
-                                                   MAX_CHANNELS_2GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_2,
+                                        IEEE80211_BAND_2GHZ,
+                                        false, true, 0,
+                                        MAX_CHANNELS_2GHZ,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->active[0] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
-                                                   IEEE80211_BAND_2GHZ,
-                                                   false, false,
-                                                   cfg->passive[0],
-                                                   MAX_CHANNELS_2GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_2,
+                                        IEEE80211_BAND_2GHZ,
+                                        false, false,
+                                        cfg->passive[0],
+                                        MAX_CHANNELS_2GHZ,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->passive[1] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
-                                                   IEEE80211_BAND_5GHZ,
-                                                   false, true, 0,
-                                                   MAX_CHANNELS_5GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_5,
+                                        IEEE80211_BAND_5GHZ,
+                                        false, true, 0,
+                                        wl->max_channels_5,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->dfs =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
-                                                   IEEE80211_BAND_5GHZ,
-                                                   true, true,
-                                                   cfg->passive[1],
-                                                   MAX_CHANNELS_5GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_5,
+                                        IEEE80211_BAND_5GHZ,
+                                        true, true,
+                                        cfg->passive[1],
+                                        wl->max_channels_5,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->active[1] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
-                                                   IEEE80211_BAND_5GHZ,
-                                                   false, false,
-                                                   cfg->passive[1] + cfg->dfs,
-                                                   MAX_CHANNELS_5GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_5,
+                                        IEEE80211_BAND_5GHZ,
+                                        false, false,
+                                        cfg->passive[1] + cfg->dfs,
+                                        wl->max_channels_5,
+                                        &n_pactive_ch,
+                                        scan_type);
+
        /* 802.11j channels are not supported yet */
        cfg->passive[2] = 0;
        cfg->active[2] = 0;
 
-       cfg->n_pactive_ch = n_pactive_ch;
+       cfg->passive_active = n_pactive_ch;
 
        wl1271_debug(DEBUG_SCAN, "    2.4GHz: active %d passive %d",
                     cfg->active[0], cfg->passive[0]);
@@ -556,10 +314,48 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
                cfg->passive[1] || cfg->active[1] || cfg->dfs ||
                cfg->passive[2] || cfg->active[2];
 }
+EXPORT_SYMBOL_GPL(wlcore_set_scan_chan_params);
+
+int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
+               const u8 *ssid, size_t ssid_len,
+               struct cfg80211_scan_request *req)
+{
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+
+       /*
+        * cfg80211 should guarantee that we don't get more channels
+        * than what we have registered.
+        */
+       BUG_ON(req->n_channels > WL1271_MAX_CHANNELS);
+
+       if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
+               return -EBUSY;
+
+       wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
+
+       if (ssid_len && ssid) {
+               wl->scan.ssid_len = ssid_len;
+               memcpy(wl->scan.ssid, ssid, ssid_len);
+       } else {
+               wl->scan.ssid_len = 0;
+       }
+
+       wl->scan_wlvif = wlvif;
+       wl->scan.req = req;
+       memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
+
+       /* we assume failure so that timeout scenarios are handled correctly */
+       wl->scan.failed = true;
+       ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                    msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
 
+       wl->ops->scan_start(wl, wlvif, req);
+
+       return 0;
+}
 /* Returns the scan type to be used or a negative value on error */
-static int
-wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
+int
+wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
                                 struct wl12xx_vif *wlvif,
                                 struct cfg80211_sched_scan_request *req)
 {
@@ -662,160 +458,12 @@ out:
                return ret;
        return type;
 }
+EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_ssid_list);
 
-int wl1271_scan_sched_scan_config(struct wl1271 *wl,
-                                 struct wl12xx_vif *wlvif,
-                                 struct cfg80211_sched_scan_request *req,
-                                 struct ieee80211_sched_scan_ies *ies)
-{
-       struct wl1271_cmd_sched_scan_config *cfg = NULL;
-       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
-       int i, ret;
-       bool force_passive = !req->n_ssids;
-
-       wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
-
-       cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
-       if (!cfg)
-               return -ENOMEM;
-
-       cfg->role_id = wlvif->role_id;
-       cfg->rssi_threshold = c->rssi_threshold;
-       cfg->snr_threshold  = c->snr_threshold;
-       cfg->n_probe_reqs = c->num_probe_reqs;
-       /* cycles set to 0 it means infinite (until manually stopped) */
-       cfg->cycles = 0;
-       /* report APs when at least 1 is found */
-       cfg->report_after = 1;
-       /* don't stop scanning automatically when something is found */
-       cfg->terminate = 0;
-       cfg->tag = WL1271_SCAN_DEFAULT_TAG;
-       /* don't filter on BSS type */
-       cfg->bss_type = SCAN_BSS_TYPE_ANY;
-       /* currently NL80211 supports only a single interval */
-       for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
-               cfg->intervals[i] = cpu_to_le32(req->interval);
-
-       cfg->ssid_len = 0;
-       ret = wl12xx_scan_sched_scan_ssid_list(wl, wlvif, req);
-       if (ret < 0)
-               goto out;
-
-       cfg->filter_type = ret;
-
-       wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
-
-       if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) {
-               wl1271_error("scan channel list is empty");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       if (!force_passive && cfg->active[0]) {
-               u8 band = IEEE80211_BAND_2GHZ;
-               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
-                                                wlvif->role_id, band,
-                                                req->ssids[0].ssid,
-                                                req->ssids[0].ssid_len,
-                                                ies->ie[band],
-                                                ies->len[band], true);
-               if (ret < 0) {
-                       wl1271_error("2.4GHz PROBE request template failed");
-                       goto out;
-               }
-       }
-
-       if (!force_passive && cfg->active[1]) {
-               u8 band = IEEE80211_BAND_5GHZ;
-               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
-                                                wlvif->role_id, band,
-                                                req->ssids[0].ssid,
-                                                req->ssids[0].ssid_len,
-                                                ies->ie[band],
-                                                ies->len[band], true);
-               if (ret < 0) {
-                       wl1271_error("5GHz PROBE request template failed");
-                       goto out;
-               }
-       }
-
-       wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg));
-
-       ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg,
-                             sizeof(*cfg), 0);
-       if (ret < 0) {
-               wl1271_error("SCAN configuration failed");
-               goto out;
-       }
-out:
-       kfree(cfg);
-       return ret;
-}
-
-int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
-{
-       struct wl1271_cmd_sched_scan_start *start;
-       int ret = 0;
-
-       wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
-
-       if (wlvif->bss_type != BSS_TYPE_STA_BSS)
-               return -EOPNOTSUPP;
-
-       if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) &&
-           test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
-               return -EBUSY;
-
-       start = kzalloc(sizeof(*start), GFP_KERNEL);
-       if (!start)
-               return -ENOMEM;
-
-       start->role_id = wlvif->role_id;
-       start->tag = WL1271_SCAN_DEFAULT_TAG;
-
-       ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
-                             sizeof(*start), 0);
-       if (ret < 0) {
-               wl1271_error("failed to send scan start command");
-               goto out_free;
-       }
-
-out_free:
-       kfree(start);
-       return ret;
-}
-
-void wl1271_scan_sched_scan_results(struct wl1271 *wl)
+void wlcore_scan_sched_scan_results(struct wl1271 *wl)
 {
        wl1271_debug(DEBUG_SCAN, "got periodic scan results");
 
        ieee80211_sched_scan_results(wl->hw);
 }
-
-void wl1271_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif)
-{
-       struct wl1271_cmd_sched_scan_stop *stop;
-       int ret = 0;
-
-       wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
-
-       /* FIXME: what to do if alloc'ing to stop fails? */
-       stop = kzalloc(sizeof(*stop), GFP_KERNEL);
-       if (!stop) {
-               wl1271_error("failed to alloc memory to send sched scan stop");
-               return;
-       }
-
-       stop->role_id = wlvif->role_id;
-       stop->tag = WL1271_SCAN_DEFAULT_TAG;
-
-       ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
-                             sizeof(*stop), 0);
-       if (ret < 0) {
-               wl1271_error("failed to send sched scan stop command");
-               goto out_free;
-       }
-
-out_free:
-       kfree(stop);
-}
+EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_results);
index 29f3c8d6b0468265dc55528a493eee7de9a8625b..a6ab24b5c0f96ab86278f71b93aae80a11048433 100644 (file)
 
 #include "wlcore.h"
 
-int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
+int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
                const u8 *ssid, size_t ssid_len,
                struct cfg80211_scan_request *req);
-int wl1271_scan_stop(struct wl1271 *wl);
 int wl1271_scan_build_probe_req(struct wl1271 *wl,
                                const u8 *ssid, size_t ssid_len,
                                const u8 *ie, size_t ie_len, u8 band);
-void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif);
+void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wl1271_scan_complete_work(struct work_struct *work);
 int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                                     struct wl12xx_vif *wlvif,
                                     struct cfg80211_sched_scan_request *req,
                                     struct ieee80211_sched_scan_ies *ies);
 int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-void wl1271_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif);
-void wl1271_scan_sched_scan_results(struct wl1271 *wl);
+void wlcore_scan_sched_scan_results(struct wl1271 *wl);
 
 #define WL1271_SCAN_MAX_CHANNELS       24
 #define WL1271_SCAN_DEFAULT_TAG        1
@@ -66,56 +64,6 @@ enum {
        WL1271_SCAN_STATE_DONE
 };
 
-struct basic_scan_params {
-       /* Scan option flags (WL1271_SCAN_OPT_*) */
-       __le16 scan_options;
-       u8 role_id;
-       /* Number of scan channels in the list (maximum 30) */
-       u8 n_ch;
-       /* This field indicates the number of probe requests to send
-          per channel for an active scan */
-       u8 n_probe_reqs;
-       u8 tid_trigger;
-       u8 ssid_len;
-       u8 use_ssid_list;
-
-       /* Rate bit field for sending the probes */
-       __le32 tx_rate;
-
-       u8 ssid[IEEE80211_MAX_SSID_LEN];
-       /* Band to scan */
-       u8 band;
-
-       u8 scan_tag;
-       u8 padding2[2];
-} __packed;
-
-struct basic_scan_channel_params {
-       /* Duration in TU to wait for frames on a channel for active scan */
-       __le32 min_duration;
-       __le32 max_duration;
-       __le32 bssid_lsb;
-       __le16 bssid_msb;
-       u8 early_termination;
-       u8 tx_power_att;
-       u8 channel;
-       /* FW internal use only! */
-       u8 dfs_candidate;
-       u8 activity_detected;
-       u8 pad;
-} __packed;
-
-struct wl1271_cmd_scan {
-       struct wl1271_cmd_header header;
-
-       struct basic_scan_params params;
-       struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
-
-       /* src mac address */
-       u8 addr[ETH_ALEN];
-       u8 padding[2];
-} __packed;
-
 struct wl1271_cmd_trigger_scan_to {
        struct wl1271_cmd_header header;
 
@@ -123,9 +71,17 @@ struct wl1271_cmd_trigger_scan_to {
 } __packed;
 
 #define MAX_CHANNELS_2GHZ      14
-#define MAX_CHANNELS_5GHZ      23
 #define MAX_CHANNELS_4GHZ      4
 
+/*
+ * This max value here is used only for the struct definition of
+ * wlcore_scan_channels. This struct is used by both 12xx
+ * and 18xx (which have different max 5ghz channels value).
+ * In order to make sure this is large enough, just use the
+ * max possible 5ghz channels.
+ */
+#define MAX_CHANNELS_5GHZ      42
+
 #define SCAN_MAX_CYCLE_INTERVALS 16
 #define SCAN_MAX_BANDS 3
 
@@ -160,43 +116,6 @@ struct conn_scan_ch_params {
        u8  padding[3];
 } __packed;
 
-struct wl1271_cmd_sched_scan_config {
-       struct wl1271_cmd_header header;
-
-       __le32 intervals[SCAN_MAX_CYCLE_INTERVALS];
-
-       s8 rssi_threshold; /* for filtering (in dBm) */
-       s8 snr_threshold;  /* for filtering (in dB) */
-
-       u8 cycles;       /* maximum number of scan cycles */
-       u8 report_after; /* report when this number of results are received */
-       u8 terminate;    /* stop scanning after reporting */
-
-       u8 tag;
-       u8 bss_type; /* for filtering */
-       u8 filter_type;
-
-       u8 ssid_len;     /* For SCAN_SSID_FILTER_SPECIFIC */
-       u8 ssid[IEEE80211_MAX_SSID_LEN];
-
-       u8 n_probe_reqs; /* Number of probes requests per channel */
-
-       u8 passive[SCAN_MAX_BANDS];
-       u8 active[SCAN_MAX_BANDS];
-
-       u8 dfs;
-
-       u8 n_pactive_ch; /* number of pactive (passive until fw detects energy)
-                           channels in BG band */
-       u8 role_id;
-       u8 padding[1];
-
-       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
-       struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
-       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
-} __packed;
-
-
 #define SCHED_SCAN_MAX_SSIDS 16
 
 enum {
@@ -220,21 +139,34 @@ struct wl1271_cmd_sched_scan_ssid_list {
        u8 padding[2];
 } __packed;
 
-struct wl1271_cmd_sched_scan_start {
-       struct wl1271_cmd_header header;
+struct wlcore_scan_channels {
+       u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */
+       u8 active[SCAN_MAX_BANDS];  /* number of active scan channels */
+       u8 dfs;            /* number of dfs channels in 5ghz */
+       u8 passive_active; /* number of passive before active channels 2.4ghz */
 
-       u8 tag;
-       u8 role_id;
-       u8 padding[2];
-} __packed;
-
-struct wl1271_cmd_sched_scan_stop {
-       struct wl1271_cmd_header header;
+       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+       struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
+       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+};
 
-       u8 tag;
-       u8 role_id;
-       u8 padding[2];
-} __packed;
+enum {
+       SCAN_TYPE_SEARCH        = 0,
+       SCAN_TYPE_PERIODIC      = 1,
+       SCAN_TYPE_TRACKING      = 2,
+};
 
+bool
+wlcore_set_scan_chan_params(struct wl1271 *wl,
+                           struct wlcore_scan_channels *cfg,
+                           struct ieee80211_channel *channels[],
+                           u32 n_channels,
+                           u32 n_ssids,
+                           int scan_type);
+
+int
+wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
+                                struct wl12xx_vif *wlvif,
+                                struct cfg80211_sched_scan_request *req);
 
 #endif /* __WL1271_SCAN_H__ */
index 646f703ae739b3b84ebff7316aea31faf8fadfc2..198028df6f4b98bb4514f667daec3ba3fe520065 100644 (file)
@@ -217,7 +217,7 @@ static struct wl1271_if_operations sdio_ops = {
 static int wl1271_probe(struct sdio_func *func,
                                  const struct sdio_device_id *id)
 {
-       struct wl12xx_platform_data *wlan_data;
+       struct wlcore_platdev_data *pdev_data;
        struct wl12xx_sdio_glue *glue;
        struct resource res[1];
        mmc_pm_flag_t mmcflags;
@@ -228,10 +228,18 @@ static int wl1271_probe(struct sdio_func *func,
        if (func->num != 0x02)
                return -ENODEV;
 
+       pdev_data = kzalloc(sizeof(*pdev_data), GFP_KERNEL);
+       if (!pdev_data) {
+               dev_err(&func->dev, "can't allocate platdev_data\n");
+               goto out;
+       }
+
+       pdev_data->if_ops = &sdio_ops;
+
        glue = kzalloc(sizeof(*glue), GFP_KERNEL);
        if (!glue) {
                dev_err(&func->dev, "can't allocate glue\n");
-               goto out;
+               goto out_free_pdev_data;
        }
 
        glue->dev = &func->dev;
@@ -242,9 +250,9 @@ static int wl1271_probe(struct sdio_func *func,
        /* Use block mode for transferring over one block size of data */
        func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
 
-       wlan_data = wl12xx_get_platform_data();
-       if (IS_ERR(wlan_data)) {
-               ret = PTR_ERR(wlan_data);
+       pdev_data->pdata = wl12xx_get_platform_data();
+       if (IS_ERR(pdev_data->pdata)) {
+               ret = PTR_ERR(pdev_data->pdata);
                dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
                goto out_free_glue;
        }
@@ -254,9 +262,7 @@ static int wl1271_probe(struct sdio_func *func,
        dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags);
 
        if (mmcflags & MMC_PM_KEEP_POWER)
-               wlan_data->pwr_in_suspend = true;
-
-       wlan_data->ops = &sdio_ops;
+               pdev_data->pdata->pwr_in_suspend = true;
 
        sdio_set_drvdata(func, glue);
 
@@ -274,7 +280,7 @@ static int wl1271_probe(struct sdio_func *func,
        else
                chip_family = "wl12xx";
 
-       glue->core = platform_device_alloc(chip_family, -1);
+       glue->core = platform_device_alloc(chip_family, PLATFORM_DEVID_AUTO);
        if (!glue->core) {
                dev_err(glue->dev, "can't allocate platform_device");
                ret = -ENOMEM;
@@ -285,7 +291,7 @@ static int wl1271_probe(struct sdio_func *func,
 
        memset(res, 0x00, sizeof(res));
 
-       res[0].start = wlan_data->irq;
+       res[0].start = pdev_data->pdata->irq;
        res[0].flags = IORESOURCE_IRQ;
        res[0].name = "irq";
 
@@ -295,8 +301,8 @@ static int wl1271_probe(struct sdio_func *func,
                goto out_dev_put;
        }
 
-       ret = platform_device_add_data(glue->core, wlan_data,
-                                      sizeof(*wlan_data));
+       ret = platform_device_add_data(glue->core, pdev_data,
+                                      sizeof(*pdev_data));
        if (ret) {
                dev_err(glue->dev, "can't add platform data\n");
                goto out_dev_put;
@@ -315,6 +321,9 @@ out_dev_put:
 out_free_glue:
        kfree(glue);
 
+out_free_pdev_data:
+       kfree(pdev_data);
+
 out:
        return ret;
 }
@@ -326,8 +335,7 @@ static void wl1271_remove(struct sdio_func *func)
        /* Undo decrement done above in wl1271_probe */
        pm_runtime_get_noresume(&func->dev);
 
-       platform_device_del(glue->core);
-       platform_device_put(glue->core);
+       platform_device_unregister(glue->core);
        kfree(glue);
 }
 
index f06f4770ce029076b8e622fed5285b0167ef7aaa..5ad2e100e59b1b650be4b1502da43e5b5995575d 100644 (file)
@@ -270,7 +270,7 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
                                             void *buf, size_t len, bool fixed)
 {
        struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
-       struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
+       struct spi_transfer t[2 * (WSPI_MAX_NUM_OF_CHUNKS + 1)];
        struct spi_message m;
        u32 commands[WSPI_MAX_NUM_OF_CHUNKS];
        u32 *cmd;
@@ -327,22 +327,29 @@ static struct wl1271_if_operations spi_ops = {
 static int wl1271_probe(struct spi_device *spi)
 {
        struct wl12xx_spi_glue *glue;
-       struct wl12xx_platform_data *pdata;
+       struct wlcore_platdev_data *pdev_data;
        struct resource res[1];
        int ret = -ENOMEM;
 
-       pdata = spi->dev.platform_data;
-       if (!pdata) {
+       pdev_data = kzalloc(sizeof(*pdev_data), GFP_KERNEL);
+       if (!pdev_data) {
+               dev_err(&spi->dev, "can't allocate platdev_data\n");
+               goto out;
+       }
+
+       pdev_data->pdata = spi->dev.platform_data;
+       if (!pdev_data->pdata) {
                dev_err(&spi->dev, "no platform data\n");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto out_free_pdev_data;
        }
 
-       pdata->ops = &spi_ops;
+       pdev_data->if_ops = &spi_ops;
 
        glue = kzalloc(sizeof(*glue), GFP_KERNEL);
        if (!glue) {
                dev_err(&spi->dev, "can't allocate glue\n");
-               goto out;
+               goto out_free_pdev_data;
        }
 
        glue->dev = &spi->dev;
@@ -359,7 +366,7 @@ static int wl1271_probe(struct spi_device *spi)
                goto out_free_glue;
        }
 
-       glue->core = platform_device_alloc("wl12xx", -1);
+       glue->core = platform_device_alloc("wl12xx", PLATFORM_DEVID_AUTO);
        if (!glue->core) {
                dev_err(glue->dev, "can't allocate platform_device\n");
                ret = -ENOMEM;
@@ -380,7 +387,8 @@ static int wl1271_probe(struct spi_device *spi)
                goto out_dev_put;
        }
 
-       ret = platform_device_add_data(glue->core, pdata, sizeof(*pdata));
+       ret = platform_device_add_data(glue->core, pdev_data,
+                                      sizeof(*pdev_data));
        if (ret) {
                dev_err(glue->dev, "can't add platform data\n");
                goto out_dev_put;
@@ -399,6 +407,10 @@ out_dev_put:
 
 out_free_glue:
        kfree(glue);
+
+out_free_pdev_data:
+       kfree(pdev_data);
+
 out:
        return ret;
 }
@@ -407,8 +419,7 @@ static int wl1271_remove(struct spi_device *spi)
 {
        struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
 
-       platform_device_del(glue->core);
-       platform_device_put(glue->core);
+       platform_device_unregister(glue->core);
        kfree(glue);
 
        return 0;
index a90d3cd094089c82fe60db12dca55bedaa33af0f..004d02e71f01a25eb3abf9496fa752f79af3df7d 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/etherdevice.h>
+#include <linux/spinlock.h>
 
 #include "wlcore.h"
 #include "debug.h"
@@ -104,7 +105,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
                                    struct wl12xx_vif *wlvif,
                                    u8 hlid)
 {
-       bool fw_ps, single_sta;
+       bool fw_ps;
        u8 tx_pkts;
 
        if (WARN_ON(!test_bit(hlid, wlvif->links_map)))
@@ -112,15 +113,19 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
 
        fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
        tx_pkts = wl->links[hlid].allocated_pkts;
-       single_sta = (wl->active_sta_count == 1);
 
        /*
         * if in FW PS and there is enough data in FW we can put the link
         * into high-level PS and clean out its TX queues.
-        * Make an exception if this is the only connected station. In this
-        * case FW-memory congestion is not a problem.
+        * Make an exception if this is the only connected link. In this
+        * case FW-memory congestion is less of a problem.
+        * Note that a single connected STA means 3 active links, since we must
+        * account for the global and broadcast AP links. The "fw_ps" check
+        * assures us the third link is a STA connected to the AP. Otherwise
+        * the FW would not set the PSM bit.
         */
-       if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
+       if (wl->active_link_count > 3 && fw_ps &&
+           tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
@@ -155,21 +160,18 @@ static u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                      struct sk_buff *skb, struct ieee80211_sta *sta)
 {
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
-       if (!wlvif || wl12xx_is_dummy_packet(wl, skb))
-               return wl->system_hlid;
+       struct ieee80211_tx_info *control;
 
        if (wlvif->bss_type == BSS_TYPE_AP_BSS)
                return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta);
 
-       if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
-            test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) &&
-           !ieee80211_is_auth(hdr->frame_control) &&
-           !ieee80211_is_assoc_req(hdr->frame_control))
-               return wlvif->sta.hlid;
-       else
+       control = IEEE80211_SKB_CB(skb);
+       if (control->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+               wl1271_debug(DEBUG_TX, "tx offchannel");
                return wlvif->dev_hlid;
+       }
+
+       return wlvif->sta.hlid;
 }
 
 unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
@@ -224,9 +226,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
                wl->tx_allocated_pkts[ac]++;
 
-               if (!wl12xx_is_dummy_packet(wl, skb) && wlvif &&
-                   wlvif->bss_type == BSS_TYPE_AP_BSS &&
-                   test_bit(hlid, wlvif->ap.sta_hlid_map))
+               if (test_bit(hlid, wl->links_map))
                        wl->links[hlid].allocated_pkts++;
 
                ret = 0;
@@ -293,9 +293,14 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
                tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ;
        } else if (wlvif) {
+               u8 session_id = wl->session_ids[hlid];
+
+               if ((wl->quirks & WLCORE_QUIRK_AP_ZERO_SESSION_ID) &&
+                   (wlvif->bss_type == BSS_TYPE_AP_BSS))
+                       session_id = 0;
+
                /* configure the tx attributes */
-               tx_attr = wlvif->session_counter <<
-                         TX_HW_ATTR_OFST_SESSION_COUNTER;
+               tx_attr = session_id << TX_HW_ATTR_OFST_SESSION_COUNTER;
        }
 
        desc->hlid = hlid;
@@ -452,20 +457,22 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
 void wl1271_handle_tx_low_watermark(struct wl1271 *wl)
 {
        int i;
+       struct wl12xx_vif *wlvif;
 
-       for (i = 0; i < NUM_TX_QUEUES; i++) {
-               if (wlcore_is_queue_stopped_by_reason(wl, i,
-                       WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
-                   wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) {
-                       /* firmware buffer has space, restart queues */
-                       wlcore_wake_queue(wl, i,
-                                         WLCORE_QUEUE_STOP_REASON_WATERMARK);
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               for (i = 0; i < NUM_TX_QUEUES; i++) {
+                       if (wlcore_is_queue_stopped_by_reason(wl, wlvif, i,
+                                       WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
+                           wlvif->tx_queue_count[i] <=
+                                       WL1271_TX_QUEUE_LOW_WATERMARK)
+                               /* firmware buffer has space, restart queues */
+                               wlcore_wake_queue(wl, wlvif, i,
+                                       WLCORE_QUEUE_STOP_REASON_WATERMARK);
                }
        }
 }
 
-static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
-                                               struct sk_buff_head *queues)
+static int wlcore_select_ac(struct wl1271 *wl)
 {
        int i, q = -1, ac;
        u32 min_pkts = 0xffffffff;
@@ -479,45 +486,60 @@ static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
         */
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                ac = wl1271_tx_get_queue(i);
-               if (!skb_queue_empty(&queues[ac]) &&
-                   (wl->tx_allocated_pkts[ac] < min_pkts)) {
+               if (wl->tx_queue_count[ac] &&
+                   wl->tx_allocated_pkts[ac] < min_pkts) {
                        q = ac;
                        min_pkts = wl->tx_allocated_pkts[q];
                }
        }
 
-       if (q == -1)
-               return NULL;
-
-       return &queues[q];
+       return q;
 }
 
-static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
-                                             struct wl1271_link *lnk)
+static struct sk_buff *wlcore_lnk_dequeue(struct wl1271 *wl,
+                                         struct wl1271_link *lnk, u8 q)
 {
        struct sk_buff *skb;
        unsigned long flags;
-       struct sk_buff_head *queue;
 
-       queue = wl1271_select_queue(wl, lnk->tx_queue);
-       if (!queue)
-               return NULL;
-
-       skb = skb_dequeue(queue);
+       skb = skb_dequeue(&lnk->tx_queue[q]);
        if (skb) {
-               int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
                spin_lock_irqsave(&wl->wl_lock, flags);
                WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
                wl->tx_queue_count[q]--;
+               if (lnk->wlvif) {
+                       WARN_ON_ONCE(lnk->wlvif->tx_queue_count[q] <= 0);
+                       lnk->wlvif->tx_queue_count[q]--;
+               }
                spin_unlock_irqrestore(&wl->wl_lock, flags);
        }
 
        return skb;
 }
 
-static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
-                                             struct wl12xx_vif *wlvif,
-                                             u8 *hlid)
+static struct sk_buff *wlcore_lnk_dequeue_high_prio(struct wl1271 *wl,
+                                                   u8 hlid, u8 ac,
+                                                   u8 *low_prio_hlid)
+{
+       struct wl1271_link *lnk = &wl->links[hlid];
+
+       if (!wlcore_hw_lnk_high_prio(wl, hlid, lnk)) {
+               if (*low_prio_hlid == WL12XX_INVALID_LINK_ID &&
+                   !skb_queue_empty(&lnk->tx_queue[ac]) &&
+                   wlcore_hw_lnk_low_prio(wl, hlid, lnk))
+                       /* we found the first non-empty low priority queue */
+                       *low_prio_hlid = hlid;
+
+               return NULL;
+       }
+
+       return wlcore_lnk_dequeue(wl, lnk, ac);
+}
+
+static struct sk_buff *wlcore_vif_dequeue_high_prio(struct wl1271 *wl,
+                                                   struct wl12xx_vif *wlvif,
+                                                   u8 ac, u8 *hlid,
+                                                   u8 *low_prio_hlid)
 {
        struct sk_buff *skb = NULL;
        int i, h, start_hlid;
@@ -533,7 +555,8 @@ static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
                if (!test_bit(h, wlvif->links_map))
                        continue;
 
-               skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[h]);
+               skb = wlcore_lnk_dequeue_high_prio(wl, h, ac,
+                                                  low_prio_hlid);
                if (!skb)
                        continue;
 
@@ -553,42 +576,75 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
        unsigned long flags;
        struct wl12xx_vif *wlvif = wl->last_wlvif;
        struct sk_buff *skb = NULL;
+       int ac;
+       u8 low_prio_hlid = WL12XX_INVALID_LINK_ID;
+
+       ac = wlcore_select_ac(wl);
+       if (ac < 0)
+               goto out;
 
        /* continue from last wlvif (round robin) */
        if (wlvif) {
                wl12xx_for_each_wlvif_continue(wl, wlvif) {
-                       skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
-                       if (skb) {
-                               wl->last_wlvif = wlvif;
-                               break;
-                       }
+                       if (!wlvif->tx_queue_count[ac])
+                               continue;
+
+                       skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
+                                                          &low_prio_hlid);
+                       if (!skb)
+                               continue;
+
+                       wl->last_wlvif = wlvif;
+                       break;
                }
        }
 
        /* dequeue from the system HLID before the restarting wlvif list */
        if (!skb) {
-               skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
-               *hlid = wl->system_hlid;
+               skb = wlcore_lnk_dequeue_high_prio(wl, wl->system_hlid,
+                                                  ac, &low_prio_hlid);
+               if (skb) {
+                       *hlid = wl->system_hlid;
+                       wl->last_wlvif = NULL;
+               }
        }
 
-       /* do a new pass over the wlvif list */
+       /* Do a new pass over the wlvif list. But no need to continue
+        * after last_wlvif. The previous pass should have found it. */
        if (!skb) {
                wl12xx_for_each_wlvif(wl, wlvif) {
-                       skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
+                       if (!wlvif->tx_queue_count[ac])
+                               goto next;
+
+                       skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
+                                                          &low_prio_hlid);
                        if (skb) {
                                wl->last_wlvif = wlvif;
                                break;
                        }
 
-                       /*
-                        * No need to continue after last_wlvif. The previous
-                        * pass should have found it.
-                        */
+next:
                        if (wlvif == wl->last_wlvif)
                                break;
                }
        }
 
+       /* no high priority skbs found - but maybe a low priority one? */
+       if (!skb && low_prio_hlid != WL12XX_INVALID_LINK_ID) {
+               struct wl1271_link *lnk = &wl->links[low_prio_hlid];
+               skb = wlcore_lnk_dequeue(wl, lnk, ac);
+
+               WARN_ON(!skb); /* we checked this before */
+               *hlid = low_prio_hlid;
+
+               /* ensure proper round robin in the vif/link levels */
+               wl->last_wlvif = lnk->wlvif;
+               if (lnk->wlvif)
+                       lnk->wlvif->last_tx_hlid = low_prio_hlid;
+
+       }
+
+out:
        if (!skb &&
            test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
                int q;
@@ -623,6 +679,8 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        spin_lock_irqsave(&wl->wl_lock, flags);
        wl->tx_queue_count[q]++;
+       if (wlvif)
+               wlvif->tx_queue_count[q]++;
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
 
@@ -699,7 +757,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
                bool has_data = false;
 
                wlvif = NULL;
-               if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif)
+               if (!wl12xx_is_dummy_packet(wl, skb))
                        wlvif = wl12xx_vif_to_data(info->control.vif);
                else
                        hlid = wl->system_hlid;
@@ -875,25 +933,6 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
 
        wl->stats.retry_count += result->ack_failures;
 
-       /*
-        * update sequence number only when relevant, i.e. only in
-        * sessions of TKIP, AES and GEM (not in open or WEP sessions)
-        */
-       if (info->control.hw_key &&
-           (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP ||
-            info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP ||
-            info->control.hw_key->cipher == WL1271_CIPHER_SUITE_GEM)) {
-               u8 fw_lsb = result->tx_security_sequence_number_lsb;
-               u8 cur_lsb = wlvif->tx_security_last_seq_lsb;
-
-               /*
-                * update security sequence number, taking care of potential
-                * wrap-around
-                */
-               wlvif->tx_security_seq += (fw_lsb - cur_lsb) & 0xff;
-               wlvif->tx_security_last_seq_lsb = fw_lsb;
-       }
-
        /* remove private header from packet */
        skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
 
@@ -972,10 +1011,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
        unsigned long flags;
        struct ieee80211_tx_info *info;
        int total[NUM_TX_QUEUES];
+       struct wl1271_link *lnk = &wl->links[hlid];
 
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                total[i] = 0;
-               while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
+               while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
                        wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb);
 
                        if (!wl12xx_is_dummy_packet(wl, skb)) {
@@ -990,8 +1030,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
        }
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-       for (i = 0; i < NUM_TX_QUEUES; i++)
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
                wl->tx_queue_count[i] -= total[i];
+               if (lnk->wlvif)
+                       lnk->wlvif->tx_queue_count[i] -= total[i];
+       }
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
        wl1271_handle_tx_low_watermark(wl);
@@ -1004,16 +1047,19 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 
        /* TX failure */
        for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) {
-               if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
+                   i != wlvif->ap.bcast_hlid && i != wlvif->ap.global_hlid) {
+                       /* this calls wl12xx_free_link */
                        wl1271_free_sta(wl, wlvif, i);
-               else
-                       wlvif->sta.ba_rx_bitmap = 0;
-
-               wl->links[i].allocated_pkts = 0;
-               wl->links[i].prev_freed_pkts = 0;
+               } else {
+                       u8 hlid = i;
+                       wl12xx_free_link(wl, wlvif, &hlid);
+               }
        }
        wlvif->last_tx_hlid = 0;
 
+       for (i = 0; i < NUM_TX_QUEUES; i++)
+               wlvif->tx_queue_count[i] = 0;
 }
 /* caller must hold wl->mutex and TX must be stopped */
 void wl12xx_tx_reset(struct wl1271 *wl)
@@ -1023,7 +1069,7 @@ void wl12xx_tx_reset(struct wl1271 *wl)
        struct ieee80211_tx_info *info;
 
        /* only reset the queues if something bad happened */
-       if (WARN_ON_ONCE(wl1271_tx_total_queue_count(wl) != 0)) {
+       if (wl1271_tx_total_queue_count(wl) != 0) {
                for (i = 0; i < WL12XX_MAX_LINKS; i++)
                        wl1271_tx_reset_link_queues(wl, i);
 
@@ -1135,45 +1181,48 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
 
        return BIT(__ffs(rate_set));
 }
+EXPORT_SYMBOL_GPL(wl1271_tx_min_rate_get);
 
-void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
-                             enum wlcore_queue_stop_reason reason)
+void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 queue, enum wlcore_queue_stop_reason reason)
 {
-       bool stopped = !!wl->queue_stop_reasons[queue];
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+       bool stopped = !!wl->queue_stop_reasons[hwq];
 
        /* queue should not be stopped for this reason */
-       WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue]));
+       WARN_ON_ONCE(test_and_set_bit(reason, &wl->queue_stop_reasons[hwq]));
 
        if (stopped)
                return;
 
-       ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
+       ieee80211_stop_queue(wl->hw, hwq);
 }
 
-void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
+void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-       wlcore_stop_queue_locked(wl, queue, reason);
+       wlcore_stop_queue_locked(wl, wlvif, queue, reason);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
 
-void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
+void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason)
 {
        unsigned long flags;
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
 
        spin_lock_irqsave(&wl->wl_lock, flags);
 
        /* queue should not be clear for this reason */
-       WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue]));
+       WARN_ON_ONCE(!test_and_clear_bit(reason, &wl->queue_stop_reasons[hwq]));
 
-       if (wl->queue_stop_reasons[queue])
+       if (wl->queue_stop_reasons[hwq])
                goto out;
 
-       ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
+       ieee80211_wake_queue(wl->hw, hwq);
 
 out:
        spin_unlock_irqrestore(&wl->wl_lock, flags);
@@ -1183,48 +1232,74 @@ void wlcore_stop_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason)
 {
        int i;
+       unsigned long flags;
 
-       for (i = 0; i < NUM_TX_QUEUES; i++)
-               wlcore_stop_queue(wl, i, reason);
+       spin_lock_irqsave(&wl->wl_lock, flags);
+
+       /* mark all possible queues as stopped */
+        for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++)
+                WARN_ON_ONCE(test_and_set_bit(reason,
+                                             &wl->queue_stop_reasons[i]));
+
+       /* use the global version to make sure all vifs in mac80211 we don't
+        * know are stopped.
+        */
+       ieee80211_stop_queues(wl->hw);
+
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
-EXPORT_SYMBOL_GPL(wlcore_stop_queues);
 
 void wlcore_wake_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason)
 {
        int i;
+       unsigned long flags;
 
-       for (i = 0; i < NUM_TX_QUEUES; i++)
-               wlcore_wake_queue(wl, i, reason);
+       spin_lock_irqsave(&wl->wl_lock, flags);
+
+       /* mark all possible queues as awake */
+        for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++)
+               WARN_ON_ONCE(!test_and_clear_bit(reason,
+                                                &wl->queue_stop_reasons[i]));
+
+       /* use the global version to make sure all vifs in mac80211 we don't
+        * know are woken up.
+        */
+       ieee80211_wake_queues(wl->hw);
+
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
-EXPORT_SYMBOL_GPL(wlcore_wake_queues);
 
-void wlcore_reset_stopped_queues(struct wl1271 *wl)
+bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif, u8 queue,
+                                      enum wlcore_queue_stop_reason reason)
 {
-       int i;
        unsigned long flags;
+       bool stopped;
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-
-       for (i = 0; i < NUM_TX_QUEUES; i++) {
-               if (!wl->queue_stop_reasons[i])
-                       continue;
-
-               wl->queue_stop_reasons[i] = 0;
-               ieee80211_wake_queue(wl->hw,
-                                    wl1271_tx_get_mac80211_queue(i));
-       }
-
+       stopped = wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, queue,
+                                                          reason);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+       return stopped;
 }
 
-bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
-                            enum wlcore_queue_stop_reason reason)
+bool wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif, u8 queue,
+                                      enum wlcore_queue_stop_reason reason)
 {
-       return test_bit(reason, &wl->queue_stop_reasons[queue]);
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+
+       assert_spin_locked(&wl->wl_lock);
+       return test_bit(reason, &wl->queue_stop_reasons[hwq]);
 }
 
-bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue)
+bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                   u8 queue)
 {
-       return !!wl->queue_stop_reasons[queue];
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+
+       assert_spin_locked(&wl->wl_lock);
+       return !!wl->queue_stop_reasons[hwq];
 }
index 349520d8b7240686b2e7ffa266c4f9019c5ce481..55aa4acf9105a4e703036b0b12250ae19a9a7f81 100644 (file)
@@ -207,19 +207,22 @@ static inline int wl1271_tx_get_queue(int queue)
        }
 }
 
-static inline int wl1271_tx_get_mac80211_queue(int queue)
+static inline
+int wlcore_tx_get_mac80211_queue(struct wl12xx_vif *wlvif, int queue)
 {
+       int mac_queue = wlvif->hw_queue_base;
+
        switch (queue) {
        case CONF_TX_AC_VO:
-               return 0;
+               return mac_queue + 0;
        case CONF_TX_AC_VI:
-               return 1;
+               return mac_queue + 1;
        case CONF_TX_AC_BE:
-               return 2;
+               return mac_queue + 2;
        case CONF_TX_AC_BK:
-               return 3;
+               return mac_queue + 3;
        default:
-               return 2;
+               return mac_queue + 2;
        }
 }
 
@@ -252,20 +255,26 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
 unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
                                          unsigned int packet_length);
 void wl1271_free_tx_id(struct wl1271 *wl, int id);
-void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
-                             enum wlcore_queue_stop_reason reason);
-void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
+void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 queue, enum wlcore_queue_stop_reason reason);
+void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason);
-void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
+void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason);
 void wlcore_stop_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason);
 void wlcore_wake_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason);
-void wlcore_reset_stopped_queues(struct wl1271 *wl);
-bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
+bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif, u8 queue,
                                       enum wlcore_queue_stop_reason reason);
-bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue);
+bool
+wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl,
+                                        struct wl12xx_vif *wlvif,
+                                        u8 queue,
+                                        enum wlcore_queue_stop_reason reason);
+bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                   u8 queue);
 
 /* from main.c */
 void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
index c3884937c007cf049b6efe0c5d158008972a7bf9..0034979e97cbaa0316dfd2a62db2e5be59b7f058 100644 (file)
@@ -37,6 +37,9 @@
  */
 #define WLCORE_NUM_MAC_ADDRESSES 3
 
+/* wl12xx/wl18xx maximum transmission power (in dBm) */
+#define WLCORE_MAX_TXPWR        25
+
 /* forward declaration */
 struct wl1271_tx_hw_descr;
 enum wl_rx_buf_align;
@@ -51,6 +54,9 @@ struct wlcore_ops {
        int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr,
                           void *buf, size_t len);
        int (*ack_event)(struct wl1271 *wl);
+       int (*wait_for_event)(struct wl1271 *wl, enum wlcore_wait_event event,
+                             bool *timeout);
+       int (*process_mailbox_events)(struct wl1271 *wl);
        u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks);
        void (*set_tx_desc_blocks)(struct wl1271 *wl,
                                   struct wl1271_tx_hw_descr *desc,
@@ -82,12 +88,32 @@ struct wlcore_ops {
        int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir);
        int (*handle_static_data)(struct wl1271 *wl,
                                  struct wl1271_static_data *static_data);
+       int (*scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                         struct cfg80211_scan_request *req);
+       int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+       int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                               struct cfg80211_sched_scan_request *req,
+                               struct ieee80211_sched_scan_ies *ies);
+       void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
        int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
        int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
                       struct ieee80211_vif *vif,
                       struct ieee80211_sta *sta,
                       struct ieee80211_key_conf *key_conf);
+       int (*channel_switch)(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch);
        u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len);
+       void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             struct ieee80211_sta *sta, u32 changed);
+       int (*set_peer_cap)(struct wl1271 *wl,
+                           struct ieee80211_sta_ht_cap *ht_cap,
+                           bool allow_ht_operation,
+                           u32 rate_set, u8 hlid);
+       bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid,
+                             struct wl1271_link *lnk);
+       bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
+                            struct wl1271_link *lnk);
 };
 
 enum wlcore_partitions {
@@ -157,7 +183,6 @@ struct wl1271 {
 
        struct wl1271_if_operations *if_ops;
 
-       void (*set_power)(bool enable);
        int irq;
 
        spinlock_t wl_lock;
@@ -202,6 +227,8 @@ struct wl1271 {
        unsigned long klv_templates_map[
                        BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)];
 
+       u8 session_ids[WL12XX_MAX_LINKS];
+
        struct list_head wlvif_list;
 
        u8 sta_count;
@@ -227,7 +254,8 @@ struct wl1271 {
 
        /* Frames scheduled for transmission, not handled yet */
        int tx_queue_count[NUM_TX_QUEUES];
-       unsigned long queue_stop_reasons[NUM_TX_QUEUES];
+       unsigned long queue_stop_reasons[
+                               NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES];
 
        /* Frames received, not handled yet by mac80211 */
        struct sk_buff_head deferred_rx_queue;
@@ -269,24 +297,30 @@ struct wl1271 {
        struct work_struct recovery_work;
        bool watchdog_recovery;
 
+       /* Reg domain last configuration */
+       u32 reg_ch_conf_last[2];
+       /* Reg domain pending configuration */
+       u32 reg_ch_conf_pending[2];
+
        /* Pointer that holds DMA-friendly block for the mailbox */
-       struct event_mailbox *mbox;
+       void *mbox;
 
        /* The mbox event mask */
        u32 event_mask;
 
        /* Mailbox pointers */
+       u32 mbox_size;
        u32 mbox_ptr[2];
 
        /* Are we currently scanning */
-       struct ieee80211_vif *scan_vif;
+       struct wl12xx_vif *scan_wlvif;
        struct wl1271_scan scan;
        struct delayed_work scan_complete_work;
 
-       /* Connection loss work */
-       struct delayed_work connection_loss_work;
+       struct ieee80211_vif *roc_vif;
+       struct delayed_work roc_complete_work;
 
-       bool sched_scanning;
+       struct wl12xx_vif *sched_vif;
 
        /* The current band */
        enum ieee80211_band band;
@@ -299,7 +333,7 @@ struct wl1271 {
 
        struct wl1271_stats stats;
 
-       __le32 buffer_32;
+       __le32 *buffer_32;
        u32 buffer_cmd;
        u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
 
@@ -314,6 +348,8 @@ struct wl1271 {
 
        bool enable_11a;
 
+       int recovery_count;
+
        /* Most recently reported noise in dBm */
        s8 noise;
 
@@ -333,6 +369,12 @@ struct wl1271 {
         */
        struct wl1271_link links[WL12XX_MAX_LINKS];
 
+       /* number of currently active links */
+       int active_link_count;
+
+       /* Fast/slow links bitmap according to FW */
+       u32 fw_fast_lnk_map;
+
        /* AP-mode - a bitmap of links currently in PS mode according to FW */
        u32 ap_fw_ps_map;
 
@@ -348,6 +390,9 @@ struct wl1271 {
        /* number of currently active RX BA sessions */
        int ba_rx_session_count;
 
+       /* Maximum number of supported RX BA sessions */
+       int ba_rx_session_count_max;
+
        /* AP-mode - number of currently connected stations */
        int active_sta_count;
 
@@ -367,6 +412,12 @@ struct wl1271 {
        const char *sr_fw_name;
        const char *mr_fw_name;
 
+       u8 scan_templ_id_2_4;
+       u8 scan_templ_id_5;
+       u8 sched_scan_templ_id_2_4;
+       u8 sched_scan_templ_id_5;
+       u8 max_channels_5;
+
        /* per-chip-family private structure */
        void *priv;
 
@@ -408,20 +459,28 @@ struct wl1271 {
        /* the number of allocated MAC addresses in this chip */
        int num_mac_addr;
 
-       /* the minimum FW version required for the driver to work */
-       unsigned int min_fw_ver[NUM_FW_VER];
+       /* minimum FW version required for the driver to work in single-role */
+       unsigned int min_sr_fw_ver[NUM_FW_VER];
+
+       /* minimum FW version required for the driver to work in multi-role */
+       unsigned int min_mr_fw_ver[NUM_FW_VER];
 
        struct completion nvs_loading_complete;
+
+       /* number of concurrent channels the HW supports */
+       u32 num_channels;
 };
 
 int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
 int wlcore_remove(struct platform_device *pdev);
-struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size);
+struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
+                                    u32 mbox_size);
 int wlcore_free_hw(struct wl1271 *wl);
 int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                   struct ieee80211_vif *vif,
                   struct ieee80211_sta *sta,
                   struct ieee80211_key_conf *key_conf);
+void wlcore_regdomain_config(struct wl1271 *wl);
 
 static inline void
 wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
@@ -430,16 +489,27 @@ wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
        memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap));
 }
 
+/* Tell wlcore not to care about this element when checking the version */
+#define WLCORE_FW_VER_IGNORE   -1
+
 static inline void
 wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
-                     unsigned int iftype, unsigned int major,
-                     unsigned int subtype, unsigned int minor)
+                     unsigned int iftype_sr, unsigned int major_sr,
+                     unsigned int subtype_sr, unsigned int minor_sr,
+                     unsigned int iftype_mr, unsigned int major_mr,
+                     unsigned int subtype_mr, unsigned int minor_mr)
 {
-       wl->min_fw_ver[FW_VER_CHIP] = chip;
-       wl->min_fw_ver[FW_VER_IF_TYPE] = iftype;
-       wl->min_fw_ver[FW_VER_MAJOR] = major;
-       wl->min_fw_ver[FW_VER_SUBTYPE] = subtype;
-       wl->min_fw_ver[FW_VER_MINOR] = minor;
+       wl->min_sr_fw_ver[FW_VER_CHIP] = chip;
+       wl->min_sr_fw_ver[FW_VER_IF_TYPE] = iftype_sr;
+       wl->min_sr_fw_ver[FW_VER_MAJOR] = major_sr;
+       wl->min_sr_fw_ver[FW_VER_SUBTYPE] = subtype_sr;
+       wl->min_sr_fw_ver[FW_VER_MINOR] = minor_sr;
+
+       wl->min_mr_fw_ver[FW_VER_CHIP] = chip;
+       wl->min_mr_fw_ver[FW_VER_IF_TYPE] = iftype_mr;
+       wl->min_mr_fw_ver[FW_VER_MAJOR] = major_mr;
+       wl->min_mr_fw_ver[FW_VER_SUBTYPE] = subtype_mr;
+       wl->min_mr_fw_ver[FW_VER_MINOR] = minor_mr;
 }
 
 /* Firmware image load chunk size */
@@ -450,6 +520,9 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
 /* Each RX/TX transaction requires an end-of-transaction transfer */
 #define WLCORE_QUIRK_END_OF_TRANSACTION                BIT(0)
 
+/* the first start_role(sta) sometimes doesn't work on wl12xx */
+#define WLCORE_QUIRK_START_STA_FAILS           BIT(1)
+
 /* wl127x and SPI don't support SDIO block size alignment */
 #define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN                BIT(2)
 
@@ -462,9 +535,6 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
 /* Older firmwares use an old NVS format */
 #define WLCORE_QUIRK_LEGACY_NVS                        BIT(5)
 
-/* Some firmwares may not support ELP */
-#define WLCORE_QUIRK_NO_ELP                    BIT(6)
-
 /* pad only the last frame in the aggregate buffer */
 #define WLCORE_QUIRK_TX_PAD_LAST_FRAME         BIT(7)
 
@@ -477,11 +547,11 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
 /* separate probe response templates for one-shot and sched scans */
 #define WLCORE_QUIRK_DUAL_PROBE_TMPL           BIT(10)
 
-/* TODO: move to the lower drivers when all usages are abstracted */
-#define CHIP_ID_1271_PG10              (0x4030101)
-#define CHIP_ID_1271_PG20              (0x4030111)
-#define CHIP_ID_1283_PG10              (0x05030101)
-#define CHIP_ID_1283_PG20              (0x05030111)
+/* Firmware requires reg domain configuration for active calibration */
+#define WLCORE_QUIRK_REGDOMAIN_CONF            BIT(11)
+
+/* The FW only support a zero session id for AP */
+#define WLCORE_QUIRK_AP_ZERO_SESSION_ID                BIT(12)
 
 /* TODO: move all these common registers and values elsewhere */
 #define HW_ACCESS_ELP_CTRL_REG         0x1FFFC
index 6678d4b18611556be7617b6ff79dab0dad851d14..7b55ef9c428807cce1a43a0e78ada8b14bae7f03 100644 (file)
@@ -109,17 +109,6 @@ enum {
        NUM_FW_VER
 };
 
-#define FW_VER_CHIP_WL127X 6
-#define FW_VER_CHIP_WL128X 7
-
-#define FW_VER_IF_TYPE_STA 1
-#define FW_VER_IF_TYPE_AP  2
-
-#define FW_VER_MINOR_1_SPARE_STA_MIN 58
-#define FW_VER_MINOR_1_SPARE_AP_MIN  47
-
-#define FW_VER_MINOR_FWLOG_STA_MIN 70
-
 struct wl1271_chip {
        u32 id;
        char fw_ver_str[ETHTOOL_BUSINFO_LEN];
@@ -141,7 +130,10 @@ struct wl_fw_packet_counters {
        /* Cumulative counter of released Voice memory blocks */
        u8 tx_voice_released_blks;
 
-       u8 padding[3];
+       /* Tx rate of the last transmitted packet */
+       u8 tx_last_rate;
+
+       u8 padding[2];
 } __packed;
 
 /* FW status registers */
@@ -214,6 +206,11 @@ struct wl1271_if_operations {
        void (*set_block_size) (struct device *child, unsigned int blksz);
 };
 
+struct wlcore_platdev_data {
+       struct wl12xx_platform_data *pdata;
+       struct wl1271_if_operations *if_ops;
+};
+
 #define MAX_NUM_KEYS 14
 #define MAX_KEY_SIZE 32
 
@@ -260,6 +257,8 @@ enum wl12xx_vif_flags {
        WLVIF_FLAG_IN_USE,
 };
 
+struct wl12xx_vif;
+
 struct wl1271_link {
        /* AP-mode - TX queue per AC in link */
        struct sk_buff_head tx_queue[NUM_TX_QUEUES];
@@ -272,6 +271,16 @@ struct wl1271_link {
 
        /* bitmap of TIDs where RX BA sessions are active for this link */
        u8 ba_bitmap;
+
+       /* The wlvif this link belongs to. Might be null for global links */
+       struct wl12xx_vif *wlvif;
+
+       /*
+        * total freed FW packets on the link - used for tracking the
+        * AES/TKIP PN across recoveries. Re-initialized each time
+        * from the wl1271_station structure.
+        */
+       u64 total_freed_pkts;
 };
 
 #define WL1271_MAX_RX_FILTERS 5
@@ -315,6 +324,14 @@ struct wl12xx_rx_filter {
 
 struct wl1271_station {
        u8 hlid;
+       bool in_connection;
+
+       /*
+        * total freed FW packets on the link to the STA - used for tracking the
+        * AES/TKIP PN across recoveries. Re-initialized each time from the
+        * wl1271_station structure.
+        */
+       u64 total_freed_pkts;
 };
 
 struct wl12xx_vif {
@@ -332,7 +349,6 @@ struct wl12xx_vif {
        union {
                struct {
                        u8 hlid;
-                       u8 ba_rx_bitmap;
 
                        u8 basic_rate_idx;
                        u8 ap_rate_idx;
@@ -341,6 +357,8 @@ struct wl12xx_vif {
                        u8 klv_template_id;
 
                        bool qos;
+                       /* channel type we started the STA role with */
+                       enum nl80211_channel_type role_chan_type;
                } sta;
                struct {
                        u8 global_hlid;
@@ -362,6 +380,9 @@ struct wl12xx_vif {
        /* the hlid of the last transmitted skb */
        int last_tx_hlid;
 
+       /* counters of packets per AC, across all links in the vif */
+       int tx_queue_count[NUM_TX_QUEUES];
+
        unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
 
        u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
@@ -396,9 +417,6 @@ struct wl12xx_vif {
        /* Our association ID */
        u16 aid;
 
-       /* Session counter for the chipset */
-       int session_counter;
-
        /* retry counter for PSM entries */
        u8 psm_entry_retry;
 
@@ -416,11 +434,28 @@ struct wl12xx_vif {
        bool ba_support;
        bool ba_allowed;
 
+       bool wmm_enabled;
+
        /* Rx Streaming */
        struct work_struct rx_streaming_enable_work;
        struct work_struct rx_streaming_disable_work;
        struct timer_list rx_streaming_timer;
 
+       struct delayed_work channel_switch_work;
+       struct delayed_work connection_loss_work;
+
+       /* number of in connection stations */
+       int inconn_count;
+
+       /*
+        * This vif's queues are mapped to mac80211 HW queues as:
+        * VO - hw_queue_base
+        * VI - hw_queue_base + 1
+        * BE - hw_queue_base + 2
+        * BK - hw_queue_base + 3
+        */
+       int hw_queue_base;
+
        /*
         * This struct must be last!
         * data that has to be saved acrossed reconfigs (e.g. recovery)
@@ -428,21 +463,21 @@ struct wl12xx_vif {
         */
        struct {
                u8 persistent[0];
+
                /*
-                * Security sequence number
-                *     bits 0-15: lower 16 bits part of sequence number
-                *     bits 16-47: higher 32 bits part of sequence number
-                *     bits 48-63: not in use
+                * total freed FW packets on the link - used for
+                * storing the AES/TKIP PN during recovery, as this
+                * structure is not zeroed out.
+                * For STA this holds the PN of the link to the AP.
+                * For AP this holds the PN of the broadcast link.
                 */
-               u64 tx_security_seq;
-
-               /* 8 bits of the last sequence number in use */
-               u8 tx_security_last_seq_lsb;
+               u64 total_freed_pkts;
        };
 };
 
 static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif)
 {
+       WARN_ON(!vif);
        return (struct wl12xx_vif *)vif->drv_priv;
 }
 
index ec857676c39ffaffcb6d55c25917ec9beb1192ea..80c728b28828dc538ca19d5e3ef9f756ee968568 100644 (file)
@@ -5,19 +5,6 @@
 menu "Near Field Communication (NFC) devices"
        depends on NFC
 
-config PN544_HCI_NFC
-       tristate "HCI PN544 NFC driver"
-       depends on I2C && NFC_HCI && NFC_SHDLC
-       select CRC_CCITT
-       default n
-       ---help---
-         NXP PN544 i2c driver.
-         This is a driver based on the SHDLC and HCI NFC kernel layers and
-         will thus not work with NXP libnfc library.
-
-         To compile this driver as a module, choose m here. The module will
-         be called pn544_hci.
-
 config NFC_PN533
        tristate "NXP PN533 USB driver"
        depends on USB
@@ -39,4 +26,6 @@ config NFC_WILINK
          Say Y here to compile support for Texas Instrument's NFC WiLink driver
          into the kernel or say M to compile it as module.
 
+source "drivers/nfc/pn544/Kconfig"
+
 endmenu
index 36c359043f5469b8d59b4235d9cea35db79d779e..574bbc04d97a46154d1a6e2874f6d3f803f5081f 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for nfc devices
 #
 
-obj-$(CONFIG_PN544_HCI_NFC)    += pn544/
+obj-$(CONFIG_NFC_PN544)                += pn544/
 obj-$(CONFIG_NFC_PN533)                += pn533.o
 obj-$(CONFIG_NFC_WILINK)       += nfcwilink.o
 
index 50b1ee41afc60e2a3789f1a4e26ab12ff811ce4e..3b731acbc408fbc807ea8ffa4eef9f0dc622e97f 100644 (file)
@@ -526,7 +526,7 @@ static int nfcwilink_probe(struct platform_device *pdev)
 
        nfc_dev_dbg(&pdev->dev, "probe entry");
 
-       drv = kzalloc(sizeof(struct nfcwilink), GFP_KERNEL);
+       drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL);
        if (!drv) {
                rc = -ENOMEM;
                goto exit;
@@ -542,12 +542,13 @@ static int nfcwilink_probe(struct platform_device *pdev)
 
        drv->ndev = nci_allocate_device(&nfcwilink_ops,
                                        protocols,
+                                       NFC_SE_NONE,
                                        NFCWILINK_HDR_LEN,
                                        0);
        if (!drv->ndev) {
                nfc_dev_err(&pdev->dev, "nci_allocate_device failed");
                rc = -ENOMEM;
-               goto free_exit;
+               goto exit;
        }
 
        nci_set_parent_dev(drv->ndev, &pdev->dev);
@@ -566,9 +567,6 @@ static int nfcwilink_probe(struct platform_device *pdev)
 free_dev_exit:
        nci_free_device(drv->ndev);
 
-free_exit:
-       kfree(drv);
-
 exit:
        return rc;
 }
@@ -588,8 +586,6 @@ static int nfcwilink_remove(struct platform_device *pdev)
        nci_unregister_device(ndev);
        nci_free_device(ndev);
 
-       kfree(drv);
-
        dev_set_drvdata(&pdev->dev, NULL);
 
        return 0;
index ada681b01a17be24ecfa93b8c0ddf518c32c5c1c..f696318cfb512eb80c70623ee4e9d23edf27abbf 100644 (file)
 #define SONY_VENDOR_ID         0x054c
 #define PASORI_PRODUCT_ID      0x02e1
 
-#define PN533_QUIRKS_TYPE_A          BIT(0)
-#define PN533_QUIRKS_TYPE_F          BIT(1)
-#define PN533_QUIRKS_DEP             BIT(2)
-#define PN533_QUIRKS_RAW_EXCHANGE    BIT(3)
-
 #define PN533_DEVICE_STD    0x1
 #define PN533_DEVICE_PASORI 0x2
 
@@ -84,14 +79,18 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_LISTEN_TIME 2
 
 /* frame definitions */
-#define PN533_NORMAL_FRAME_MAX_LEN 262  /* 6   (PREAMBLE, SOF, LEN, LCS, TFI)
-                                          254 (DATA)
-                                          2   (DCS, postamble) */
-
-#define PN533_FRAME_TAIL_SIZE 2
-#define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \
-                               PN533_FRAME_TAIL_SIZE)
-#define PN533_FRAME_ACK_SIZE (sizeof(struct pn533_frame) + 1)
+#define PN533_FRAME_HEADER_LEN (sizeof(struct pn533_frame) \
+                                       + 2) /* data[0] TFI, data[1] CC */
+#define PN533_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/
+
+/*
+ * Max extended frame payload len, excluding TFI and CC
+ * which are already in PN533_FRAME_HEADER_LEN.
+ */
+#define PN533_FRAME_MAX_PAYLOAD_LEN 263
+
+#define PN533_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2),
+                                 Postamble (1) */
 #define PN533_FRAME_CHECKSUM(f) (f->data[f->datalen])
 #define PN533_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1])
 
@@ -105,8 +104,6 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 
 /* PN533 Commands */
 #define PN533_FRAME_CMD(f) (f->data[1])
-#define PN533_FRAME_CMD_PARAMS_PTR(f) (&f->data[2])
-#define PN533_FRAME_CMD_PARAMS_LEN(f) (f->datalen - 2)
 
 #define PN533_CMD_GET_FIRMWARE_VERSION 0x02
 #define PN533_CMD_RF_CONFIGURATION 0x32
@@ -120,6 +117,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_CMD_TG_INIT_AS_TARGET 0x8c
 #define PN533_CMD_TG_GET_DATA 0x86
 #define PN533_CMD_TG_SET_DATA 0x8e
+#define PN533_CMD_UNDEF 0xff
 
 #define PN533_CMD_RESPONSE(cmd) (cmd + 1)
 
@@ -128,13 +126,12 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_CMD_MI_MASK 0x40
 #define PN533_CMD_RET_SUCCESS 0x00
 
-/* PN533 status codes */
-#define PN533_STATUS_TARGET_RELEASED 0x29
-
 struct pn533;
 
-typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg,
-                                       u8 *params, int params_len);
+typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg, int status);
+
+typedef int (*pn533_send_async_complete_t) (struct pn533 *dev, void *arg,
+                                       struct sk_buff *resp);
 
 /* structs for pn533 commands */
 
@@ -282,11 +279,6 @@ const struct pn533_poll_modulations poll_mod[] = {
 
 /* PN533_CMD_IN_ATR */
 
-struct pn533_cmd_activate_param {
-       u8 tg;
-       u8 next;
-} __packed;
-
 struct pn533_cmd_activate_response {
        u8 status;
        u8 nfcid3t[10];
@@ -299,14 +291,6 @@ struct pn533_cmd_activate_response {
        u8 gt[];
 } __packed;
 
-/* PN533_CMD_IN_JUMP_FOR_DEP */
-struct pn533_cmd_jump_dep {
-       u8 active;
-       u8 baud;
-       u8 next;
-       u8 data[];
-} __packed;
-
 struct pn533_cmd_jump_dep_response {
        u8 status;
        u8 tg;
@@ -329,32 +313,13 @@ struct pn533_cmd_jump_dep_response {
 #define PN533_INIT_TARGET_RESP_ACTIVE     0x1
 #define PN533_INIT_TARGET_RESP_DEP        0x4
 
-struct pn533_cmd_init_target {
-       u8 mode;
-       u8 mifare[6];
-       u8 felica[18];
-       u8 nfcid3[10];
-       u8 gb_len;
-       u8 gb[];
-} __packed;
-
-struct pn533_cmd_init_target_response {
-       u8 mode;
-       u8 cmd[];
-} __packed;
-
 struct pn533 {
        struct usb_device *udev;
        struct usb_interface *interface;
        struct nfc_dev *nfc_dev;
 
        struct urb *out_urb;
-       int out_maxlen;
-       struct pn533_frame *out_frame;
-
        struct urb *in_urb;
-       int in_maxlen;
-       struct pn533_frame *in_frame;
 
        struct sk_buff_head resp_q;
 
@@ -365,12 +330,12 @@ struct pn533 {
        struct work_struct mi_work;
        struct work_struct tg_work;
        struct timer_list listen_timer;
-       struct pn533_frame *wq_in_frame;
        int wq_in_error;
        int cancel_listen;
 
        pn533_cmd_complete_t cmd_complete;
        void *cmd_complete_arg;
+       void *cmd_complete_mi_arg;
        struct mutex cmd_lock;
        u8 cmd;
 
@@ -391,16 +356,17 @@ struct pn533 {
 
        struct list_head cmd_queue;
        u8 cmd_pending;
+
+       struct pn533_frame_ops *ops;
 };
 
 struct pn533_cmd {
        struct list_head queue;
-       struct pn533_frame *out_frame;
-       struct pn533_frame *in_frame;
-       int in_frame_len;
-       pn533_cmd_complete_t cmd_complete;
+       u8 cmd_code;
+       struct sk_buff *req;
+       struct sk_buff *resp;
+       int resp_len;
        void *arg;
-       gfp_t flags;
 };
 
 struct pn533_frame {
@@ -411,6 +377,22 @@ struct pn533_frame {
        u8 data[];
 } __packed;
 
+struct pn533_frame_ops {
+       void (*tx_frame_init)(void *frame, u8 cmd_code);
+       void (*tx_frame_finish)(void *frame);
+       void (*tx_update_payload_len)(void *frame, int len);
+       int tx_header_len;
+       int tx_tail_len;
+
+       bool (*rx_is_frame_valid)(void *frame);
+       int (*rx_frame_size)(void *frame);
+       int rx_header_len;
+       int rx_tail_len;
+
+       int max_payload_len;
+       u8 (*get_cmd_code)(void *frame);
+};
+
 /* The rule: value + checksum = 0 */
 static inline u8 pn533_checksum(u8 value)
 {
@@ -429,37 +411,21 @@ static u8 pn533_data_checksum(u8 *data, int datalen)
        return pn533_checksum(sum);
 }
 
-/**
- * pn533_tx_frame_ack - create a ack frame
- * @frame:     The frame to be set as ack
- *
- * Ack is different type of standard frame. As a standard frame, it has
- * preamble and start_frame. However the checksum of this frame must fail,
- * i.e. datalen + datalen_checksum must NOT be zero. When the checksum test
- * fails and datalen = 0 and datalen_checksum = 0xFF, the frame is a ack.
- * After datalen_checksum field, the postamble is placed.
- */
-static void pn533_tx_frame_ack(struct pn533_frame *frame)
+static void pn533_tx_frame_init(void *_frame, u8 cmd_code)
 {
-       frame->preamble = 0;
-       frame->start_frame = cpu_to_be16(PN533_SOF);
-       frame->datalen = 0;
-       frame->datalen_checksum = 0xFF;
-       /* data[0] is used as postamble */
-       frame->data[0] = 0;
-}
+       struct pn533_frame *frame = _frame;
 
-static void pn533_tx_frame_init(struct pn533_frame *frame, u8 cmd)
-{
        frame->preamble = 0;
        frame->start_frame = cpu_to_be16(PN533_SOF);
        PN533_FRAME_IDENTIFIER(frame) = PN533_DIR_OUT;
-       PN533_FRAME_CMD(frame) = cmd;
+       PN533_FRAME_CMD(frame) = cmd_code;
        frame->datalen = 2;
 }
 
-static void pn533_tx_frame_finish(struct pn533_frame *frame)
+static void pn533_tx_frame_finish(void *_frame)
 {
+       struct pn533_frame *frame = _frame;
+
        frame->datalen_checksum = pn533_checksum(frame->datalen);
 
        PN533_FRAME_CHECKSUM(frame) =
@@ -468,9 +434,17 @@ static void pn533_tx_frame_finish(struct pn533_frame *frame)
        PN533_FRAME_POSTAMBLE(frame) = 0;
 }
 
-static bool pn533_rx_frame_is_valid(struct pn533_frame *frame)
+static void pn533_tx_update_payload_len(void *_frame, int len)
+{
+       struct pn533_frame *frame = _frame;
+
+       frame->datalen += len;
+}
+
+static bool pn533_rx_frame_is_valid(void *_frame)
 {
        u8 checksum;
+       struct pn533_frame *frame = _frame;
 
        if (frame->start_frame != cpu_to_be16(PN533_SOF))
                return false;
@@ -497,28 +471,48 @@ static bool pn533_rx_frame_is_ack(struct pn533_frame *frame)
        return true;
 }
 
-static bool pn533_rx_frame_is_cmd_response(struct pn533_frame *frame, u8 cmd)
+static inline int pn533_rx_frame_size(void *frame)
+{
+       struct pn533_frame *f = frame;
+
+       return sizeof(struct pn533_frame) + f->datalen + PN533_FRAME_TAIL_LEN;
+}
+
+static u8 pn533_get_cmd_code(void *frame)
+{
+       struct pn533_frame *f = frame;
+
+       return PN533_FRAME_CMD(f);
+}
+
+struct pn533_frame_ops pn533_std_frame_ops = {
+       .tx_frame_init = pn533_tx_frame_init,
+       .tx_frame_finish = pn533_tx_frame_finish,
+       .tx_update_payload_len = pn533_tx_update_payload_len,
+       .tx_header_len = PN533_FRAME_HEADER_LEN,
+       .tx_tail_len = PN533_FRAME_TAIL_LEN,
+
+       .rx_is_frame_valid = pn533_rx_frame_is_valid,
+       .rx_frame_size = pn533_rx_frame_size,
+       .rx_header_len = PN533_FRAME_HEADER_LEN,
+       .rx_tail_len = PN533_FRAME_TAIL_LEN,
+
+       .max_payload_len =  PN533_FRAME_MAX_PAYLOAD_LEN,
+       .get_cmd_code = pn533_get_cmd_code,
+};
+
+static bool pn533_rx_frame_is_cmd_response(struct pn533 *dev, void *frame)
 {
-       return (PN533_FRAME_CMD(frame) == PN533_CMD_RESPONSE(cmd));
+       return (dev->ops->get_cmd_code(frame) == PN533_CMD_RESPONSE(dev->cmd));
 }
 
 
 static void pn533_wq_cmd_complete(struct work_struct *work)
 {
        struct pn533 *dev = container_of(work, struct pn533, cmd_complete_work);
-       struct pn533_frame *in_frame;
        int rc;
 
-       in_frame = dev->wq_in_frame;
-
-       if (dev->wq_in_error)
-               rc = dev->cmd_complete(dev, dev->cmd_complete_arg, NULL,
-                                                       dev->wq_in_error);
-       else
-               rc = dev->cmd_complete(dev, dev->cmd_complete_arg,
-                                       PN533_FRAME_CMD_PARAMS_PTR(in_frame),
-                                       PN533_FRAME_CMD_PARAMS_LEN(in_frame));
-
+       rc = dev->cmd_complete(dev, dev->cmd_complete_arg, dev->wq_in_error);
        if (rc != -EINPROGRESS)
                queue_work(dev->wq, &dev->cmd_work);
 }
@@ -526,46 +520,47 @@ static void pn533_wq_cmd_complete(struct work_struct *work)
 static void pn533_recv_response(struct urb *urb)
 {
        struct pn533 *dev = urb->context;
-       struct pn533_frame *in_frame;
-
-       dev->wq_in_frame = NULL;
+       u8 *in_frame;
 
        switch (urb->status) {
        case 0:
-               /* success */
-               break;
+               break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-       case -ESHUTDOWN:
-               nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with"
-                                               " status: %d", urb->status);
+               nfc_dev_dbg(&dev->interface->dev,
+                           "The urb has been canceled (status %d)",
+                           urb->status);
                dev->wq_in_error = urb->status;
                goto sched_wq;
+               break;
+       case -ESHUTDOWN:
        default:
-               nfc_dev_err(&dev->interface->dev, "Nonzero urb status received:"
-                                                       " %d", urb->status);
+               nfc_dev_err(&dev->interface->dev,
+                           "Urb failure (status %d)", urb->status);
                dev->wq_in_error = urb->status;
                goto sched_wq;
        }
 
        in_frame = dev->in_urb->transfer_buffer;
 
-       if (!pn533_rx_frame_is_valid(in_frame)) {
+       nfc_dev_dbg(&dev->interface->dev, "Received a frame.");
+       print_hex_dump(KERN_DEBUG, "PN533 RX: ", DUMP_PREFIX_NONE, 16, 1,
+                      in_frame, dev->ops->rx_frame_size(in_frame), false);
+
+       if (!dev->ops->rx_is_frame_valid(in_frame)) {
                nfc_dev_err(&dev->interface->dev, "Received an invalid frame");
                dev->wq_in_error = -EIO;
                goto sched_wq;
        }
 
-       if (!pn533_rx_frame_is_cmd_response(in_frame, dev->cmd)) {
-               nfc_dev_err(&dev->interface->dev, "The received frame is not "
-                                               "response to the last command");
+       if (!pn533_rx_frame_is_cmd_response(dev, in_frame)) {
+               nfc_dev_err(&dev->interface->dev,
+                           "It it not the response to the last command");
                dev->wq_in_error = -EIO;
                goto sched_wq;
        }
 
-       nfc_dev_dbg(&dev->interface->dev, "Received a valid frame");
        dev->wq_in_error = 0;
-       dev->wq_in_frame = in_frame;
 
 sched_wq:
        queue_work(dev->wq, &dev->cmd_complete_work);
@@ -586,18 +581,19 @@ static void pn533_recv_ack(struct urb *urb)
 
        switch (urb->status) {
        case 0:
-               /* success */
-               break;
+               break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-       case -ESHUTDOWN:
-               nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with"
-                                               " status: %d", urb->status);
+               nfc_dev_dbg(&dev->interface->dev,
+                           "The urb has been stopped (status %d)",
+                           urb->status);
                dev->wq_in_error = urb->status;
                goto sched_wq;
+               break;
+       case -ESHUTDOWN:
        default:
-               nfc_dev_err(&dev->interface->dev, "Nonzero urb status received:"
-                                                       " %d", urb->status);
+               nfc_dev_err(&dev->interface->dev,
+                           "Urb failure (status %d)", urb->status);
                dev->wq_in_error = urb->status;
                goto sched_wq;
        }
@@ -610,12 +606,10 @@ static void pn533_recv_ack(struct urb *urb)
                goto sched_wq;
        }
 
-       nfc_dev_dbg(&dev->interface->dev, "Received a valid ack");
-
        rc = pn533_submit_urb_for_response(dev, GFP_ATOMIC);
        if (rc) {
-               nfc_dev_err(&dev->interface->dev, "usb_submit_urb failed with"
-                                                       " result %d", rc);
+               nfc_dev_err(&dev->interface->dev,
+                           "usb_submit_urb failed with result %d", rc);
                dev->wq_in_error = rc;
                goto sched_wq;
        }
@@ -623,7 +617,6 @@ static void pn533_recv_ack(struct urb *urb)
        return;
 
 sched_wq:
-       dev->wq_in_frame = NULL;
        queue_work(dev->wq, &dev->cmd_complete_work);
 }
 
@@ -636,47 +629,46 @@ static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags)
 
 static int pn533_send_ack(struct pn533 *dev, gfp_t flags)
 {
+       u8 ack[PN533_FRAME_ACK_SIZE] = {0x00, 0x00, 0xff, 0x00, 0xff, 0x00};
+       /* spec 7.1.1.3:  Preamble, SoPC (2), ACK Code (2), Postamble */
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       pn533_tx_frame_ack(dev->out_frame);
-
-       dev->out_urb->transfer_buffer = dev->out_frame;
-       dev->out_urb->transfer_buffer_length = PN533_FRAME_ACK_SIZE;
+       dev->out_urb->transfer_buffer = ack;
+       dev->out_urb->transfer_buffer_length = sizeof(ack);
        rc = usb_submit_urb(dev->out_urb, flags);
 
        return rc;
 }
 
-static int __pn533_send_cmd_frame_async(struct pn533 *dev,
-                                       struct pn533_frame *out_frame,
-                                       struct pn533_frame *in_frame,
-                                       int in_frame_len,
+static int __pn533_send_frame_async(struct pn533 *dev,
+                                       struct sk_buff *out,
+                                       struct sk_buff *in,
+                                       int in_len,
                                        pn533_cmd_complete_t cmd_complete,
-                                       void *arg, gfp_t flags)
+                                       void *arg)
 {
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x",
-                                               PN533_FRAME_CMD(out_frame));
-
-       dev->cmd = PN533_FRAME_CMD(out_frame);
+       dev->cmd = dev->ops->get_cmd_code(out->data);
        dev->cmd_complete = cmd_complete;
        dev->cmd_complete_arg = arg;
 
-       dev->out_urb->transfer_buffer = out_frame;
-       dev->out_urb->transfer_buffer_length =
-                               PN533_FRAME_SIZE(out_frame);
+       dev->out_urb->transfer_buffer = out->data;
+       dev->out_urb->transfer_buffer_length = out->len;
 
-       dev->in_urb->transfer_buffer = in_frame;
-       dev->in_urb->transfer_buffer_length = in_frame_len;
+       dev->in_urb->transfer_buffer = in->data;
+       dev->in_urb->transfer_buffer_length = in_len;
 
-       rc = usb_submit_urb(dev->out_urb, flags);
+       print_hex_dump(KERN_DEBUG, "PN533 TX: ", DUMP_PREFIX_NONE, 16, 1,
+                      out->data, out->len, false);
+
+       rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
        if (rc)
                return rc;
 
-       rc = pn533_submit_urb_for_ack(dev, flags);
+       rc = pn533_submit_urb_for_ack(dev, GFP_KERNEL);
        if (rc)
                goto error;
 
@@ -687,146 +679,325 @@ error:
        return rc;
 }
 
-static void pn533_wq_cmd(struct work_struct *work)
+static void pn533_build_cmd_frame(struct pn533 *dev, u8 cmd_code,
+                                 struct sk_buff *skb)
 {
-       struct pn533 *dev = container_of(work, struct pn533, cmd_work);
-       struct pn533_cmd *cmd;
+       /* payload is already there, just update datalen */
+       int payload_len = skb->len;
+       struct pn533_frame_ops *ops = dev->ops;
 
-       mutex_lock(&dev->cmd_lock);
 
-       if (list_empty(&dev->cmd_queue)) {
-               dev->cmd_pending = 0;
-               mutex_unlock(&dev->cmd_lock);
-               return;
-       }
+       skb_push(skb, ops->tx_header_len);
+       skb_put(skb, ops->tx_tail_len);
 
-       cmd = list_first_entry(&dev->cmd_queue, struct pn533_cmd, queue);
+       ops->tx_frame_init(skb->data, cmd_code);
+       ops->tx_update_payload_len(skb->data, payload_len);
+       ops->tx_frame_finish(skb->data);
+}
 
-       list_del(&cmd->queue);
+struct pn533_send_async_complete_arg {
+       pn533_send_async_complete_t  complete_cb;
+       void *complete_cb_context;
+       struct sk_buff *resp;
+       struct sk_buff *req;
+};
 
-       mutex_unlock(&dev->cmd_lock);
+static int pn533_send_async_complete(struct pn533 *dev, void *_arg, int status)
+{
+       struct pn533_send_async_complete_arg *arg = _arg;
 
-       __pn533_send_cmd_frame_async(dev, cmd->out_frame, cmd->in_frame,
-                                    cmd->in_frame_len, cmd->cmd_complete,
-                                    cmd->arg, cmd->flags);
+       struct sk_buff *req = arg->req;
+       struct sk_buff *resp = arg->resp;
 
-       kfree(cmd);
+       int rc;
+
+       dev_kfree_skb(req);
+
+       if (status < 0) {
+               arg->complete_cb(dev, arg->complete_cb_context,
+                                ERR_PTR(status));
+               dev_kfree_skb(resp);
+               kfree(arg);
+               return status;
+       }
+
+       skb_put(resp, dev->ops->rx_frame_size(resp->data));
+       skb_pull(resp, dev->ops->rx_header_len);
+       skb_trim(resp, resp->len - dev->ops->rx_tail_len);
+
+       rc = arg->complete_cb(dev, arg->complete_cb_context, resp);
+
+       kfree(arg);
+       return rc;
 }
 
-static int pn533_send_cmd_frame_async(struct pn533 *dev,
-                                       struct pn533_frame *out_frame,
-                                       struct pn533_frame *in_frame,
-                                       int in_frame_len,
-                                       pn533_cmd_complete_t cmd_complete,
-                                       void *arg, gfp_t flags)
+static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
+                             struct sk_buff *req, struct sk_buff *resp,
+                             int resp_len,
+                             pn533_send_async_complete_t complete_cb,
+                             void *complete_cb_context)
 {
        struct pn533_cmd *cmd;
+       struct pn533_send_async_complete_arg *arg;
        int rc = 0;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x", cmd_code);
+
+       arg = kzalloc(sizeof(*arg), GFP_KERNEL);
+       if (!arg)
+               return -ENOMEM;
+
+       arg->complete_cb = complete_cb;
+       arg->complete_cb_context = complete_cb_context;
+       arg->resp = resp;
+       arg->req = req;
+
+       pn533_build_cmd_frame(dev, cmd_code, req);
 
        mutex_lock(&dev->cmd_lock);
 
        if (!dev->cmd_pending) {
-               rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
-                                                 in_frame_len, cmd_complete,
-                                                 arg, flags);
-               if (!rc)
-                       dev->cmd_pending = 1;
+               rc = __pn533_send_frame_async(dev, req, resp, resp_len,
+                                             pn533_send_async_complete, arg);
+               if (rc)
+                       goto error;
 
+               dev->cmd_pending = 1;
                goto unlock;
        }
 
-       nfc_dev_dbg(&dev->interface->dev, "%s Queueing command", __func__);
+       nfc_dev_dbg(&dev->interface->dev, "%s Queueing command 0x%x", __func__,
+                   cmd_code);
 
-       cmd = kzalloc(sizeof(struct pn533_cmd), flags);
+       cmd = kzalloc(sizeof(struct pn533_cmd), GFP_KERNEL);
        if (!cmd) {
                rc = -ENOMEM;
-               goto unlock;
+               goto error;
        }
 
        INIT_LIST_HEAD(&cmd->queue);
-       cmd->out_frame = out_frame;
-       cmd->in_frame = in_frame;
-       cmd->in_frame_len = in_frame_len;
-       cmd->cmd_complete = cmd_complete;
+       cmd->cmd_code = cmd_code;
+       cmd->req = req;
+       cmd->resp = resp;
+       cmd->resp_len = resp_len;
        cmd->arg = arg;
-       cmd->flags = flags;
 
        list_add_tail(&cmd->queue, &dev->cmd_queue);
 
+       goto unlock;
+
+error:
+       kfree(arg);
 unlock:
        mutex_unlock(&dev->cmd_lock);
+       return rc;
+}
+
+static int pn533_send_data_async(struct pn533 *dev, u8 cmd_code,
+                                struct sk_buff *req,
+                                pn533_send_async_complete_t complete_cb,
+                                void *complete_cb_context)
+{
+       struct sk_buff *resp;
+       int rc;
+       int  resp_len = dev->ops->rx_header_len +
+                       dev->ops->max_payload_len +
+                       dev->ops->rx_tail_len;
+
+       resp = nfc_alloc_recv_skb(resp_len, GFP_KERNEL);
+       if (!resp)
+               return -ENOMEM;
+
+       rc = __pn533_send_async(dev, cmd_code, req, resp, resp_len, complete_cb,
+                               complete_cb_context);
+       if (rc)
+               dev_kfree_skb(resp);
 
        return rc;
 }
 
-struct pn533_sync_cmd_response {
+static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code,
+                               struct sk_buff *req,
+                               pn533_send_async_complete_t complete_cb,
+                               void *complete_cb_context)
+{
+       struct sk_buff *resp;
        int rc;
-       struct completion done;
-};
+       int  resp_len = dev->ops->rx_header_len +
+                       dev->ops->max_payload_len +
+                       dev->ops->rx_tail_len;
+
+       resp = alloc_skb(resp_len, GFP_KERNEL);
+       if (!resp)
+               return -ENOMEM;
+
+       rc = __pn533_send_async(dev, cmd_code, req, resp, resp_len, complete_cb,
+                               complete_cb_context);
+       if (rc)
+               dev_kfree_skb(resp);
 
-static int pn533_sync_cmd_complete(struct pn533 *dev, void *_arg,
-                                       u8 *params, int params_len)
+       return rc;
+}
+
+/*
+ * pn533_send_cmd_direct_async
+ *
+ * The function sends a piority cmd directly to the chip omiting the cmd
+ * queue. It's intended to be used by chaining mechanism of received responses
+ * where the host has to request every single chunk of data before scheduling
+ * next cmd from the queue.
+ */
+static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code,
+                                      struct sk_buff *req,
+                                      pn533_send_async_complete_t complete_cb,
+                                      void *complete_cb_context)
 {
-       struct pn533_sync_cmd_response *arg = _arg;
+       struct pn533_send_async_complete_arg *arg;
+       struct sk_buff *resp;
+       int rc;
+       int resp_len = dev->ops->rx_header_len +
+                      dev->ops->max_payload_len +
+                      dev->ops->rx_tail_len;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       resp = alloc_skb(resp_len, GFP_KERNEL);
+       if (!resp)
+               return -ENOMEM;
+
+       arg = kzalloc(sizeof(*arg), GFP_KERNEL);
+       if (!arg) {
+               dev_kfree_skb(resp);
+               return -ENOMEM;
+       }
 
-       arg->rc = 0;
+       arg->complete_cb = complete_cb;
+       arg->complete_cb_context = complete_cb_context;
+       arg->resp = resp;
+       arg->req = req;
 
-       if (params_len < 0) /* error */
-               arg->rc = params_len;
+       pn533_build_cmd_frame(dev, cmd_code, req);
 
+       rc = __pn533_send_frame_async(dev, req, resp, resp_len,
+                                     pn533_send_async_complete, arg);
+       if (rc < 0) {
+               dev_kfree_skb(resp);
+               kfree(arg);
+       }
+
+       return rc;
+}
+
+static void pn533_wq_cmd(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, cmd_work);
+       struct pn533_cmd *cmd;
+
+       mutex_lock(&dev->cmd_lock);
+
+       if (list_empty(&dev->cmd_queue)) {
+               dev->cmd_pending = 0;
+               mutex_unlock(&dev->cmd_lock);
+               return;
+       }
+
+       cmd = list_first_entry(&dev->cmd_queue, struct pn533_cmd, queue);
+
+       list_del(&cmd->queue);
+
+       mutex_unlock(&dev->cmd_lock);
+
+       __pn533_send_frame_async(dev, cmd->req, cmd->resp, cmd->resp_len,
+                                pn533_send_async_complete, cmd->arg);
+
+       kfree(cmd);
+}
+
+struct pn533_sync_cmd_response {
+       struct sk_buff *resp;
+       struct completion done;
+};
+
+static int pn533_send_sync_complete(struct pn533 *dev, void *_arg,
+                                   struct sk_buff *resp)
+{
+       struct pn533_sync_cmd_response *arg = _arg;
+
+       arg->resp = resp;
        complete(&arg->done);
 
        return 0;
 }
 
-static int pn533_send_cmd_frame_sync(struct pn533 *dev,
-                                               struct pn533_frame *out_frame,
-                                               struct pn533_frame *in_frame,
-                                               int in_frame_len)
+/*  pn533_send_cmd_sync
+ *
+ *  Please note the req parameter is freed inside the function to
+ *  limit a number of return value interpretations by the caller.
+ *
+ *  1. negative in case of error during TX path -> req should be freed
+ *
+ *  2. negative in case of error during RX path -> req should not be freed
+ *     as it's been already freed at the begining of RX path by
+ *     async_complete_cb.
+ *
+ *  3. valid pointer in case of succesfult RX path
+ *
+ *  A caller has to check a return value with IS_ERR macro. If the test pass,
+ *  the returned pointer is valid.
+ *
+ * */
+static struct sk_buff *pn533_send_cmd_sync(struct pn533 *dev, u8 cmd_code,
+                                              struct sk_buff *req)
 {
        int rc;
        struct pn533_sync_cmd_response arg;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
-
        init_completion(&arg.done);
 
-       rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, in_frame_len,
-                               pn533_sync_cmd_complete, &arg, GFP_KERNEL);
-       if (rc)
-               return rc;
+       rc = pn533_send_cmd_async(dev, cmd_code, req,
+                                 pn533_send_sync_complete, &arg);
+       if (rc) {
+               dev_kfree_skb(req);
+               return ERR_PTR(rc);
+       }
 
        wait_for_completion(&arg.done);
 
-       return arg.rc;
+       return arg.resp;
 }
 
 static void pn533_send_complete(struct urb *urb)
 {
        struct pn533 *dev = urb->context;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
-
        switch (urb->status) {
        case 0:
-               /* success */
-               break;
+               break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-       case -ESHUTDOWN:
-               nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with"
-                                               " status: %d", urb->status);
+               nfc_dev_dbg(&dev->interface->dev,
+                           "The urb has been stopped (status %d)",
+                           urb->status);
                break;
+       case -ESHUTDOWN:
        default:
-               nfc_dev_dbg(&dev->interface->dev, "Nonzero urb status received:"
-                                                       " %d", urb->status);
+               nfc_dev_err(&dev->interface->dev,
+                           "Urb failure (status %d)", urb->status);
        }
 }
 
+static struct sk_buff *pn533_alloc_skb(struct pn533 *dev, unsigned int size)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_skb(dev->ops->tx_header_len +
+                       size +
+                       dev->ops->tx_tail_len, GFP_KERNEL);
+
+       if (skb)
+               skb_reserve(skb, dev->ops->tx_header_len);
+
+       return skb;
+}
+
 struct pn533_target_type_a {
        __be16 sens_res;
        u8 sel_res;
@@ -867,9 +1038,9 @@ static bool pn533_target_type_a_is_valid(struct pn533_target_type_a *type_a,
        platconf = PN533_TYPE_A_SENS_RES_PLATCONF(type_a->sens_res);
 
        if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
-                       platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
-                       (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
-                       platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL))
+            platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
+           (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+            platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL))
                return false;
 
        /* Requirements 4.8.2.1, 4.8.2.3, 4.8.2.5 and 4.8.2.7 from NFC Forum */
@@ -884,7 +1055,7 @@ static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data,
 {
        struct pn533_target_type_a *tgt_type_a;
 
-       tgt_type_a = (struct pn533_target_type_a *) tgt_data;
+       tgt_type_a = (struct pn533_target_type_a *)tgt_data;
 
        if (!pn533_target_type_a_is_valid(tgt_type_a, tgt_data_len))
                return -EPROTO;
@@ -942,14 +1113,13 @@ static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data,
 {
        struct pn533_target_felica *tgt_felica;
 
-       tgt_felica = (struct pn533_target_felica *) tgt_data;
+       tgt_felica = (struct pn533_target_felica *)tgt_data;
 
        if (!pn533_target_felica_is_valid(tgt_felica, tgt_data_len))
                return -EPROTO;
 
-       if (tgt_felica->nfcid2[0] == PN533_FELICA_SENSF_NFCID2_DEP_B1 &&
-                                       tgt_felica->nfcid2[1] ==
-                                       PN533_FELICA_SENSF_NFCID2_DEP_B2)
+       if ((tgt_felica->nfcid2[0] == PN533_FELICA_SENSF_NFCID2_DEP_B1) &&
+           (tgt_felica->nfcid2[1] == PN533_FELICA_SENSF_NFCID2_DEP_B2))
                nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
        else
                nfc_tgt->supported_protocols = NFC_PROTO_FELICA_MASK;
@@ -979,9 +1149,9 @@ static bool pn533_target_jewel_is_valid(struct pn533_target_jewel *jewel,
        platconf = PN533_TYPE_A_SENS_RES_PLATCONF(jewel->sens_res);
 
        if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
-                       platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
-                       (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
-                       platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL))
+            platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) ||
+           (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL &&
+            platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL))
                return false;
 
        return true;
@@ -992,7 +1162,7 @@ static int pn533_target_found_jewel(struct nfc_target *nfc_tgt, u8 *tgt_data,
 {
        struct pn533_target_jewel *tgt_jewel;
 
-       tgt_jewel = (struct pn533_target_jewel *) tgt_data;
+       tgt_jewel = (struct pn533_target_jewel *)tgt_data;
 
        if (!pn533_target_jewel_is_valid(tgt_jewel, tgt_data_len))
                return -EPROTO;
@@ -1051,7 +1221,7 @@ static int pn533_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data,
 {
        struct pn533_target_type_b *tgt_type_b;
 
-       tgt_type_b = (struct pn533_target_type_b *) tgt_data;
+       tgt_type_b = (struct pn533_target_type_b *)tgt_data;
 
        if (!pn533_target_type_b_is_valid(tgt_type_b, tgt_data_len))
                return -EPROTO;
@@ -1061,50 +1231,37 @@ static int pn533_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data,
        return 0;
 }
 
-struct pn533_poll_response {
-       u8 nbtg;
-       u8 tg;
-       u8 target_data[];
-} __packed;
-
-static int pn533_target_found(struct pn533 *dev,
-                       struct pn533_poll_response *resp, int resp_len)
+static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata,
+                             int tgdata_len)
 {
-       int target_data_len;
        struct nfc_target nfc_tgt;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s - modulation=%d", __func__,
-                                                       dev->poll_mod_curr);
+                   dev->poll_mod_curr);
 
-       if (resp->tg != 1)
+       if (tg != 1)
                return -EPROTO;
 
        memset(&nfc_tgt, 0, sizeof(struct nfc_target));
 
-       target_data_len = resp_len - sizeof(struct pn533_poll_response);
-
        switch (dev->poll_mod_curr) {
        case PN533_POLL_MOD_106KBPS_A:
-               rc = pn533_target_found_type_a(&nfc_tgt, resp->target_data,
-                                                       target_data_len);
+               rc = pn533_target_found_type_a(&nfc_tgt, tgdata, tgdata_len);
                break;
        case PN533_POLL_MOD_212KBPS_FELICA:
        case PN533_POLL_MOD_424KBPS_FELICA:
-               rc = pn533_target_found_felica(&nfc_tgt, resp->target_data,
-                                                       target_data_len);
+               rc = pn533_target_found_felica(&nfc_tgt, tgdata, tgdata_len);
                break;
        case PN533_POLL_MOD_106KBPS_JEWEL:
-               rc = pn533_target_found_jewel(&nfc_tgt, resp->target_data,
-                                                       target_data_len);
+               rc = pn533_target_found_jewel(&nfc_tgt, tgdata, tgdata_len);
                break;
        case PN533_POLL_MOD_847KBPS_B:
-               rc = pn533_target_found_type_b(&nfc_tgt, resp->target_data,
-                                                       target_data_len);
+               rc = pn533_target_found_type_b(&nfc_tgt, tgdata, tgdata_len);
                break;
        default:
-               nfc_dev_err(&dev->interface->dev, "Unknown current poll"
-                                                               " modulation");
+               nfc_dev_err(&dev->interface->dev,
+                           "Unknown current poll modulation");
                return -EPROTO;
        }
 
@@ -1112,13 +1269,14 @@ static int pn533_target_found(struct pn533 *dev,
                return rc;
 
        if (!(nfc_tgt.supported_protocols & dev->poll_protocols)) {
-               nfc_dev_dbg(&dev->interface->dev, "The target found does not"
-                                               " have the desired protocol");
+               nfc_dev_dbg(&dev->interface->dev,
+                           "The Tg found doesn't have the desired protocol");
                return -EAGAIN;
        }
 
-       nfc_dev_dbg(&dev->interface->dev, "Target found - supported protocols: "
-                                       "0x%x", nfc_tgt.supported_protocols);
+       nfc_dev_dbg(&dev->interface->dev,
+                   "Target found - supported protocols: 0x%x",
+                   nfc_tgt.supported_protocols);
 
        dev->tgt_available_prots = nfc_tgt.supported_protocols;
 
@@ -1140,7 +1298,7 @@ static void pn533_poll_reset_mod_list(struct pn533 *dev)
 static void pn533_poll_add_mod(struct pn533 *dev, u8 mod_index)
 {
        dev->poll_mod_active[dev->poll_mod_count] =
-               (struct pn533_poll_modulations *) &poll_mod[mod_index];
+               (struct pn533_poll_modulations *)&poll_mod[mod_index];
        dev->poll_mod_count++;
 }
 
@@ -1149,13 +1307,13 @@ static void pn533_poll_create_mod_list(struct pn533 *dev,
 {
        pn533_poll_reset_mod_list(dev);
 
-       if (im_protocols & NFC_PROTO_MIFARE_MASK
-           || im_protocols & NFC_PROTO_ISO14443_MASK
-           || im_protocols & NFC_PROTO_NFC_DEP_MASK)
+       if ((im_protocols & NFC_PROTO_MIFARE_MASK) ||
+           (im_protocols & NFC_PROTO_ISO14443_MASK) ||
+           (im_protocols & NFC_PROTO_NFC_DEP_MASK))
                pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_A);
 
-       if (im_protocols & NFC_PROTO_FELICA_MASK
-           || im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+       if (im_protocols & NFC_PROTO_FELICA_MASK ||
+           im_protocols & NFC_PROTO_NFC_DEP_MASK) {
                pn533_poll_add_mod(dev, PN533_POLL_MOD_212KBPS_FELICA);
                pn533_poll_add_mod(dev, PN533_POLL_MOD_424KBPS_FELICA);
        }
@@ -1170,16 +1328,20 @@ static void pn533_poll_create_mod_list(struct pn533 *dev,
                pn533_poll_add_mod(dev, PN533_LISTEN_MOD);
 }
 
-static int pn533_start_poll_complete(struct pn533 *dev, u8 *params, int params_len)
+static int pn533_start_poll_complete(struct pn533 *dev, struct sk_buff *resp)
 {
-       struct pn533_poll_response *resp;
-       int rc;
+       u8 nbtg, tg, *tgdata;
+       int rc, tgdata_len;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       resp = (struct pn533_poll_response *) params;
-       if (resp->nbtg) {
-               rc = pn533_target_found(dev, resp, params_len);
+       nbtg = resp->data[0];
+       tg = resp->data[1];
+       tgdata = &resp->data[2];
+       tgdata_len = resp->len - 2;  /* nbtg + tg */
+
+       if (nbtg) {
+               rc = pn533_target_found(dev, tg, tgdata, tgdata_len);
 
                /* We must stop the poll after a valid target found */
                if (rc == 0) {
@@ -1191,158 +1353,134 @@ static int pn533_start_poll_complete(struct pn533 *dev, u8 *params, int params_l
        return -EAGAIN;
 }
 
-static int pn533_init_target_frame(struct pn533_frame *frame,
-                                  u8 *gb, size_t gb_len)
+static struct sk_buff *pn533_alloc_poll_tg_frame(struct pn533 *dev)
 {
-       struct pn533_cmd_init_target *cmd;
-       size_t cmd_len;
+       struct sk_buff *skb;
+       u8 *felica, *nfcid3, *gb;
+
+       u8 *gbytes = dev->gb;
+       size_t gbytes_len = dev->gb_len;
+
        u8 felica_params[18] = {0x1, 0xfe, /* DEP */
                                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* random */
                                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
                                0xff, 0xff}; /* System code */
+
        u8 mifare_params[6] = {0x1, 0x1, /* SENS_RES */
                               0x0, 0x0, 0x0,
                               0x40}; /* SEL_RES for DEP */
 
-       cmd_len = sizeof(struct pn533_cmd_init_target) + gb_len + 1;
-       cmd = kzalloc(cmd_len, GFP_KERNEL);
-       if (cmd == NULL)
-               return -ENOMEM;
+       unsigned int skb_len = 36 + /* mode (1), mifare (6),
+                                      felica (18), nfcid3 (10), gb_len (1) */
+                              gbytes_len +
+                              1;  /* len Tk*/
 
-       pn533_tx_frame_init(frame, PN533_CMD_TG_INIT_AS_TARGET);
+       skb = pn533_alloc_skb(dev, skb_len);
+       if (!skb)
+               return NULL;
 
        /* DEP support only */
-       cmd->mode |= PN533_INIT_TARGET_DEP;
+       *skb_put(skb, 1) |= PN533_INIT_TARGET_DEP;
+
+       /* MIFARE params */
+       memcpy(skb_put(skb, 6), mifare_params, 6);
 
        /* Felica params */
-       memcpy(cmd->felica, felica_params, 18);
-       get_random_bytes(cmd->felica + 2, 6);
+       felica = skb_put(skb, 18);
+       memcpy(felica, felica_params, 18);
+       get_random_bytes(felica + 2, 6);
 
        /* NFCID3 */
-       memset(cmd->nfcid3, 0, 10);
-       memcpy(cmd->nfcid3, cmd->felica, 8);
-
-       /* MIFARE params */
-       memcpy(cmd->mifare, mifare_params, 6);
+       nfcid3 = skb_put(skb, 10);
+       memset(nfcid3, 0, 10);
+       memcpy(nfcid3, felica, 8);
 
        /* General bytes */
-       cmd->gb_len = gb_len;
-       memcpy(cmd->gb, gb, gb_len);
-
-       /* Len Tk */
-       cmd->gb[gb_len] = 0;
-
-       memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), cmd, cmd_len);
+       *skb_put(skb, 1) = gbytes_len;
 
-       frame->datalen += cmd_len;
+       gb = skb_put(skb, gbytes_len);
+       memcpy(gb, gbytes, gbytes_len);
 
-       pn533_tx_frame_finish(frame);
-
-       kfree(cmd);
+       /* Len Tk */
+       *skb_put(skb, 1) = 0;
 
-       return 0;
+       return skb;
 }
 
-#define PN533_CMD_DATAEXCH_HEAD_LEN (sizeof(struct pn533_frame) + 3)
+#define PN533_CMD_DATAEXCH_HEAD_LEN 1
 #define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
 static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg,
-                                     u8 *params, int params_len)
+                                     struct sk_buff *resp)
 {
-       struct sk_buff *skb_resp = arg;
-       struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data;
+       u8 status;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (params_len < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error %d when starting as a target",
-                           params_len);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
-               return params_len;
-       }
+       status = resp->data[0];
+       skb_pull(resp, sizeof(status));
 
-       if (params_len > 0 && params[0] != 0) {
+       if (status != 0) {
                nfc_tm_deactivated(dev->nfc_dev);
-
                dev->tgt_mode = 0;
-
-               kfree_skb(skb_resp);
+               dev_kfree_skb(resp);
                return 0;
        }
 
-       skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
-       skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
-       skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE);
-
-       return nfc_tm_data_received(dev->nfc_dev, skb_resp);
+       return nfc_tm_data_received(dev->nfc_dev, resp);
 }
 
 static void pn533_wq_tg_get_data(struct work_struct *work)
 {
        struct pn533 *dev = container_of(work, struct pn533, tg_work);
-       struct pn533_frame *in_frame;
-       struct sk_buff *skb_resp;
-       size_t skb_resp_len;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       struct sk_buff *skb;
+       int rc;
 
-       skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN +
-               PN533_CMD_DATAEXCH_DATA_MAXLEN +
-               PN533_FRAME_TAIL_SIZE;
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       skb_resp = nfc_alloc_recv_skb(skb_resp_len, GFP_KERNEL);
-       if (!skb_resp)
+       skb = pn533_alloc_skb(dev, 0);
+       if (!skb)
                return;
 
-       in_frame = (struct pn533_frame *)skb_resp->data;
+       rc = pn533_send_data_async(dev, PN533_CMD_TG_GET_DATA, skb,
+                                  pn533_tm_get_data_complete, NULL);
 
-       pn533_tx_frame_init(dev->out_frame, PN533_CMD_TG_GET_DATA);
-       pn533_tx_frame_finish(dev->out_frame);
-
-       pn533_send_cmd_frame_async(dev, dev->out_frame, in_frame,
-                                  skb_resp_len,
-                                  pn533_tm_get_data_complete,
-                                  skb_resp, GFP_KERNEL);
+       if (rc < 0)
+               dev_kfree_skb(skb);
 
        return;
 }
 
 #define ATR_REQ_GB_OFFSET 17
-static int pn533_init_target_complete(struct pn533 *dev, u8 *params, int params_len)
+static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp)
 {
-       struct pn533_cmd_init_target_response *resp;
-       u8 frame, comm_mode = NFC_COMM_PASSIVE, *gb;
+       u8 mode, *cmd, comm_mode = NFC_COMM_PASSIVE, *gb;
        size_t gb_len;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (params_len < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error %d when starting as a target",
-                           params_len);
-
-               return params_len;
-       }
-
-       if (params_len < ATR_REQ_GB_OFFSET + 1)
+       if (resp->len < ATR_REQ_GB_OFFSET + 1)
                return -EINVAL;
 
-       resp = (struct pn533_cmd_init_target_response *) params;
+       mode = resp->data[0];
+       cmd = &resp->data[1];
 
-       nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x param len %d\n",
-                   resp->mode, params_len);
+       nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x len %d\n",
+                   mode, resp->len);
 
-       frame = resp->mode & PN533_INIT_TARGET_RESP_FRAME_MASK;
-       if (frame == PN533_INIT_TARGET_RESP_ACTIVE)
+       if ((mode & PN533_INIT_TARGET_RESP_FRAME_MASK) ==
+           PN533_INIT_TARGET_RESP_ACTIVE)
                comm_mode = NFC_COMM_ACTIVE;
 
-       /* Again, only DEP */
-       if ((resp->mode & PN533_INIT_TARGET_RESP_DEP) == 0)
+       if ((mode & PN533_INIT_TARGET_RESP_DEP) == 0)  /* Only DEP supported */
                return -EOPNOTSUPP;
 
-       gb = resp->cmd + ATR_REQ_GB_OFFSET;
-       gb_len = params_len - (ATR_REQ_GB_OFFSET + 1);
+       gb = cmd + ATR_REQ_GB_OFFSET;
+       gb_len = resp->len - (ATR_REQ_GB_OFFSET + 1);
 
        rc = nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
                              comm_mode, gb, gb_len);
@@ -1353,7 +1491,6 @@ static int pn533_init_target_complete(struct pn533 *dev, u8 *params, int params_
        }
 
        dev->tgt_mode = 1;
-
        queue_work(dev->wq, &dev->tg_work);
 
        return 0;
@@ -1361,7 +1498,7 @@ static int pn533_init_target_complete(struct pn533 *dev, u8 *params, int params_
 
 static void pn533_listen_mode_timer(unsigned long data)
 {
-       struct pn533 *dev = (struct pn533 *) data;
+       struct pn533 *dev = (struct pn533 *)data;
 
        nfc_dev_dbg(&dev->interface->dev, "Listen mode timeout");
 
@@ -1376,88 +1513,104 @@ static void pn533_listen_mode_timer(unsigned long data)
 }
 
 static int pn533_poll_complete(struct pn533 *dev, void *arg,
-                              u8 *params, int params_len)
+                              struct sk_buff *resp)
 {
        struct pn533_poll_modulations *cur_mod;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (params_len == -ENOENT) {
-               if (dev->poll_mod_count != 0)
-                       return 0;
-
-               nfc_dev_err(&dev->interface->dev,
-                           "Polling operation has been stopped");
-
-               goto stop_poll;
-       }
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
 
-       if (params_len < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error %d when running poll", params_len);
+               nfc_dev_err(&dev->interface->dev, "%s  Poll complete error %d",
+                           __func__, rc);
 
-               goto stop_poll;
+               if (rc == -ENOENT) {
+                       if (dev->poll_mod_count != 0)
+                               return rc;
+                       else
+                               goto stop_poll;
+               } else if (rc < 0) {
+                       nfc_dev_err(&dev->interface->dev,
+                                   "Error %d when running poll", rc);
+                       goto stop_poll;
+               }
        }
 
        cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
 
-       if (cur_mod->len == 0) {
+       if (cur_mod->len == 0) { /* Target mode */
                del_timer(&dev->listen_timer);
-
-               return pn533_init_target_complete(dev, params, params_len);
-       } else {
-               rc = pn533_start_poll_complete(dev, params, params_len);
-               if (!rc)
-                       return rc;
+               rc = pn533_init_target_complete(dev, resp);
+               goto done;
        }
 
-       pn533_poll_next_mod(dev);
+       /* Initiator mode */
+       rc = pn533_start_poll_complete(dev, resp);
+       if (!rc)
+               goto done;
 
+       pn533_poll_next_mod(dev);
        queue_work(dev->wq, &dev->poll_work);
 
-       return 0;
+done:
+       dev_kfree_skb(resp);
+       return rc;
 
 stop_poll:
+       nfc_dev_err(&dev->interface->dev, "Polling operation has been stopped");
+
        pn533_poll_reset_mod_list(dev);
        dev->poll_protocols = 0;
-       return 0;
+       return rc;
 }
 
-static void pn533_build_poll_frame(struct pn533 *dev,
-                                  struct pn533_frame *frame,
-                                  struct pn533_poll_modulations *mod)
+static struct sk_buff *pn533_alloc_poll_in_frame(struct pn533 *dev,
+                                       struct pn533_poll_modulations *mod)
 {
-       nfc_dev_dbg(&dev->interface->dev, "mod len %d\n", mod->len);
+       struct sk_buff *skb;
 
-       if (mod->len == 0) {
-               /* Listen mode */
-               pn533_init_target_frame(frame, dev->gb, dev->gb_len);
-       } else {
-               /* Polling mode */
-               pn533_tx_frame_init(frame, PN533_CMD_IN_LIST_PASSIVE_TARGET);
+       skb = pn533_alloc_skb(dev, mod->len);
+       if (!skb)
+               return NULL;
 
-               memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), &mod->data, mod->len);
-               frame->datalen += mod->len;
+       memcpy(skb_put(skb, mod->len), &mod->data, mod->len);
 
-               pn533_tx_frame_finish(frame);
-       }
+       return skb;
 }
 
 static int pn533_send_poll_frame(struct pn533 *dev)
 {
-       struct pn533_poll_modulations *cur_mod;
+       struct pn533_poll_modulations *mod;
+       struct sk_buff *skb;
        int rc;
+       u8 cmd_code;
 
-       cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+       mod = dev->poll_mod_active[dev->poll_mod_curr];
 
-       pn533_build_poll_frame(dev, dev->out_frame, cur_mod);
+       nfc_dev_dbg(&dev->interface->dev, "%s mod len %d\n",
+                   __func__, mod->len);
 
-       rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame,
-                               dev->in_maxlen, pn533_poll_complete,
-                               NULL, GFP_KERNEL);
-       if (rc)
+       if (mod->len == 0) {  /* Listen mode */
+               cmd_code = PN533_CMD_TG_INIT_AS_TARGET;
+               skb = pn533_alloc_poll_tg_frame(dev);
+       } else {  /* Polling mode */
+               cmd_code =  PN533_CMD_IN_LIST_PASSIVE_TARGET;
+               skb = pn533_alloc_poll_in_frame(dev, mod);
+       }
+
+       if (!skb) {
+               nfc_dev_err(&dev->interface->dev, "Failed to allocate skb.");
+               return -ENOMEM;
+       }
+
+       rc = pn533_send_cmd_async(dev, cmd_code, skb, pn533_poll_complete,
+                                 NULL);
+       if (rc < 0) {
+               dev_kfree_skb(skb);
                nfc_dev_err(&dev->interface->dev, "Polling loop error %d", rc);
+       }
 
        return rc;
 }
@@ -1533,8 +1686,8 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev)
        del_timer(&dev->listen_timer);
 
        if (!dev->poll_mod_count) {
-               nfc_dev_dbg(&dev->interface->dev, "Polling operation was not"
-                                                               " running");
+               nfc_dev_dbg(&dev->interface->dev,
+                           "Polling operation was not running");
                return;
        }
 
@@ -1549,38 +1702,38 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev)
 
 static int pn533_activate_target_nfcdep(struct pn533 *dev)
 {
-       struct pn533_cmd_activate_param param;
-       struct pn533_cmd_activate_response *resp;
+       struct pn533_cmd_activate_response *rsp;
        u16 gt_len;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       struct sk_buff *skb;
+       struct sk_buff *resp;
 
-       pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_ATR);
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       param.tg = 1;
-       param.next = 0;
-       memcpy(PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame), &param,
-                               sizeof(struct pn533_cmd_activate_param));
-       dev->out_frame->datalen += sizeof(struct pn533_cmd_activate_param);
+       skb = pn533_alloc_skb(dev, sizeof(u8) * 2); /*TG + Next*/
+       if (!skb)
+               return -ENOMEM;
 
-       pn533_tx_frame_finish(dev->out_frame);
+       *skb_put(skb, sizeof(u8)) = 1; /* TG */
+       *skb_put(skb, sizeof(u8)) = 0; /* Next */
 
-       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
-                                                               dev->in_maxlen);
-       if (rc)
-               return rc;
+       resp = pn533_send_cmd_sync(dev, PN533_CMD_IN_ATR, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
-       resp = (struct pn533_cmd_activate_response *)
-                               PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame);
-       rc = resp->status & PN533_CMD_RET_MASK;
-       if (rc != PN533_CMD_RET_SUCCESS)
+       rsp = (struct pn533_cmd_activate_response *)resp->data;
+       rc = rsp->status & PN533_CMD_RET_MASK;
+       if (rc != PN533_CMD_RET_SUCCESS) {
+               dev_kfree_skb(resp);
                return -EIO;
+       }
 
        /* ATR_RES general bytes are located at offset 16 */
-       gt_len = PN533_FRAME_CMD_PARAMS_LEN(dev->in_frame) - 16;
-       rc = nfc_set_remote_general_bytes(dev->nfc_dev, resp->gt, gt_len);
+       gt_len = resp->len - 16;
+       rc = nfc_set_remote_general_bytes(dev->nfc_dev, rsp->gt, gt_len);
 
+       dev_kfree_skb(resp);
        return rc;
 }
 
@@ -1591,38 +1744,38 @@ static int pn533_activate_target(struct nfc_dev *nfc_dev,
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s - protocol=%u", __func__,
-                                                               protocol);
+                   protocol);
 
        if (dev->poll_mod_count) {
-               nfc_dev_err(&dev->interface->dev, "Cannot activate while"
-                                                               " polling");
+               nfc_dev_err(&dev->interface->dev,
+                           "Cannot activate while polling");
                return -EBUSY;
        }
 
        if (dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev, "There is already an active"
-                                                               " target");
+               nfc_dev_err(&dev->interface->dev,
+                           "There is already an active target");
                return -EBUSY;
        }
 
        if (!dev->tgt_available_prots) {
-               nfc_dev_err(&dev->interface->dev, "There is no available target"
-                                                               " to activate");
+               nfc_dev_err(&dev->interface->dev,
+                           "There is no available target to activate");
                return -EINVAL;
        }
 
        if (!(dev->tgt_available_prots & (1 << protocol))) {
-               nfc_dev_err(&dev->interface->dev, "The target does not support"
-                                       " the requested protocol %u", protocol);
+               nfc_dev_err(&dev->interface->dev,
+                           "Target doesn't support requested proto %u",
+                           protocol);
                return -EINVAL;
        }
 
        if (protocol == NFC_PROTO_NFC_DEP) {
                rc = pn533_activate_target_nfcdep(dev);
                if (rc) {
-                       nfc_dev_err(&dev->interface->dev, "Error %d when"
-                                               " activating target with"
-                                               " NFC_DEP protocol", rc);
+                       nfc_dev_err(&dev->interface->dev,
+                                   "Activating target with DEP failed %d", rc);
                        return rc;
                }
        }
@@ -1637,8 +1790,10 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
                                    struct nfc_target *target)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
-       u8 tg;
-       u8 status;
+
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
@@ -1649,83 +1804,69 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
        }
 
        dev->tgt_active_prot = 0;
-
        skb_queue_purge(&dev->resp_q);
 
-       pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_RELEASE);
-
-       tg = 1;
-       memcpy(PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame), &tg, sizeof(u8));
-       dev->out_frame->datalen += sizeof(u8);
+       skb = pn533_alloc_skb(dev, sizeof(u8));
+       if (!skb)
+               return;
 
-       pn533_tx_frame_finish(dev->out_frame);
+       *skb_put(skb, 1) = 1; /* TG*/
 
-       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
-                                                               dev->in_maxlen);
-       if (rc) {
-               nfc_dev_err(&dev->interface->dev, "Error when sending release"
-                                               " command to the controller");
+       resp = pn533_send_cmd_sync(dev, PN533_CMD_IN_RELEASE, skb);
+       if (IS_ERR(resp))
                return;
-       }
 
-       status = PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame)[0];
-       rc = status & PN533_CMD_RET_MASK;
+       rc = resp->data[0] & PN533_CMD_RET_MASK;
        if (rc != PN533_CMD_RET_SUCCESS)
-               nfc_dev_err(&dev->interface->dev, "Error 0x%x when releasing"
-                                                       " the target", rc);
+               nfc_dev_err(&dev->interface->dev,
+                           "Error 0x%x when releasing the target", rc);
 
+       dev_kfree_skb(resp);
        return;
 }
 
 
 static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg,
-                                               u8 *params, int params_len)
+                                        struct sk_buff *resp)
 {
-       struct pn533_cmd_jump_dep_response *resp;
-       struct nfc_target nfc_target;
+       struct pn533_cmd_jump_dep_response *rsp;
        u8 target_gt_len;
        int rc;
-       struct pn533_cmd_jump_dep *cmd = (struct pn533_cmd_jump_dep *)arg;
-       u8 active = cmd->active;
+       u8 active = *(u8 *)arg;
 
        kfree(arg);
 
-       if (params_len == -ENOENT) {
-               nfc_dev_dbg(&dev->interface->dev, "");
-               return 0;
-       }
-
-       if (params_len < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                               "Error %d when bringing DEP link up",
-                                                               params_len);
-               return 0;
-       }
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
        if (dev->tgt_available_prots &&
            !(dev->tgt_available_prots & (1 << NFC_PROTO_NFC_DEP))) {
                nfc_dev_err(&dev->interface->dev,
-                       "The target does not support DEP");
-               return -EINVAL;
+                           "The target does not support DEP");
+               rc =  -EINVAL;
+               goto error;
        }
 
-       resp = (struct pn533_cmd_jump_dep_response *) params;
-       rc = resp->status & PN533_CMD_RET_MASK;
+       rsp = (struct pn533_cmd_jump_dep_response *)resp->data;
+
+       rc = rsp->status & PN533_CMD_RET_MASK;
        if (rc != PN533_CMD_RET_SUCCESS) {
                nfc_dev_err(&dev->interface->dev,
-                               "Bringing DEP link up failed %d", rc);
-               return 0;
+                           "Bringing DEP link up failed %d", rc);
+               goto error;
        }
 
        if (!dev->tgt_available_prots) {
+               struct nfc_target nfc_target;
+
                nfc_dev_dbg(&dev->interface->dev, "Creating new target");
 
                nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
                nfc_target.nfcid1_len = 10;
-               memcpy(nfc_target.nfcid1, resp->nfcid3t, nfc_target.nfcid1_len);
+               memcpy(nfc_target.nfcid1, rsp->nfcid3t, nfc_target.nfcid1_len);
                rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1);
                if (rc)
-                       return 0;
+                       goto error;
 
                dev->tgt_available_prots = 0;
        }
@@ -1733,15 +1874,17 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg,
        dev->tgt_active_prot = NFC_PROTO_NFC_DEP;
 
        /* ATR_RES general bytes are located at offset 17 */
-       target_gt_len = PN533_FRAME_CMD_PARAMS_LEN(dev->in_frame) - 17;
+       target_gt_len = resp->len - 17;
        rc = nfc_set_remote_general_bytes(dev->nfc_dev,
-                                               resp->gt, target_gt_len);
+                                         rsp->gt, target_gt_len);
        if (rc == 0)
                rc = nfc_dep_link_is_up(dev->nfc_dev,
-                                               dev->nfc_dev->targets[0].idx,
-                                               !active, NFC_RF_INITIATOR);
+                                       dev->nfc_dev->targets[0].idx,
+                                       !active, NFC_RF_INITIATOR);
 
-       return 0;
+error:
+       dev_kfree_skb(resp);
+       return rc;
 }
 
 static int pn533_mod_to_baud(struct pn533 *dev)
@@ -1760,25 +1903,26 @@ static int pn533_mod_to_baud(struct pn533 *dev)
 
 #define PASSIVE_DATA_LEN 5
 static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
-                            u8 comm_mode, u8gb, size_t gb_len)
+                            u8 comm_mode, u8 *gb, size_t gb_len)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
-       struct pn533_cmd_jump_dep *cmd;
-       u8 cmd_len, *data_ptr;
+       struct sk_buff *skb;
+       int rc, baud, skb_len;
+       u8 *next, *arg;
+
        u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
-       int rc, baud;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
        if (dev->poll_mod_count) {
                nfc_dev_err(&dev->interface->dev,
-                               "Cannot bring the DEP link up while polling");
+                           "Cannot bring the DEP link up while polling");
                return -EBUSY;
        }
 
        if (dev->tgt_active_prot) {
                nfc_dev_err(&dev->interface->dev,
-                               "There is already an active target");
+                           "There is already an active target");
                return -EBUSY;
        }
 
@@ -1789,43 +1933,48 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                return baud;
        }
 
-       cmd_len = sizeof(struct pn533_cmd_jump_dep) + gb_len;
+       skb_len = 3 + gb_len; /* ActPass + BR + Next */
        if (comm_mode == NFC_COMM_PASSIVE)
-               cmd_len += PASSIVE_DATA_LEN;
+               skb_len += PASSIVE_DATA_LEN;
 
-       cmd = kzalloc(cmd_len, GFP_KERNEL);
-       if (cmd == NULL)
+       skb = pn533_alloc_skb(dev, skb_len);
+       if (!skb)
                return -ENOMEM;
 
-       pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_JUMP_FOR_DEP);
+       *skb_put(skb, 1) = !comm_mode;  /* ActPass */
+       *skb_put(skb, 1) = baud;  /* Baud rate */
 
-       cmd->active = !comm_mode;
-       cmd->next = 0;
-       cmd->baud = baud;
-       data_ptr = cmd->data;
-       if (comm_mode == NFC_COMM_PASSIVE && cmd->baud > 0) {
-               memcpy(data_ptr, passive_data, PASSIVE_DATA_LEN);
-               cmd->next |= 1;
-               data_ptr += PASSIVE_DATA_LEN;
+       next = skb_put(skb, 1);  /* Next */
+       *next = 0;
+
+       if (comm_mode == NFC_COMM_PASSIVE && baud > 0) {
+               memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data,
+                      PASSIVE_DATA_LEN);
+               *next |= 1;
        }
 
        if (gb != NULL && gb_len > 0) {
-               cmd->next |= 4; /* We have some Gi */
-               memcpy(data_ptr, gb, gb_len);
+               memcpy(skb_put(skb, gb_len), gb, gb_len);
+               *next |= 4; /* We have some Gi */
        } else {
-               cmd->next = 0;
+               *next = 0;
        }
 
-       memcpy(PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame), cmd, cmd_len);
-       dev->out_frame->datalen += cmd_len;
+       arg = kmalloc(sizeof(*arg), GFP_KERNEL);
+       if (!arg) {
+               dev_kfree_skb(skb);
+               return -ENOMEM;
+       }
 
-       pn533_tx_frame_finish(dev->out_frame);
+       *arg = !comm_mode;
 
-       rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame,
-                               dev->in_maxlen, pn533_in_dep_link_up_complete,
-                               cmd, GFP_KERNEL);
-       if (rc < 0)
-               kfree(cmd);
+       rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb,
+                                 pn533_in_dep_link_up_complete, arg);
+
+       if (rc < 0) {
+               dev_kfree_skb(skb);
+               kfree(arg);
+       }
 
        return rc;
 }
@@ -1834,6 +1983,8 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
 
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
        pn533_poll_reset_mod_list(dev);
 
        if (dev->tgt_mode || dev->tgt_active_prot) {
@@ -1849,68 +2000,7 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev)
        return 0;
 }
 
-static int pn533_build_tx_frame(struct pn533 *dev, struct sk_buff *skb,
-                               bool target)
-{
-       int payload_len = skb->len;
-       struct pn533_frame *out_frame;
-       u8 tg;
-
-       nfc_dev_dbg(&dev->interface->dev, "%s - Sending %d bytes", __func__,
-                                                               payload_len);
-
-       if (payload_len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
-               /* TODO: Implement support to multi-part data exchange */
-               nfc_dev_err(&dev->interface->dev, "Data length greater than the"
-                                               " max allowed: %d",
-                                               PN533_CMD_DATAEXCH_DATA_MAXLEN);
-               return -ENOSYS;
-       }
-
-       if (target == true) {
-               switch (dev->device_type) {
-               case PN533_DEVICE_PASORI:
-                       if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
-                               skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN - 1);
-                               out_frame = (struct pn533_frame *) skb->data;
-                               pn533_tx_frame_init(out_frame,
-                                                   PN533_CMD_IN_COMM_THRU);
-
-                               break;
-                       }
-
-               default:
-                       skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN);
-                       out_frame = (struct pn533_frame *) skb->data;
-                       pn533_tx_frame_init(out_frame,
-                                           PN533_CMD_IN_DATA_EXCHANGE);
-                       tg = 1;
-                       memcpy(PN533_FRAME_CMD_PARAMS_PTR(out_frame),
-                              &tg, sizeof(u8));
-                       out_frame->datalen += sizeof(u8);
-
-                       break;
-               }
-
-       } else {
-               skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN - 1);
-               out_frame = (struct pn533_frame *) skb->data;
-               pn533_tx_frame_init(out_frame, PN533_CMD_TG_SET_DATA);
-       }
-
-
-       /* The data is already in the out_frame, just update the datalen */
-       out_frame->datalen += payload_len;
-
-       pn533_tx_frame_finish(out_frame);
-       skb_put(skb, PN533_FRAME_TAIL_SIZE);
-
-       return 0;
-}
-
 struct pn533_data_exchange_arg {
-       struct sk_buff *skb_resp;
-       struct sk_buff *skb_out;
        data_exchange_cb_t cb;
        void *cb_context;
 };
@@ -1920,7 +2010,7 @@ static struct sk_buff *pn533_build_response(struct pn533 *dev)
        struct sk_buff *skb, *tmp, *t;
        unsigned int skb_len = 0, tmp_len = 0;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s\n", __func__);
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
        if (skb_queue_empty(&dev->resp_q))
                return NULL;
@@ -1954,46 +2044,44 @@ out:
 }
 
 static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
-                                               u8 *params, int params_len)
+                                       struct sk_buff *resp)
 {
        struct pn533_data_exchange_arg *arg = _arg;
-       struct sk_buff *skb = NULL, *skb_resp = arg->skb_resp;
-       struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data;
-       int err = 0;
-       u8 status;
-       u8 cmd_ret;
+       struct sk_buff *skb;
+       int rc = 0;
+       u8 status, ret, mi;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       dev_kfree_skb(arg->skb_out);
-
-       if (params_len < 0) { /* error */
-               err = params_len;
-               goto error;
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               goto _error;
        }
 
-       status = params[0];
+       status = resp->data[0];
+       ret = status & PN533_CMD_RET_MASK;
+       mi = status & PN533_CMD_MI_MASK;
+
+       skb_pull(resp, sizeof(status));
 
-       cmd_ret = status & PN533_CMD_RET_MASK;
-       if (cmd_ret != PN533_CMD_RET_SUCCESS) {
-               nfc_dev_err(&dev->interface->dev, "PN533 reported error %d when"
-                                               " exchanging data", cmd_ret);
-               err = -EIO;
+       if (ret != PN533_CMD_RET_SUCCESS) {
+               nfc_dev_err(&dev->interface->dev,
+                           "PN533 reported error %d when exchanging data",
+                           ret);
+               rc = -EIO;
                goto error;
        }
 
-       skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
-       skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
-       skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE);
-       skb_queue_tail(&dev->resp_q, skb_resp);
+       skb_queue_tail(&dev->resp_q, resp);
 
-       if (status & PN533_CMD_MI_MASK) {
+       if (mi) {
+               dev->cmd_complete_mi_arg = arg;
                queue_work(dev->wq, &dev->mi_work);
                return -EINPROGRESS;
        }
 
        skb = pn533_build_response(dev);
-       if (skb == NULL)
+       if (!skb)
                goto error;
 
        arg->cb(arg->cb_context, skb, 0);
@@ -2001,11 +2089,12 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
        return 0;
 
 error:
+       dev_kfree_skb(resp);
+_error:
        skb_queue_purge(&dev->resp_q);
-       dev_kfree_skb(skb_resp);
-       arg->cb(arg->cb_context, NULL, err);
+       arg->cb(arg->cb_context, NULL, rc);
        kfree(arg);
-       return 0;
+       return rc;
 }
 
 static int pn533_transceive(struct nfc_dev *nfc_dev,
@@ -2013,87 +2102,82 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
                            data_exchange_cb_t cb, void *cb_context)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
-       struct pn533_frame *out_frame, *in_frame;
-       struct pn533_data_exchange_arg *arg;
-       struct sk_buff *skb_resp;
-       int skb_resp_len;
+       struct pn533_data_exchange_arg *arg = NULL;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (!dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev, "Cannot exchange data if"
-                                               " there is no active target");
-               rc = -EINVAL;
+       if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
+               /* TODO: Implement support to multi-part data exchange */
+               nfc_dev_err(&dev->interface->dev,
+                           "Data length greater than the max allowed: %d",
+                           PN533_CMD_DATAEXCH_DATA_MAXLEN);
+               rc = -ENOSYS;
                goto error;
        }
 
-       rc = pn533_build_tx_frame(dev, skb, true);
-       if (rc)
-               goto error;
-
-       skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN +
-                       PN533_CMD_DATAEXCH_DATA_MAXLEN +
-                       PN533_FRAME_TAIL_SIZE;
-
-       skb_resp = nfc_alloc_recv_skb(skb_resp_len, GFP_KERNEL);
-       if (!skb_resp) {
-               rc = -ENOMEM;
+       if (!dev->tgt_active_prot) {
+               nfc_dev_err(&dev->interface->dev,
+                           "Can't exchange data if there is no active target");
+               rc = -EINVAL;
                goto error;
        }
 
-       in_frame = (struct pn533_frame *) skb_resp->data;
-       out_frame = (struct pn533_frame *) skb->data;
-
-       arg = kmalloc(sizeof(struct pn533_data_exchange_arg), GFP_KERNEL);
+       arg = kmalloc(sizeof(*arg), GFP_KERNEL);
        if (!arg) {
                rc = -ENOMEM;
-               goto free_skb_resp;
+               goto error;
        }
 
-       arg->skb_resp = skb_resp;
-       arg->skb_out = skb;
        arg->cb = cb;
        arg->cb_context = cb_context;
 
-       rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, skb_resp_len,
-                                       pn533_data_exchange_complete, arg,
-                                       GFP_KERNEL);
-       if (rc) {
-               nfc_dev_err(&dev->interface->dev, "Error %d when trying to"
-                                               " perform data_exchange", rc);
-               goto free_arg;
+       switch (dev->device_type) {
+       case PN533_DEVICE_PASORI:
+               if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
+                       rc = pn533_send_data_async(dev, PN533_CMD_IN_COMM_THRU,
+                                                  skb,
+                                                  pn533_data_exchange_complete,
+                                                  arg);
+
+                       break;
+               }
+       default:
+               *skb_push(skb, sizeof(u8)) =  1; /*TG*/
+
+               rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE,
+                                          skb, pn533_data_exchange_complete,
+                                          arg);
+
+               break;
        }
 
+       if (rc < 0) /* rc from send_async */
+               goto error;
+
        return 0;
 
-free_arg:
-       kfree(arg);
-free_skb_resp:
-       kfree_skb(skb_resp);
 error:
-       kfree_skb(skb);
+       kfree(arg);
+       dev_kfree_skb(skb);
        return rc;
 }
 
 static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
-                                 u8 *params, int params_len)
+                                 struct sk_buff *resp)
 {
-       struct sk_buff *skb_out = arg;
+       u8 status;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       dev_kfree_skb(skb_out);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
-       if (params_len < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error %d when sending data",
-                           params_len);
+       status = resp->data[0];
 
-               return params_len;
-       }
+       dev_kfree_skb(resp);
 
-       if (params_len > 0 && params[0] != 0) {
+       if (status != 0) {
                nfc_tm_deactivated(dev->nfc_dev);
 
                dev->tgt_mode = 0;
@@ -2109,30 +2193,21 @@ static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
 static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
-       struct pn533_frame *out_frame;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       rc = pn533_build_tx_frame(dev, skb, false);
-       if (rc)
-               goto error;
-
-       out_frame = (struct pn533_frame *) skb->data;
-
-       rc = pn533_send_cmd_frame_async(dev, out_frame, dev->in_frame,
-                                       dev->in_maxlen, pn533_tm_send_complete,
-                                       skb, GFP_KERNEL);
-       if (rc) {
+       if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
                nfc_dev_err(&dev->interface->dev,
-                           "Error %d when trying to send data", rc);
-               goto error;
+                           "Data length greater than the max allowed: %d",
+                           PN533_CMD_DATAEXCH_DATA_MAXLEN);
+               return -ENOSYS;
        }
 
-       return 0;
-
-error:
-       kfree_skb(skb);
+       rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb,
+                                  pn533_tm_send_complete, NULL);
+       if (rc < 0)
+               dev_kfree_skb(skb);
 
        return rc;
 }
@@ -2140,107 +2215,123 @@ error:
 static void pn533_wq_mi_recv(struct work_struct *work)
 {
        struct pn533 *dev = container_of(work, struct pn533, mi_work);
-       struct sk_buff *skb_cmd;
-       struct pn533_data_exchange_arg *arg = dev->cmd_complete_arg;
-       struct pn533_frame *out_frame, *in_frame;
-       struct sk_buff *skb_resp;
-       int skb_resp_len;
+
+       struct sk_buff *skb;
        int rc;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       /* This is a zero payload size skb */
-       skb_cmd = alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN + PN533_FRAME_TAIL_SIZE,
-                           GFP_KERNEL);
-       if (skb_cmd == NULL)
-               goto error_cmd;
-
-       skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN);
+       skb = pn533_alloc_skb(dev, PN533_CMD_DATAEXCH_HEAD_LEN);
+       if (!skb)
+               goto error;
 
-       rc = pn533_build_tx_frame(dev, skb_cmd, true);
-       if (rc)
-               goto error_frame;
+       switch (dev->device_type) {
+       case PN533_DEVICE_PASORI:
+               if (dev->tgt_active_prot == NFC_PROTO_FELICA) {
+                       rc = pn533_send_cmd_direct_async(dev,
+                                               PN533_CMD_IN_COMM_THRU,
+                                               skb,
+                                               pn533_data_exchange_complete,
+                                                dev->cmd_complete_mi_arg);
 
-       skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN +
-                       PN533_CMD_DATAEXCH_DATA_MAXLEN +
-                       PN533_FRAME_TAIL_SIZE;
-       skb_resp = alloc_skb(skb_resp_len, GFP_KERNEL);
-       if (!skb_resp) {
-               rc = -ENOMEM;
-               goto error_frame;
-       }
+                       break;
+               }
+       default:
+               *skb_put(skb, sizeof(u8)) =  1; /*TG*/
 
-       in_frame = (struct pn533_frame *) skb_resp->data;
-       out_frame = (struct pn533_frame *) skb_cmd->data;
+               rc = pn533_send_cmd_direct_async(dev,
+                                                PN533_CMD_IN_DATA_EXCHANGE,
+                                                skb,
+                                                pn533_data_exchange_complete,
+                                                dev->cmd_complete_mi_arg);
 
-       arg->skb_resp = skb_resp;
-       arg->skb_out = skb_cmd;
+               break;
+       }
 
-       rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
-                                         skb_resp_len,
-                                         pn533_data_exchange_complete,
-                                         dev->cmd_complete_arg, GFP_KERNEL);
-       if (!rc)
+       if (rc == 0) /* success */
                return;
 
-       nfc_dev_err(&dev->interface->dev, "Error %d when trying to"
-                                               " perform data_exchange", rc);
-
-       kfree_skb(skb_resp);
+       nfc_dev_err(&dev->interface->dev,
+                   "Error %d when trying to perform data_exchange", rc);
 
-error_frame:
-       kfree_skb(skb_cmd);
+       dev_kfree_skb(skb);
+       kfree(dev->cmd_complete_arg);
 
-error_cmd:
+error:
        pn533_send_ack(dev, GFP_KERNEL);
-
-       kfree(arg);
-
        queue_work(dev->wq, &dev->cmd_work);
 }
 
 static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
                                                                u8 cfgdata_len)
 {
-       int rc;
-       u8 *params;
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+
+       int skb_len;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       pn533_tx_frame_init(dev->out_frame, PN533_CMD_RF_CONFIGURATION);
+       skb_len = sizeof(cfgitem) + cfgdata_len; /* cfgitem + cfgdata */
 
-       params = PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame);
-       params[0] = cfgitem;
-       memcpy(&params[1], cfgdata, cfgdata_len);
-       dev->out_frame->datalen += (1 + cfgdata_len);
+       skb = pn533_alloc_skb(dev, skb_len);
+       if (!skb)
+               return -ENOMEM;
 
-       pn533_tx_frame_finish(dev->out_frame);
+       *skb_put(skb, sizeof(cfgitem)) = cfgitem;
+       memcpy(skb_put(skb, cfgdata_len), cfgdata, cfgdata_len);
 
-       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
-                                                               dev->in_maxlen);
+       resp = pn533_send_cmd_sync(dev, PN533_CMD_RF_CONFIGURATION, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
-       return rc;
+       dev_kfree_skb(resp);
+       return 0;
+}
+
+static int pn533_get_firmware_version(struct pn533 *dev,
+                                     struct pn533_fw_version *fv)
+{
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+
+       skb = pn533_alloc_skb(dev, 0);
+       if (!skb)
+               return -ENOMEM;
+
+       resp = pn533_send_cmd_sync(dev, PN533_CMD_GET_FIRMWARE_VERSION, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       fv->ic = resp->data[0];
+       fv->ver = resp->data[1];
+       fv->rev = resp->data[2];
+       fv->support = resp->data[3];
+
+       dev_kfree_skb(resp);
+       return 0;
 }
 
 static int pn533_fw_reset(struct pn533 *dev)
 {
-       int rc;
-       u8 *params;
+       struct sk_buff *skb;
+       struct sk_buff *resp;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       pn533_tx_frame_init(dev->out_frame, 0x18);
+       skb = pn533_alloc_skb(dev, sizeof(u8));
+       if (!skb)
+               return -ENOMEM;
 
-       params = PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame);
-       params[0] = 0x1;
-       dev->out_frame->datalen += 1;
+       *skb_put(skb, sizeof(u8)) = 0x1;
 
-       pn533_tx_frame_finish(dev->out_frame);
+       resp = pn533_send_cmd_sync(dev, 0x18, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
 
-       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
-                                      dev->in_maxlen);
+       dev_kfree_skb(resp);
 
-       return rc;
+       return 0;
 }
 
 static struct nfc_ops pn533_nfc_ops = {
@@ -2337,7 +2428,7 @@ static int pn533_setup(struct pn533 *dev)
 static int pn533_probe(struct usb_interface *interface,
                        const struct usb_device_id *id)
 {
-       struct pn533_fw_version *fw_ver;
+       struct pn533_fw_version fw_ver;
        struct pn533 *dev;
        struct usb_host_interface *iface_desc;
        struct usb_endpoint_descriptor *endpoint;
@@ -2359,41 +2450,32 @@ static int pn533_probe(struct usb_interface *interface,
        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
                endpoint = &iface_desc->endpoint[i].desc;
 
-               if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
-                       dev->in_maxlen = le16_to_cpu(endpoint->wMaxPacketSize);
+               if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint))
                        in_endpoint = endpoint->bEndpointAddress;
-               }
 
-               if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint)) {
-                       dev->out_maxlen =
-                               le16_to_cpu(endpoint->wMaxPacketSize);
+               if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint))
                        out_endpoint = endpoint->bEndpointAddress;
-               }
        }
 
        if (!in_endpoint || !out_endpoint) {
-               nfc_dev_err(&interface->dev, "Could not find bulk-in or"
-                                                       " bulk-out endpoint");
+               nfc_dev_err(&interface->dev,
+                           "Could not find bulk-in or bulk-out endpoint");
                rc = -ENODEV;
                goto error;
        }
 
-       dev->in_frame = kmalloc(PN533_NORMAL_FRAME_MAX_LEN, GFP_KERNEL);
        dev->in_urb = usb_alloc_urb(0, GFP_KERNEL);
-       dev->out_frame = kmalloc(PN533_NORMAL_FRAME_MAX_LEN, GFP_KERNEL);
        dev->out_urb = usb_alloc_urb(0, GFP_KERNEL);
 
-       if (!dev->in_frame || !dev->out_frame ||
-               !dev->in_urb || !dev->out_urb)
+       if (!dev->in_urb || !dev->out_urb)
                goto error;
 
        usb_fill_bulk_urb(dev->in_urb, dev->udev,
-                       usb_rcvbulkpipe(dev->udev, in_endpoint),
-                       NULL, 0, NULL, dev);
+                         usb_rcvbulkpipe(dev->udev, in_endpoint),
+                         NULL, 0, NULL, dev);
        usb_fill_bulk_urb(dev->out_urb, dev->udev,
-                       usb_sndbulkpipe(dev->udev, out_endpoint),
-                       NULL, 0,
-                       pn533_send_complete, dev);
+                         usb_sndbulkpipe(dev->udev, out_endpoint),
+                         NULL, 0, pn533_send_complete, dev);
 
        INIT_WORK(&dev->cmd_work, pn533_wq_cmd);
        INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete);
@@ -2414,18 +2496,7 @@ static int pn533_probe(struct usb_interface *interface,
 
        usb_set_intfdata(interface, dev);
 
-       pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION);
-       pn533_tx_frame_finish(dev->out_frame);
-
-       rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
-                                                               dev->in_maxlen);
-       if (rc)
-               goto destroy_wq;
-
-       fw_ver = (struct pn533_fw_version *)
-                               PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame);
-       nfc_dev_info(&dev->interface->dev, "NXP PN533 firmware ver %d.%d now"
-                                       " attached", fw_ver->ver, fw_ver->rev);
+       dev->ops = &pn533_std_frame_ops;
 
        dev->device_type = id->driver_info;
        switch (dev->device_type) {
@@ -2444,9 +2515,21 @@ static int pn533_probe(struct usb_interface *interface,
                goto destroy_wq;
        }
 
+       memset(&fw_ver, 0, sizeof(fw_ver));
+       rc = pn533_get_firmware_version(dev, &fw_ver);
+       if (rc < 0)
+               goto destroy_wq;
+
+       nfc_dev_info(&dev->interface->dev,
+                    "NXP PN533 firmware ver %d.%d now attached",
+                    fw_ver.ver, fw_ver.rev);
+
+
        dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols,
+                                          NFC_SE_NONE,
+                                          dev->ops->tx_header_len +
                                           PN533_CMD_DATAEXCH_HEAD_LEN,
-                                          PN533_FRAME_TAIL_SIZE);
+                                          dev->ops->tx_tail_len);
        if (!dev->nfc_dev)
                goto destroy_wq;
 
@@ -2472,9 +2555,7 @@ free_nfc_dev:
 destroy_wq:
        destroy_workqueue(dev->wq);
 error:
-       kfree(dev->in_frame);
        usb_free_urb(dev->in_urb);
-       kfree(dev->out_frame);
        usb_free_urb(dev->out_urb);
        kfree(dev);
        return rc;
@@ -2505,9 +2586,7 @@ static void pn533_disconnect(struct usb_interface *interface)
                kfree(cmd);
        }
 
-       kfree(dev->in_frame);
        usb_free_urb(dev->in_urb);
-       kfree(dev->out_frame);
        usb_free_urb(dev->out_urb);
        kfree(dev);
 
diff --git a/drivers/nfc/pn544/Kconfig b/drivers/nfc/pn544/Kconfig
new file mode 100644 (file)
index 0000000..c277790
--- /dev/null
@@ -0,0 +1,23 @@
+config NFC_PN544
+       tristate "NXP PN544 NFC driver"
+       depends on NFC_HCI
+       select CRC_CCITT
+       default n
+       ---help---
+         NXP PN544 core driver.
+         This is a driver based on the HCI NFC kernel layers and
+         will thus not work with NXP libnfc library.
+
+         To compile this driver as a module, choose m here. The module will
+         be called pn544.
+         Say N if unsure.
+
+config NFC_PN544_I2C
+       tristate "NFC PN544 i2c support"
+       depends on NFC_PN544 && I2C && NFC_SHDLC
+       ---help---
+         This module adds support for the NXP pn544 i2c interface.
+         Select this if your platform is using the i2c bus.
+
+         If you choose to build a module, it'll be called pn544_i2c.
+         Say N if unsure.
\ No newline at end of file
index 725733881eb3d04074629b5b9e8acd13e24b375f..ac076793687d582d4ec6068fd444f55dc84c15c8 100644 (file)
@@ -2,6 +2,7 @@
 # Makefile for PN544 HCI based NFC driver
 #
 
-obj-$(CONFIG_PN544_HCI_NFC)    += pn544_i2c.o
+pn544_i2c-objs  = i2c.o
 
-pn544_i2c-y            := pn544.o i2c.o
+obj-$(CONFIG_NFC_PN544)     += pn544.o
+obj-$(CONFIG_NFC_PN544_I2C) += pn544_i2c.o
index 2a9c8d93d2e8a302e8db87b91160e7fdd301a77d..8cf64c19f0229c97952ecd9f9fb689b29c6c3240 100644 (file)
@@ -376,12 +376,12 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
                return -ENODEV;
        }
 
-       phy = kzalloc(sizeof(struct pn544_i2c_phy), GFP_KERNEL);
+       phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy),
+                          GFP_KERNEL);
        if (!phy) {
                dev_err(&client->dev,
                        "Cannot allocate memory for pn544 i2c phy.\n");
-               r = -ENOMEM;
-               goto err_phy_alloc;
+               return -ENOMEM;
        }
 
        phy->i2c_dev = client;
@@ -390,20 +390,18 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
        pdata = client->dev.platform_data;
        if (pdata == NULL) {
                dev_err(&client->dev, "No platform data\n");
-               r = -EINVAL;
-               goto err_pdata;
+               return -EINVAL;
        }
 
        if (pdata->request_resources == NULL) {
                dev_err(&client->dev, "request_resources() missing\n");
-               r = -EINVAL;
-               goto err_pdata;
+               return -EINVAL;
        }
 
        r = pdata->request_resources(client);
        if (r) {
                dev_err(&client->dev, "Cannot get platform resources\n");
-               goto err_pdata;
+               return r;
        }
 
        phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
@@ -435,10 +433,6 @@ err_rti:
        if (pdata->free_resources != NULL)
                pdata->free_resources();
 
-err_pdata:
-       kfree(phy);
-
-err_phy_alloc:
        return r;
 }
 
@@ -458,8 +452,6 @@ static int pn544_hci_i2c_remove(struct i2c_client *client)
        if (pdata->free_resources)
                pdata->free_resources();
 
-       kfree(phy);
-
        return 0;
 }
 
@@ -472,29 +464,7 @@ static struct i2c_driver pn544_hci_i2c_driver = {
        .remove = pn544_hci_i2c_remove,
 };
 
-static int __init pn544_hci_i2c_init(void)
-{
-       int r;
-
-       pr_debug(DRIVER_DESC ": %s\n", __func__);
-
-       r = i2c_add_driver(&pn544_hci_i2c_driver);
-       if (r) {
-               pr_err(PN544_HCI_I2C_DRIVER_NAME
-                      ": driver registration failed\n");
-               return r;
-       }
-
-       return 0;
-}
-
-static void __exit pn544_hci_i2c_exit(void)
-{
-       i2c_del_driver(&pn544_hci_i2c_driver);
-}
-
-module_init(pn544_hci_i2c_init);
-module_exit(pn544_hci_i2c_exit);
+module_i2c_driver(pn544_hci_i2c_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION(DRIVER_DESC);
index cc666de3b8e5a56b58ac984ff1f5ab890048c2e9..9c5f16e7baefa40970f7d3274aaff53fcd2f8c9a 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/module.h>
 
 #include <linux/nfc.h>
 #include <net/nfc/hci.h>
@@ -675,11 +676,17 @@ static int pn544_hci_im_transceive(struct nfc_hci_dev *hdev,
 
 static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb)
 {
+       int r;
+
        /* Set default false for multiple information chaining */
        *skb_push(skb, 1) = 0;
 
-       return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
-                               PN544_HCI_EVT_SND_DATA, skb->data, skb->len);
+       r = nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
+                              PN544_HCI_EVT_SND_DATA, skb->data, skb->len);
+
+       kfree_skb(skb);
+
+       return r;
 }
 
 static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
@@ -714,35 +721,40 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
        return 0;
 }
 
-static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
-                                       u8 event, struct sk_buff *skb)
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ *    1: driver does not handle the event, please do standard processing
+ */
+static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event,
+                                   struct sk_buff *skb)
 {
        struct sk_buff *rgb_skb = NULL;
-       int r = 0;
+       int r;
 
        pr_debug("hci event %d", event);
        switch (event) {
        case PN544_HCI_EVT_ACTIVATED:
-               if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE)
-                       nfc_hci_target_discovered(hdev, gate);
-               else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) {
+               if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) {
+                       r = nfc_hci_target_discovered(hdev, gate);
+               else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) {
                        r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ,
-                                               &rgb_skb);
-
+                                             &rgb_skb);
                        if (r < 0)
                                goto exit;
 
-                       nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
-                                       NFC_COMM_PASSIVE, rgb_skb->data,
-                                       rgb_skb->len);
+                       r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
+                                            NFC_COMM_PASSIVE, rgb_skb->data,
+                                            rgb_skb->len);
 
                        kfree_skb(rgb_skb);
+               } else {
+                       r = -EINVAL;
                }
-
                break;
        case PN544_HCI_EVT_DEACTIVATED:
-               nfc_hci_send_event(hdev, gate,
-                       NFC_HCI_EVT_END_OPERATION, NULL, 0);
+               r = nfc_hci_send_event(hdev, gate, NFC_HCI_EVT_END_OPERATION,
+                                      NULL, 0);
                break;
        case PN544_HCI_EVT_RCV_DATA:
                if (skb->len < 2) {
@@ -757,15 +769,15 @@ static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
                }
 
                skb_pull(skb, 2);
-               nfc_tm_data_received(hdev->ndev, skb);
-
-               return;
+               return nfc_tm_data_received(hdev->ndev, skb);
        default:
-               break;
+               return 1;
        }
 
 exit:
        kfree_skb(skb);
+
+       return r;
 }
 
 static struct nfc_hci_ops pn544_hci_ops = {
@@ -789,7 +801,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    struct nfc_hci_dev **hdev)
 {
        struct pn544_hci_info *info;
-       u32 protocols;
+       u32 protocols, se;
        struct nfc_hci_init_data init_data;
        int r;
 
@@ -822,8 +834,10 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    NFC_PROTO_ISO14443_B_MASK |
                    NFC_PROTO_NFC_DEP_MASK;
 
-       info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data,
-                                            protocols, llc_name,
+       se = NFC_SE_UICC | NFC_SE_EMBEDDED;
+
+       info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0,
+                                            protocols, se, llc_name,
                                             phy_headroom + PN544_CMDS_HEADROOM,
                                             phy_tailroom, phy_payload);
        if (!info->hdev) {
@@ -851,6 +865,7 @@ err_alloc_hdev:
 err_info_alloc:
        return r;
 }
+EXPORT_SYMBOL(pn544_hci_probe);
 
 void pn544_hci_remove(struct nfc_hci_dev *hdev)
 {
@@ -860,3 +875,7 @@ void pn544_hci_remove(struct nfc_hci_dev *hdev)
        nfc_hci_free_device(hdev);
        kfree(info);
 }
+EXPORT_SYMBOL(pn544_hci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
index e027f444d10cfeae104eca088bf8c7824d88ba5f..eafa107aed403782187831e4ca81a339972d83d1 100644 (file)
@@ -1,4 +1,4 @@
-obj-y = base.o
+obj-y = base.o dma.o
 obj-$(CONFIG_OF_FLATTREE) += fdt.o
 obj-$(CONFIG_OF_PROMTREE) += pdt.o
 obj-$(CONFIG_OF_ADDRESS)  += address.o
diff --git a/drivers/of/dma.c b/drivers/of/dma.c
new file mode 100644 (file)
index 0000000..59631b2
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Device tree helpers for DMA request / controller
+ *
+ * Based on of_gpio.c
+ *
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/rculist.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+
+static LIST_HEAD(of_dma_list);
+static DEFINE_SPINLOCK(of_dma_lock);
+
+/**
+ * of_dma_get_controller - Get a DMA controller in DT DMA helpers list
+ * @dma_spec:  pointer to DMA specifier as found in the device tree
+ *
+ * Finds a DMA controller with matching device node and number for dma cells
+ * in a list of registered DMA controllers. If a match is found the use_count
+ * variable is increased and a valid pointer to the DMA data stored is retuned.
+ * A NULL pointer is returned if no match is found.
+ */
+static struct of_dma *of_dma_get_controller(struct of_phandle_args *dma_spec)
+{
+       struct of_dma *ofdma;
+
+       spin_lock(&of_dma_lock);
+
+       if (list_empty(&of_dma_list)) {
+               spin_unlock(&of_dma_lock);
+               return NULL;
+       }
+
+       list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers)
+               if ((ofdma->of_node == dma_spec->np) &&
+                   (ofdma->of_dma_nbcells == dma_spec->args_count)) {
+                       ofdma->use_count++;
+                       spin_unlock(&of_dma_lock);
+                       return ofdma;
+               }
+
+       spin_unlock(&of_dma_lock);
+
+       pr_debug("%s: can't find DMA controller %s\n", __func__,
+                dma_spec->np->full_name);
+
+       return NULL;
+}
+
+/**
+ * of_dma_put_controller - Decrement use count for a registered DMA controller
+ * @of_dma:    pointer to DMA controller data
+ *
+ * Decrements the use_count variable in the DMA data structure. This function
+ * should be called only when a valid pointer is returned from
+ * of_dma_get_controller() and no further accesses to data referenced by that
+ * pointer are needed.
+ */
+static void of_dma_put_controller(struct of_dma *ofdma)
+{
+       spin_lock(&of_dma_lock);
+       ofdma->use_count--;
+       spin_unlock(&of_dma_lock);
+}
+
+/**
+ * of_dma_controller_register - Register a DMA controller to DT DMA helpers
+ * @np:                        device node of DMA controller
+ * @of_dma_xlate:      translation function which converts a phandle
+ *                     arguments list into a dma_chan structure
+ * @data               pointer to controller specific data to be used by
+ *                     translation function
+ *
+ * Returns 0 on success or appropriate errno value on error.
+ *
+ * Allocated memory should be freed with appropriate of_dma_controller_free()
+ * call.
+ */
+int of_dma_controller_register(struct device_node *np,
+                               struct dma_chan *(*of_dma_xlate)
+                               (struct of_phandle_args *, struct of_dma *),
+                               void *data)
+{
+       struct of_dma   *ofdma;
+       int             nbcells;
+
+       if (!np || !of_dma_xlate) {
+               pr_err("%s: not enough information provided\n", __func__);
+               return -EINVAL;
+       }
+
+       ofdma = kzalloc(sizeof(*ofdma), GFP_KERNEL);
+       if (!ofdma)
+               return -ENOMEM;
+
+       nbcells = be32_to_cpup(of_get_property(np, "#dma-cells", NULL));
+       if (!nbcells) {
+               pr_err("%s: #dma-cells property is missing or invalid\n",
+                      __func__);
+               return -EINVAL;
+       }
+
+       ofdma->of_node = np;
+       ofdma->of_dma_nbcells = nbcells;
+       ofdma->of_dma_xlate = of_dma_xlate;
+       ofdma->of_dma_data = data;
+       ofdma->use_count = 0;
+
+       /* Now queue of_dma controller structure in list */
+       list_add_tail(&ofdma->of_dma_controllers, &of_dma_list);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(of_dma_controller_register);
+
+/**
+ * of_dma_controller_free - Remove a DMA controller from DT DMA helpers list
+ * @np:                device node of DMA controller
+ *
+ * Memory allocated by of_dma_controller_register() is freed here.
+ */
+int of_dma_controller_free(struct device_node *np)
+{
+       struct of_dma *ofdma;
+
+       spin_lock(&of_dma_lock);
+
+       if (list_empty(&of_dma_list)) {
+               spin_unlock(&of_dma_lock);
+               return -ENODEV;
+       }
+
+       list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers)
+               if (ofdma->of_node == np) {
+                       if (ofdma->use_count) {
+                               spin_unlock(&of_dma_lock);
+                               return -EBUSY;
+                       }
+
+                       list_del(&ofdma->of_dma_controllers);
+                       spin_unlock(&of_dma_lock);
+                       kfree(ofdma);
+                       return 0;
+               }
+
+       spin_unlock(&of_dma_lock);
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(of_dma_controller_free);
+
+/**
+ * of_dma_match_channel - Check if a DMA specifier matches name
+ * @np:                device node to look for DMA channels
+ * @name:      channel name to be matched
+ * @index:     index of DMA specifier in list of DMA specifiers
+ * @dma_spec:  pointer to DMA specifier as found in the device tree
+ *
+ * Check if the DMA specifier pointed to by the index in a list of DMA
+ * specifiers, matches the name provided. Returns 0 if the name matches and
+ * a valid pointer to the DMA specifier is found. Otherwise returns -ENODEV.
+ */
+static int of_dma_match_channel(struct device_node *np, char *name, int index,
+                               struct of_phandle_args *dma_spec)
+{
+       const char *s;
+
+       if (of_property_read_string_index(np, "dma-names", index, &s))
+               return -ENODEV;
+
+       if (strcmp(name, s))
+               return -ENODEV;
+
+       if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index,
+                                      dma_spec))
+               return -ENODEV;
+
+       return 0;
+}
+
+/**
+ * of_dma_request_slave_channel - Get the DMA slave channel
+ * @np:                device node to get DMA request from
+ * @name:      name of desired channel
+ *
+ * Returns pointer to appropriate dma channel on success or NULL on error.
+ */
+struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
+                                             char *name)
+{
+       struct of_phandle_args  dma_spec;
+       struct of_dma           *ofdma;
+       struct dma_chan         *chan;
+       int                     count, i;
+
+       if (!np || !name) {
+               pr_err("%s: not enough information provided\n", __func__);
+               return NULL;
+       }
+
+       count = of_property_count_strings(np, "dma-names");
+       if (count < 0) {
+               pr_err("%s: dma-names property missing or empty\n", __func__);
+               return NULL;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (of_dma_match_channel(np, name, i, &dma_spec))
+                       continue;
+
+               ofdma = of_dma_get_controller(&dma_spec);
+
+               if (!ofdma)
+                       continue;
+
+               chan = ofdma->of_dma_xlate(&dma_spec, ofdma);
+
+               of_dma_put_controller(ofdma);
+
+               of_node_put(dma_spec.np);
+
+               if (chan)
+                       return chan;
+       }
+
+       return NULL;
+}
+
+/**
+ * of_dma_simple_xlate - Simple DMA engine translation function
+ * @dma_spec:  pointer to DMA specifier as found in the device tree
+ * @of_dma:    pointer to DMA controller data
+ *
+ * A simple translation function for devices that use a 32-bit value for the
+ * filter_param when calling the DMA engine dma_request_channel() function.
+ * Note that this translation function requires that #dma-cells is equal to 1
+ * and the argument of the dma specifier is the 32-bit filter_param. Returns
+ * pointer to appropriate dma channel on success or NULL on error.
+ */
+struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec,
+                                               struct of_dma *ofdma)
+{
+       int count = dma_spec->args_count;
+       struct of_dma_filter_info *info = ofdma->of_dma_data;
+
+       if (!info || !info->filter_fn)
+               return NULL;
+
+       if (count != 1)
+               return NULL;
+
+       return dma_request_channel(info->dma_cap, info->filter_fn,
+                       &dma_spec->args[0]);
+}
+EXPORT_SYMBOL_GPL(of_dma_simple_xlate);
index c9e912f583bc57485e09909bff01b58af315d2cb..c4af372b789bea2fe024f66d84de438fa85dc3ff 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/platform_device.h>
@@ -31,6 +32,7 @@ struct regs_info {
        u8      vsel_addr;
        u8      ctrl_addr;
        u8      tstep_addr;
+       u8      short_bit;
 };
 
 static const struct regs_info palmas_regs_info[] = {
@@ -39,6 +41,7 @@ static const struct regs_info palmas_regs_info[] = {
                .vsel_addr      = PALMAS_SMPS12_VOLTAGE,
                .ctrl_addr      = PALMAS_SMPS12_CTRL,
                .tstep_addr     = PALMAS_SMPS12_TSTEP,
+               .short_bit      = PALMAS_SMPS_SHORT_STATUS_SMPS12,
        },
        {
                .name           = "SMPS123",
@@ -50,12 +53,14 @@ static const struct regs_info palmas_regs_info[] = {
                .name           = "SMPS3",
                .vsel_addr      = PALMAS_SMPS3_VOLTAGE,
                .ctrl_addr      = PALMAS_SMPS3_CTRL,
+               .short_bit      = PALMAS_SMPS_SHORT_STATUS_SMPS3,
        },
        {
                .name           = "SMPS45",
                .vsel_addr      = PALMAS_SMPS45_VOLTAGE,
                .ctrl_addr      = PALMAS_SMPS45_CTRL,
                .tstep_addr     = PALMAS_SMPS45_TSTEP,
+               .short_bit      = PALMAS_SMPS_SHORT_STATUS_SMPS45,
        },
        {
                .name           = "SMPS457",
@@ -68,80 +73,96 @@ static const struct regs_info palmas_regs_info[] = {
                .vsel_addr      = PALMAS_SMPS6_VOLTAGE,
                .ctrl_addr      = PALMAS_SMPS6_CTRL,
                .tstep_addr     = PALMAS_SMPS6_TSTEP,
+               .short_bit      = PALMAS_SMPS_SHORT_STATUS_SMPS6,
        },
        {
                .name           = "SMPS7",
                .vsel_addr      = PALMAS_SMPS7_VOLTAGE,
                .ctrl_addr      = PALMAS_SMPS7_CTRL,
+               .short_bit      = PALMAS_SMPS_SHORT_STATUS_SMPS7,
        },
        {
                .name           = "SMPS8",
                .vsel_addr      = PALMAS_SMPS8_VOLTAGE,
                .ctrl_addr      = PALMAS_SMPS8_CTRL,
                .tstep_addr     = PALMAS_SMPS8_TSTEP,
+               .short_bit      = PALMAS_SMPS_SHORT_STATUS_SMPS8,
        },
        {
                .name           = "SMPS9",
                .vsel_addr      = PALMAS_SMPS9_VOLTAGE,
                .ctrl_addr      = PALMAS_SMPS9_CTRL,
+               .short_bit      = PALMAS_SMPS_SHORT_STATUS_SMPS9,
        },
        {
                .name           = "SMPS10",
+               .short_bit      = PALMAS_SMPS_SHORT_STATUS_SMPS10,
        },
        {
                .name           = "LDO1",
                .vsel_addr      = PALMAS_LDO1_VOLTAGE,
                .ctrl_addr      = PALMAS_LDO1_CTRL,
+               .short_bit      = PALMAS_LDO_SHORT_STATUS1_LDO1,
        },
        {
                .name           = "LDO2",
                .vsel_addr      = PALMAS_LDO2_VOLTAGE,
                .ctrl_addr      = PALMAS_LDO2_CTRL,
+               .short_bit      = PALMAS_LDO_SHORT_STATUS1_LDO2,
        },
        {
                .name           = "LDO3",
                .vsel_addr      = PALMAS_LDO3_VOLTAGE,
                .ctrl_addr      = PALMAS_LDO3_CTRL,
+               .short_bit      = PALMAS_LDO_SHORT_STATUS1_LDO3,
        },
        {
                .name           = "LDO4",
                .vsel_addr      = PALMAS_LDO4_VOLTAGE,
                .ctrl_addr      = PALMAS_LDO4_CTRL,
+               .short_bit      = PALMAS_LDO_SHORT_STATUS1_LDO4,
        },
        {
                .name           = "LDO5",
                .vsel_addr      = PALMAS_LDO5_VOLTAGE,
                .ctrl_addr      = PALMAS_LDO5_CTRL,
+               .short_bit      = PALMAS_LDO_SHORT_STATUS1_LDO5,
        },
        {
                .name           = "LDO6",
                .vsel_addr      = PALMAS_LDO6_VOLTAGE,
                .ctrl_addr      = PALMAS_LDO6_CTRL,
+               .short_bit      = PALMAS_LDO_SHORT_STATUS1_LDO6,
        },
        {
                .name           = "LDO7",
                .vsel_addr      = PALMAS_LDO7_VOLTAGE,
                .ctrl_addr      = PALMAS_LDO7_CTRL,
+               .short_bit      = PALMAS_LDO_SHORT_STATUS1_LDO7,
        },
        {
                .name           = "LDO8",
                .vsel_addr      = PALMAS_LDO8_VOLTAGE,
                .ctrl_addr      = PALMAS_LDO8_CTRL,
+               .short_bit      = PALMAS_LDO_SHORT_STATUS1_LDO8,
        },
        {
                .name           = "LDO9",
                .vsel_addr      = PALMAS_LDO9_VOLTAGE,
                .ctrl_addr      = PALMAS_LDO9_CTRL,
+               .short_bit      = PALMAS_LDO_SHORT_STATUS2_LDO9,
        },
        {
                .name           = "LDOLN",
                .vsel_addr      = PALMAS_LDOLN_VOLTAGE,
                .ctrl_addr      = PALMAS_LDOLN_CTRL,
+               .short_bit      = PALMAS_LDO_SHORT_STATUS2_LDOLN,
        },
        {
                .name           = "LDOUSB",
                .vsel_addr      = PALMAS_LDOUSB_VOLTAGE,
                .ctrl_addr      = PALMAS_LDOUSB_CTRL,
+               .short_bit      = PALMAS_LDO_SHORT_STATUS2_LDOUSB,
        },
 };
 
index 96ce101b906753c703cf88c1c76d6a7e9a33e826..92eb04134dc5bffa0123b31852998b99e52753b4 100644 (file)
@@ -15,7 +15,8 @@ config OMAP_REMOTEPROC
        depends on ARCH_OMAP4
        depends on OMAP_IOMMU
        select REMOTEPROC
-       select OMAP_MBOX_FWK
+       select MAILBOX
+       select OMAP2PLUS_MBOX
        select RPMSG
        help
          Say y here to support OMAP's remote processors (dual M3
index 0e396c155b3ba60d0d202af60f9aa34f84afd5bf..b90beef040831f62c27894334b8759f74f6ea84c 100644 (file)
@@ -27,8 +27,8 @@
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/remoteproc.h>
+#include <linux/mailbox.h>
 
-#include <plat/mailbox.h>
 #include <linux/platform_data/remoteproc-omap.h>
 
 #include "omap_remoteproc.h"
@@ -41,7 +41,7 @@
  * @rproc: rproc handle
  */
 struct omap_rproc {
-       struct omap_mbox *mbox;
+       struct mailbox *mbox;
        struct notifier_block nb;
        struct rproc *rproc;
 };
@@ -64,14 +64,15 @@ struct omap_rproc {
 static int omap_rproc_mbox_callback(struct notifier_block *this,
                                        unsigned long index, void *data)
 {
-       mbox_msg_t msg = (mbox_msg_t) data;
        struct omap_rproc *oproc = container_of(this, struct omap_rproc, nb);
        struct device *dev = oproc->rproc->dev.parent;
+       struct mailbox_msg *msg = data;
        const char *name = oproc->rproc->name;
+       u32 msg_data = (u32)msg->pdata;
 
-       dev_dbg(dev, "mbox msg: 0x%x\n", msg);
+       dev_dbg(dev, "mbox msg: 0x%x\n", msg_data);
 
-       switch (msg) {
+       switch (msg_data) {
        case RP_MBOX_CRASH:
                /* just log this for now. later, we'll also do recovery */
                dev_err(dev, "omap rproc %s crashed\n", name);
@@ -81,8 +82,9 @@ static int omap_rproc_mbox_callback(struct notifier_block *this,
                break;
        default:
                /* msg contains the index of the triggered vring */
-               if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE)
-                       dev_dbg(dev, "no message was found in vqid %d\n", msg);
+               if (rproc_vq_interrupt(oproc->rproc, msg_data) == IRQ_NONE)
+                       dev_dbg(dev, "no message was found in vqid %d\n",
+                                                               msg_data);
        }
 
        return NOTIFY_DONE;
@@ -93,12 +95,14 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid)
 {
        struct omap_rproc *oproc = rproc->priv;
        struct device *dev = rproc->dev.parent;
+       struct mailbox_msg msg;
        int ret;
 
        /* send the index of the triggered virtqueue in the mailbox payload */
-       ret = omap_mbox_msg_send(oproc->mbox, vqid);
+       MAILBOX_FILL_MSG(msg, 0, vqid, 0);
+       ret = mailbox_msg_send(oproc->mbox, &msg);
        if (ret)
-               dev_err(dev, "omap_mbox_msg_send failed: %d\n", ret);
+               dev_err(dev, "mailbox_msg_send failed: %d\n", ret);
 }
 
 /*
@@ -114,6 +118,7 @@ static int omap_rproc_start(struct rproc *rproc)
        struct device *dev = rproc->dev.parent;
        struct platform_device *pdev = to_platform_device(dev);
        struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
+       struct mailbox_msg msg;
        int ret;
 
        if (pdata->set_bootaddr)
@@ -122,10 +127,10 @@ static int omap_rproc_start(struct rproc *rproc)
        oproc->nb.notifier_call = omap_rproc_mbox_callback;
 
        /* every omap rproc is assigned a mailbox instance for messaging */
-       oproc->mbox = omap_mbox_get(pdata->mbox_name, &oproc->nb);
+       oproc->mbox = mailbox_get(pdata->mbox_name, &oproc->nb);
        if (IS_ERR(oproc->mbox)) {
                ret = PTR_ERR(oproc->mbox);
-               dev_err(dev, "omap_mbox_get failed: %d\n", ret);
+               dev_err(dev, "mailbox_get failed: %d\n", ret);
                return ret;
        }
 
@@ -136,9 +141,10 @@ static int omap_rproc_start(struct rproc *rproc)
         * Note that the reply will _not_ arrive immediately: this message
         * will wait in the mailbox fifo until the remote processor is booted.
         */
-       ret = omap_mbox_msg_send(oproc->mbox, RP_MBOX_ECHO_REQUEST);
+       MAILBOX_FILL_MSG(msg, 0, RP_MBOX_ECHO_REQUEST, 0);
+       ret = mailbox_msg_send(oproc->mbox, &msg);
        if (ret) {
-               dev_err(dev, "omap_mbox_get failed: %d\n", ret);
+               dev_err(dev, "mailbox_get failed: %d\n", ret);
                goto put_mbox;
        }
 
@@ -151,7 +157,7 @@ static int omap_rproc_start(struct rproc *rproc)
        return 0;
 
 put_mbox:
-       omap_mbox_put(oproc->mbox, &oproc->nb);
+       mailbox_put(oproc->mbox, &oproc->nb);
        return ret;
 }
 
@@ -168,7 +174,7 @@ static int omap_rproc_stop(struct rproc *rproc)
        if (ret)
                return ret;
 
-       omap_mbox_put(oproc->mbox, &oproc->nb);
+       mailbox_put(oproc->mbox, &oproc->nb);
 
        return 0;
 }
index 923a9da9c829d7be9a3fe95bac77b0b7e4431de6..f62eab30143817ac008a52b788843d1fbb180a47 100644 (file)
@@ -436,6 +436,12 @@ config RTC_DRV_RV3029C2
          This driver can also be built as a module. If so, the module
          will be called rtc-rv3029c2.
 
+config RTC_DRV_PALMAS
+       tristate "Palmas RTC"
+       help
+         If you say yes here you get support for the RTC on the Palmas
+         series of PMICs from TI.
+
 endif # I2C
 
 comment "SPI RTC drivers"
index 4418ef3f9ecc9278b53aae5dd5177cff522b24ba..3b66c75f6c76f12cd3d728ee8fa78d0a7b85380a 100644 (file)
@@ -76,6 +76,7 @@ obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
 obj-$(CONFIG_RTC_DRV_MV)       += rtc-mv.o
 obj-$(CONFIG_RTC_DRV_NUC900)   += rtc-nuc900.o
 obj-$(CONFIG_RTC_DRV_OMAP)     += rtc-omap.o
+obj-$(CONFIG_RTC_DRV_PALMAS)   += rtc-palmas.o
 obj-$(CONFIG_RTC_DRV_PCAP)     += rtc-pcap.o
 obj-$(CONFIG_RTC_DRV_PCF8523)  += rtc-pcf8523.o
 obj-$(CONFIG_RTC_DRV_PCF8563)  += rtc-pcf8563.o
diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c
new file mode 100644 (file)
index 0000000..116a300
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * Palmas Real Time Clock interface
+ *
+ * Copyright (C) 2012 Texas Instruments
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ * Based on rtc-twl.c
+ * Copyright (C) 2007 MontaVista Software, Inc
+ * Author: Alexandre Rusev <source@mvista.com>
+ *
+ * Based on original TI driver twl4030-rtc.c
+ *   Copyright (C) 2006 Texas Instruments, Inc.
+ *
+ * Based on rtc-omap.c
+ *   Copyright (C) 2003 MontaVista Software, Inc.
+ *   Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com>
+ *   Copyright (C) 2006 David Brownell
+ *
+ * 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/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/mfd/palmas.h>
+
+#define ALL_TIME_REGS  6
+
+struct palmas_rtc {
+       struct palmas *palmas;
+       struct device *dev;
+       struct rtc_device *rtc;
+       unsigned int irq_bits;
+       int irq;
+};
+
+static int palmas_rtc_read(struct palmas *palmas, unsigned int reg,
+               unsigned int *dest)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_RTC_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_RTC_BASE, reg);
+
+       return regmap_read(palmas->regmap[slave], addr, dest);
+}
+
+static int palmas_rtc_write(struct palmas *palmas, unsigned int reg,
+               unsigned int data)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_RTC_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_RTC_BASE, reg);
+
+       return regmap_write(palmas->regmap[slave], addr, data);
+}
+
+static int palmas_rtc_read_block(struct palmas *palmas, unsigned int reg,
+               u8 *dest, size_t count)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_RTC_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_RTC_BASE, reg);
+
+       return regmap_bulk_read(palmas->regmap[slave], addr, dest, count);
+}
+
+static int palmas_rtc_write_block(struct palmas *palmas, unsigned int reg,
+               u8 *src, size_t count)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_RTC_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_RTC_BASE, reg);
+
+       return regmap_raw_write(palmas->regmap[slave], addr, src, count);
+}
+
+static int palmas_rtc_setbits(struct palmas *palmas, unsigned int reg,
+               unsigned int data)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_RESOURCE_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_RESOURCE_BASE, reg);
+
+       return regmap_update_bits(palmas->regmap[slave], addr, data, data);
+}
+
+static int palmas_rtc_clrbits(struct palmas *palmas, unsigned int reg,
+               unsigned int data)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_RESOURCE_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_RESOURCE_BASE, reg);
+
+       return regmap_update_bits(palmas->regmap[slave], addr, data, 0);
+}
+
+/*
+ * Gets current TWL RTC time and date parameters.
+ *
+ * The RTC's time/alarm representation is not what gmtime(3) requires
+ * Linux to use:
+ *
+ *  - Months are 1..12 vs Linux 0-11
+ *  - Years are 0..99 vs Linux 1900..N (we assume 21st century)
+ */
+static int palmas_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
+       struct palmas *palmas = palmas_rtc->palmas;
+       unsigned char rtc_data[ALL_TIME_REGS];
+       int ret;
+
+       ret = palmas_rtc_setbits(palmas, PALMAS_RTC_CTRL_REG,
+                               PALMAS_RTC_CTRL_REG_GET_TIME);
+       if (ret < 0) {
+               dev_err(dev, "Failed to update RTC_CTRL %d\n", ret);
+               goto out;
+       }
+
+       ret = palmas_rtc_read_block(palmas,PALMAS_SECONDS_REG, rtc_data,
+                                  ALL_TIME_REGS);
+
+       if (ret < 0) {
+               dev_err(dev, "Failed to read time block %d\n", ret);
+               goto out;
+       }
+
+       tm->tm_sec = bcd2bin(rtc_data[0]);
+       tm->tm_min = bcd2bin(rtc_data[1]);
+       tm->tm_hour = bcd2bin(rtc_data[2]);
+       tm->tm_mday = bcd2bin(rtc_data[3]);
+       tm->tm_mon = bcd2bin(rtc_data[4]) - 1;
+       tm->tm_year = bcd2bin(rtc_data[5]) + 100;
+
+       ret = palmas_rtc_clrbits(palmas, PALMAS_RTC_CTRL_REG,
+                       PALMAS_RTC_CTRL_REG_GET_TIME);
+       if (ret < 0)
+               dev_err(dev, "Failed to update RTC_CTRL %d\n", ret);
+
+out:
+       return ret;
+}
+
+static int palmas_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
+       struct palmas *palmas = palmas_rtc->palmas;
+       unsigned char rtc_data[ALL_TIME_REGS];
+       int ret;
+
+       rtc_data[0] = bin2bcd(tm->tm_sec);
+       rtc_data[1] = bin2bcd(tm->tm_min);
+       rtc_data[2] = bin2bcd(tm->tm_hour);
+       rtc_data[3] = bin2bcd(tm->tm_mday);
+       rtc_data[4] = bin2bcd(tm->tm_mon + 1);
+       rtc_data[5] = bin2bcd(tm->tm_year - 100);
+
+       /* Stop RTC while updating the TC registers */
+       ret = palmas_rtc_clrbits(palmas, PALMAS_RTC_CTRL_REG,
+                       PALMAS_RTC_CTRL_REG_STOP_RTC);
+       if (ret < 0) {
+               dev_err(dev, "Failed to stop RTC %d\n", ret);
+               goto out;
+       }
+
+       /* update all the time registers in one shot */
+       ret = palmas_rtc_write_block(palmas, PALMAS_SECONDS_REG, rtc_data,
+                                    ALL_TIME_REGS);
+       if (ret < 0) {
+               dev_err(dev, "Failed to write time block %d\n", ret);
+               goto out;
+       }
+
+       /* Start back RTC */
+       ret = palmas_rtc_setbits(palmas, PALMAS_RTC_CTRL_REG,
+                       PALMAS_RTC_CTRL_REG_STOP_RTC);
+       if (ret < 0) {
+               dev_err(dev, "Failed to start RTC %d\n", ret);
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+static int palmas_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
+{
+       struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
+       struct palmas *palmas = palmas_rtc->palmas;
+       int ret;
+
+       if (enabled) {
+               ret = palmas_rtc_setbits(palmas, PALMAS_RTC_INTERRUPTS_REG,
+                               PALMAS_RTC_INTERRUPTS_REG_IT_ALARM);
+               if (ret)
+                       dev_err(palmas_rtc->dev,
+                               "failed to set RTC alarm IRQ %d\n", ret);
+
+               palmas_rtc->irq_bits |= PALMAS_RTC_INTERRUPTS_REG_IT_ALARM;
+       } else {
+               ret = palmas_rtc_clrbits(palmas, PALMAS_RTC_INTERRUPTS_REG,
+                               PALMAS_RTC_INTERRUPTS_REG_IT_ALARM);
+               if (ret)
+                       dev_err(palmas_rtc->dev,
+                               "failed to clear RTC alarm IRQ %d\n", ret);
+
+               palmas_rtc->irq_bits &= ~PALMAS_RTC_INTERRUPTS_REG_IT_ALARM;
+       }
+
+       return ret;
+}
+
+/*
+ * Gets current TWL RTC alarm time.
+ */
+static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+       struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
+       struct palmas *palmas = palmas_rtc->palmas;
+       unsigned char rtc_data[ALL_TIME_REGS];
+       int ret;
+
+       ret = palmas_rtc_read_block(palmas, PALMAS_ALARM_SECONDS_REG, rtc_data,
+                                   ALL_TIME_REGS);
+       if (ret < 0) {
+               dev_err(dev, "Failed to read alarm block %d\n", ret);
+               goto out;
+       }
+
+       /* some of these fields may be wildcard/"match all" */
+       alm->time.tm_sec = bcd2bin(rtc_data[0]);
+       alm->time.tm_min = bcd2bin(rtc_data[1]);
+       alm->time.tm_hour = bcd2bin(rtc_data[2]);
+       alm->time.tm_mday = bcd2bin(rtc_data[3]);
+       alm->time.tm_mon = bcd2bin(rtc_data[4]) - 1;
+       alm->time.tm_year = bcd2bin(rtc_data[5]) + 100;
+
+       /* report cached alarm enable state */
+       if (palmas_rtc->irq_bits & PALMAS_RTC_INTERRUPTS_REG_IT_ALARM)
+               alm->enabled = 1;
+
+out:
+       return ret;
+}
+
+static int palmas_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+       struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
+       struct palmas *palmas = palmas_rtc->palmas;
+       unsigned char alarm_data[ALL_TIME_REGS];
+       int ret;
+
+       ret = palmas_rtc_alarm_irq_enable(dev, 0);
+       if (ret)
+               goto out;
+
+       alarm_data[0] = bin2bcd(alm->time.tm_sec);
+       alarm_data[1] = bin2bcd(alm->time.tm_min);
+       alarm_data[2] = bin2bcd(alm->time.tm_hour);
+       alarm_data[3] = bin2bcd(alm->time.tm_mday);
+       alarm_data[4] = bin2bcd(alm->time.tm_mon + 1);
+       alarm_data[5] = bin2bcd(alm->time.tm_year - 100);
+
+       /* update all the alarm registers in one shot */
+       ret = palmas_rtc_write_block(palmas, PALMAS_ALARM_SECONDS_REG,
+                                    alarm_data, ALL_TIME_REGS);
+       if (ret) {
+               dev_err(dev, "Failed to write alarm block %d\n", ret);
+               goto out;
+       }
+
+       if (alm->enabled) {
+               ret = palmas_rtc_alarm_irq_enable(dev, 1);
+       }
+
+out:
+       return ret;
+}
+
+static irqreturn_t palmas_rtc_interrupt(int irq, void *rtc)
+{
+       struct palmas_rtc *palmas_rtc = rtc;
+       struct palmas *palmas = palmas_rtc->palmas;
+       unsigned long events = 0;
+       int ret = IRQ_NONE;
+       int res;
+       unsigned int rd_reg;
+
+       res = palmas_rtc_read(palmas, PALMAS_RTC_STATUS_REG, &rd_reg);
+       if (res) {
+               dev_err(palmas_rtc->dev, "Failed to read IRQ sts %d\n, res",
+                               res);
+               goto out;
+       }
+
+       /*
+        * Figure out source of interrupt: ALARM or TIMER in RTC_STATUS_REG.
+        * only one (ALARM or RTC) interrupt source may be enabled
+        * at time, we also could check our results
+        * by reading RTS_INTERRUPTS_REGISTER[IT_TIMER,IT_ALARM]
+        */
+       if (rd_reg & PALMAS_RTC_STATUS_REG_ALARM)
+               events |= RTC_IRQF | RTC_AF;
+       else
+               events |= RTC_IRQF | RTC_UF;
+
+       res = palmas_rtc_write(palmas, PALMAS_RTC_STATUS_REG,
+                       rd_reg | PALMAS_RTC_STATUS_REG_ALARM);
+       if (res) {
+               dev_err(palmas_rtc->dev, "Failed to clear IRQ sts %d\n, res",
+                               res);
+               goto out;
+       }
+
+       /* Notify RTC core on event */
+       rtc_update_irq(rtc, 1, events);
+
+       ret = IRQ_HANDLED;
+out:
+       return ret;
+}
+
+static struct rtc_class_ops twl_rtc_ops = {
+       .read_time      = palmas_rtc_read_time,
+       .set_time       = palmas_rtc_set_time,
+       .read_alarm     = palmas_rtc_read_alarm,
+       .set_alarm      = palmas_rtc_set_alarm,
+       .alarm_irq_enable = palmas_rtc_alarm_irq_enable,
+};
+
+/*----------------------------------------------------------------------*/
+
+static int palmas_rtc_probe(struct platform_device *pdev)
+{
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+       struct palmas_rtc *palmas_rtc;
+       struct rtc_device *rtc;
+       int ret = -EINVAL;
+       int irq;
+       unsigned int rd_reg;
+
+       ret = palmas_rtc_read(palmas, PALMAS_RTC_STATUS_REG, &rd_reg);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to read RTC status %d\n", rd_reg);
+               goto out1;
+       }
+
+       palmas_rtc = kzalloc(sizeof(*palmas_rtc), GFP_KERNEL);
+       if (!palmas_rtc) {
+               ret = -ENOMEM;
+               goto out1;
+       }
+
+       if (rd_reg & PALMAS_RTC_STATUS_REG_POWER_UP)
+               dev_warn(&pdev->dev, "Power up reset detected\n");
+
+       if (rd_reg & PALMAS_RTC_STATUS_REG_ALARM)
+               dev_warn(&pdev->dev, "Pending Alarm interrupt detected\n");
+
+       /* Clear RTC Power up reset and pending alarm interrupts */
+       ret = palmas_rtc_write(palmas, PALMAS_RTC_STATUS_REG, rd_reg);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to read RTC status %d\n", ret);
+               goto out2;
+       }
+
+       /* Check RTC module status, Enable if it is off */
+       ret = palmas_rtc_read(palmas, PALMAS_RTC_CTRL_REG, &rd_reg);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to read RTC ctrl %d\n", ret);
+               goto out2;
+       }
+
+       if (!(rd_reg & PALMAS_RTC_CTRL_REG_STOP_RTC)) {
+               dev_info(&pdev->dev, "Enabling Palmas RTC\n");
+               rd_reg = PALMAS_RTC_CTRL_REG_STOP_RTC;
+               ret = palmas_rtc_write(palmas, PALMAS_RTC_CTRL_REG, rd_reg);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Failed to write RTC ctrl %d\n",
+                                       ret);
+                       goto out2;
+               }
+       }
+
+       /* init cached IRQ enable bits */
+       ret = palmas_rtc_read(palmas, PALMAS_RTC_INTERRUPTS_REG,
+                       &palmas_rtc->irq_bits);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to cache IRQ bits %d\n", ret);
+               goto out2;
+       }
+
+       palmas_rtc->palmas = palmas;
+       palmas_rtc->dev = &pdev->dev;
+       platform_set_drvdata(pdev, palmas_rtc);
+
+       device_init_wakeup(&pdev->dev, 1);
+
+       rtc = rtc_device_register(pdev->name,
+                                 &pdev->dev, &twl_rtc_ops, THIS_MODULE);
+       if (IS_ERR(rtc)) {
+               ret = PTR_ERR(rtc);
+               dev_err(&pdev->dev, "can't register RTC device, err %d\n",
+                       ret);
+               goto out2;
+       }
+
+       irq = regmap_irq_get_virq(palmas->irq_data, PALMAS_RTC_ALARM_IRQ);
+       ret = request_threaded_irq(irq, NULL, palmas_rtc_interrupt,
+                                  IRQF_TRIGGER_RISING,
+                                  dev_name(&pdev->dev), palmas_rtc);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "IRQ is not free\n");
+               goto out3;
+       }
+
+       palmas_rtc->irq = irq;
+
+       return 0;
+
+out3:
+       rtc_device_unregister(rtc);
+out2:
+       kfree(palmas_rtc);
+out1:
+       return ret;
+}
+
+/*
+ * Disable all TWL RTC module interrupts.
+ * Sets status flag to free.
+ */
+static int palmas_rtc_remove(struct platform_device *pdev)
+{
+       struct palmas_rtc *palmas_rtc = platform_get_drvdata(pdev);
+       struct rtc_device *rtc = palmas_rtc->rtc;
+
+       palmas_rtc_alarm_irq_enable(&pdev->dev, 0);
+
+       free_irq(palmas_rtc->irq, palmas_rtc);
+       rtc_device_unregister(rtc);
+       kfree(palmas_rtc);
+
+       return 0;
+}
+
+static struct of_device_id of_palmas_match_tbl[] = {
+       { .compatible = "ti,palmas-rtc", },
+       { /* end */ },
+};
+
+static struct platform_driver palmas_rtc_driver = {
+       .probe = palmas_rtc_probe,
+       .remove = palmas_rtc_remove,
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "palmas-rtc",
+               .of_match_table = of_palmas_match_tbl,
+       },
+};
+
+static int __init palmas_rtc_init(void)
+{
+       return platform_driver_register(&palmas_rtc_driver);
+}
+module_init(palmas_rtc_init);
+
+static void __exit palmas_rtc_exit(void)
+{
+       platform_driver_unregister(&palmas_rtc_driver);
+}
+module_exit(palmas_rtc_exit);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("Palmas RTC Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:palmas-rtc");
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
index b610f522ca4470e341bda1f54f9481a52b37ecb7..2c02c02289d7aa2a4d81e6c08181b23b24e783ac 100644 (file)
@@ -102,6 +102,9 @@ struct omap2_mcspi_dma {
 
        struct completion dma_tx_completion;
        struct completion dma_rx_completion;
+
+       char dma_rx_ch_name[14];
+       char dma_tx_ch_name[14];
 };
 
 /* use PIO for small transfers, avoiding DMA setup/teardown overhead and
@@ -822,14 +825,23 @@ static int omap2_mcspi_request_dma(struct spi_device *spi)
        dma_cap_zero(mask);
        dma_cap_set(DMA_SLAVE, mask);
        sig = mcspi_dma->dma_rx_sync_dev;
-       mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+
+       mcspi_dma->dma_rx =
+               dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
+                                                &sig, &master->dev,
+                                                mcspi_dma->dma_rx_ch_name);
+
        if (!mcspi_dma->dma_rx) {
                dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n");
                return -EAGAIN;
        }
 
        sig = mcspi_dma->dma_tx_sync_dev;
-       mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+       mcspi_dma->dma_tx =
+               dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
+                                                &sig, &master->dev,
+                                                mcspi_dma->dma_tx_ch_name);
+
        if (!mcspi_dma->dma_tx) {
                dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n");
                dma_release_channel(mcspi_dma->dma_rx);
@@ -1223,29 +1235,42 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
                goto free_master;
 
        for (i = 0; i < master->num_chipselect; i++) {
-               char dma_ch_name[14];
+               char *dma_rx_ch_name = mcspi->dma_channels[i].dma_rx_ch_name;
+               char *dma_tx_ch_name = mcspi->dma_channels[i].dma_tx_ch_name;
                struct resource *dma_res;
 
-               sprintf(dma_ch_name, "rx%d", i);
-               dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
-                                                       dma_ch_name);
-               if (!dma_res) {
-                       dev_dbg(&pdev->dev, "cannot get DMA RX channel\n");
-                       status = -ENODEV;
-                       break;
-               }
+               sprintf(dma_rx_ch_name, "rx%d", i);
+               if (!pdev->dev.of_node) {
+                       dma_res =
+                               platform_get_resource_byname(pdev,
+                                                            IORESOURCE_DMA,
+                                                            dma_rx_ch_name);
+                       if (!dma_res) {
+                               dev_dbg(&pdev->dev,
+                                       "cannot get DMA RX channel\n");
+                               status = -ENODEV;
+                               break;
+                       }
 
-               mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start;
-               sprintf(dma_ch_name, "tx%d", i);
-               dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
-                                                       dma_ch_name);
-               if (!dma_res) {
-                       dev_dbg(&pdev->dev, "cannot get DMA TX channel\n");
-                       status = -ENODEV;
-                       break;
+                       mcspi->dma_channels[i].dma_rx_sync_dev =
+                               dma_res->start;
                }
+               sprintf(dma_tx_ch_name, "tx%d", i);
+               if (!pdev->dev.of_node) {
+                       dma_res =
+                               platform_get_resource_byname(pdev,
+                                                            IORESOURCE_DMA,
+                                                            dma_tx_ch_name);
+                       if (!dma_res) {
+                               dev_dbg(&pdev->dev,
+                                       "cannot get DMA TX channel\n");
+                               status = -ENODEV;
+                               break;
+                       }
 
-               mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start;
+                       mcspi->dma_channels[i].dma_tx_sync_dev =
+                               dma_res->start;
+               }
        }
 
        if (status < 0)
index 5d6f2ec1c705f4ede0ced96ca75efdf8495879d6..5ff3a4f1944310e1aa751cae74e30b3e069fd7dd 100644 (file)
@@ -136,6 +136,11 @@ config SSB_DRIVER_MIPS
 
          If unsure, say N
 
+config SSB_SFLASH
+       bool "SSB serial flash support"
+       depends on SSB_DRIVER_MIPS && BROKEN
+       default y
+
 # Assumption: We are on embedded, if we compile the MIPS core.
 config SSB_EMBEDDED
        bool
index 9159ba77c388086a06196767da05d87150d3aacd..b1ddc116d387c47119408ed07d71d3274cce0691 100644 (file)
@@ -11,6 +11,7 @@ ssb-$(CONFIG_SSB_SDIOHOST)            += sdio.o
 # built-in drivers
 ssb-y                                  += driver_chipcommon.o
 ssb-y                                  += driver_chipcommon_pmu.o
+ssb-$(CONFIG_SSB_SFLASH)               += driver_chipcommon_sflash.o
 ssb-$(CONFIG_SSB_DRIVER_MIPS)          += driver_mipscore.o
 ssb-$(CONFIG_SSB_DRIVER_EXTIF)         += driver_extif.o
 ssb-$(CONFIG_SSB_DRIVER_PCICORE)       += driver_pcicore.o
diff --git a/drivers/ssb/driver_chipcommon_sflash.c b/drivers/ssb/driver_chipcommon_sflash.c
new file mode 100644 (file)
index 0000000..720665c
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Sonics Silicon Backplane
+ * ChipCommon serial flash interface
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+
+#include "ssb_private.h"
+
+struct ssb_sflash_tbl_e {
+       char *name;
+       u32 id;
+       u32 blocksize;
+       u16 numblocks;
+};
+
+static struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
+       { "M25P20", 0x11, 0x10000, 4, },
+       { "M25P40", 0x12, 0x10000, 8, },
+
+       { "M25P16", 0x14, 0x10000, 32, },
+       { "M25P32", 0x15, 0x10000, 64, },
+       { "M25P64", 0x16, 0x10000, 128, },
+       { "M25FL128", 0x17, 0x10000, 256, },
+       { 0 },
+};
+
+static struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
+       { "SST25WF512", 1, 0x1000, 16, },
+       { "SST25VF512", 0x48, 0x1000, 16, },
+       { "SST25WF010", 2, 0x1000, 32, },
+       { "SST25VF010", 0x49, 0x1000, 32, },
+       { "SST25WF020", 3, 0x1000, 64, },
+       { "SST25VF020", 0x43, 0x1000, 64, },
+       { "SST25WF040", 4, 0x1000, 128, },
+       { "SST25VF040", 0x44, 0x1000, 128, },
+       { "SST25VF040B", 0x8d, 0x1000, 128, },
+       { "SST25WF080", 5, 0x1000, 256, },
+       { "SST25VF080B", 0x8e, 0x1000, 256, },
+       { "SST25VF016", 0x41, 0x1000, 512, },
+       { "SST25VF032", 0x4a, 0x1000, 1024, },
+       { "SST25VF064", 0x4b, 0x1000, 2048, },
+       { 0 },
+};
+
+static struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = {
+       { "AT45DB011", 0xc, 256, 512, },
+       { "AT45DB021", 0x14, 256, 1024, },
+       { "AT45DB041", 0x1c, 256, 2048, },
+       { "AT45DB081", 0x24, 256, 4096, },
+       { "AT45DB161", 0x2c, 512, 4096, },
+       { "AT45DB321", 0x34, 512, 8192, },
+       { "AT45DB642", 0x3c, 1024, 8192, },
+       { 0 },
+};
+
+static void ssb_sflash_cmd(struct ssb_chipcommon *cc, u32 opcode)
+{
+       int i;
+       chipco_write32(cc, SSB_CHIPCO_FLASHCTL,
+                      SSB_CHIPCO_FLASHCTL_START | opcode);
+       for (i = 0; i < 1000; i++) {
+               if (!(chipco_read32(cc, SSB_CHIPCO_FLASHCTL) &
+                     SSB_CHIPCO_FLASHCTL_BUSY))
+                       return;
+               cpu_relax();
+       }
+       pr_err("SFLASH control command failed (timeout)!\n");
+}
+
+/* Initialize serial flash access */
+int ssb_sflash_init(struct ssb_chipcommon *cc)
+{
+       struct ssb_sflash_tbl_e *e;
+       u32 id, id2;
+
+       switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) {
+       case SSB_CHIPCO_FLASHT_STSER:
+               ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_DP);
+
+               chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 0);
+               ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES);
+               id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA);
+
+               chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 1);
+               ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES);
+               id2 = chipco_read32(cc, SSB_CHIPCO_FLASHDATA);
+
+               switch (id) {
+               case 0xbf:
+                       for (e = ssb_sflash_sst_tbl; e->name; e++) {
+                               if (e->id == id2)
+                                       break;
+                       }
+                       break;
+               case 0x13:
+                       return -ENOTSUPP;
+               default:
+                       for (e = ssb_sflash_st_tbl; e->name; e++) {
+                               if (e->id == id)
+                                       break;
+                       }
+                       break;
+               }
+               if (!e->name) {
+                       pr_err("Unsupported ST serial flash (id: 0x%X, id2: 0x%X)\n",
+                              id, id2);
+                       return -ENOTSUPP;
+               }
+
+               break;
+       case SSB_CHIPCO_FLASHT_ATSER:
+               ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_STATUS);
+               id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA) & 0x3c;
+
+               for (e = ssb_sflash_at_tbl; e->name; e++) {
+                       if (e->id == id)
+                               break;
+               }
+               if (!e->name) {
+                       pr_err("Unsupported Atmel serial flash (id: 0x%X)\n",
+                              id);
+                       return -ENOTSUPP;
+               }
+
+               break;
+       default:
+               pr_err("Unsupported flash type\n");
+               return -ENOTSUPP;
+       }
+
+       pr_info("Found %s serial flash (blocksize: 0x%X, blocks: %d)\n",
+               e->name, e->blocksize, e->numblocks);
+
+       pr_err("Serial flash support is not implemented yet!\n");
+
+       return -ENOTSUPP;
+}
index eb2753008ef0550b6c3bfeda984ca9dfe47d26bd..dc109de228c67b079f5eed0623fc3e1d94451b6e 100644 (file)
@@ -74,6 +74,16 @@ static void ssb_gpio_chipco_free(struct gpio_chip *chip, unsigned gpio)
        ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 0);
 }
 
+static int ssb_gpio_chipco_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+       struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+       if (bus->bustype == SSB_BUSTYPE_SSB)
+               return ssb_mips_irq(bus->chipco.dev) + 2;
+       else
+               return -EINVAL;
+}
+
 static int ssb_gpio_chipco_init(struct ssb_bus *bus)
 {
        struct gpio_chip *chip = &bus->gpio;
@@ -86,6 +96,7 @@ static int ssb_gpio_chipco_init(struct ssb_bus *bus)
        chip->set               = ssb_gpio_chipco_set_value;
        chip->direction_input   = ssb_gpio_chipco_direction_input;
        chip->direction_output  = ssb_gpio_chipco_direction_output;
+       chip->to_irq            = ssb_gpio_chipco_to_irq;
        chip->ngpio             = 16;
        /* There is just one SoC in one device and its GPIO addresses should be
         * deterministic to address them more easily. The other buses could get
@@ -134,6 +145,16 @@ static int ssb_gpio_extif_direction_output(struct gpio_chip *chip,
        return 0;
 }
 
+static int ssb_gpio_extif_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+       struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+       if (bus->bustype == SSB_BUSTYPE_SSB)
+               return ssb_mips_irq(bus->extif.dev) + 2;
+       else
+               return -EINVAL;
+}
+
 static int ssb_gpio_extif_init(struct ssb_bus *bus)
 {
        struct gpio_chip *chip = &bus->gpio;
@@ -144,6 +165,7 @@ static int ssb_gpio_extif_init(struct ssb_bus *bus)
        chip->set               = ssb_gpio_extif_set_value;
        chip->direction_input   = ssb_gpio_extif_direction_input;
        chip->direction_output  = ssb_gpio_extif_direction_output;
+       chip->to_irq            = ssb_gpio_extif_to_irq;
        chip->ngpio             = 5;
        /* There is just one SoC in one device and its GPIO addresses should be
         * deterministic to address them more easily. The other buses could get
index 5bd05b136d22ef80b2cd93f78b65d6ef60640473..33b37dac40bddc4bc21078a63b3b2d0861d62ec1 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <linux/ssb/ssb.h>
 
+#include <linux/mtd/physmap.h>
 #include <linux/serial.h>
 #include <linux/serial_core.h>
 #include <linux/serial_reg.h>
 
 #include "ssb_private.h"
 
+static const char *part_probes[] = { "bcm47xxpart", NULL };
+
+static struct physmap_flash_data ssb_pflash_data = {
+       .part_probe_types       = part_probes,
+};
+
+static struct resource ssb_pflash_resource = {
+       .name   = "ssb_pflash",
+       .flags  = IORESOURCE_MEM,
+};
+
+struct platform_device ssb_pflash_dev = {
+       .name           = "physmap-flash",
+       .dev            = {
+               .platform_data  = &ssb_pflash_data,
+       },
+       .resource       = &ssb_pflash_resource,
+       .num_resources  = 1,
+};
 
 static inline u32 mips_read32(struct ssb_mipscore *mcore,
                              u16 offset)
@@ -189,34 +209,43 @@ static void ssb_mips_serial_init(struct ssb_mipscore *mcore)
 static void ssb_mips_flash_detect(struct ssb_mipscore *mcore)
 {
        struct ssb_bus *bus = mcore->dev->bus;
+       struct ssb_pflash *pflash = &mcore->pflash;
 
        /* When there is no chipcommon on the bus there is 4MB flash */
        if (!ssb_chipco_available(&bus->chipco)) {
-               mcore->pflash.present = true;
-               mcore->pflash.buswidth = 2;
-               mcore->pflash.window = SSB_FLASH1;
-               mcore->pflash.window_size = SSB_FLASH1_SZ;
-               return;
+               pflash->present = true;
+               pflash->buswidth = 2;
+               pflash->window = SSB_FLASH1;
+               pflash->window_size = SSB_FLASH1_SZ;
+               goto ssb_pflash;
        }
 
        /* There is ChipCommon, so use it to read info about flash */
        switch (bus->chipco.capabilities & SSB_CHIPCO_CAP_FLASHT) {
        case SSB_CHIPCO_FLASHT_STSER:
        case SSB_CHIPCO_FLASHT_ATSER:
-               pr_err("Serial flash not supported\n");
+               pr_debug("Found serial flash\n");
+               ssb_sflash_init(&bus->chipco);
                break;
        case SSB_CHIPCO_FLASHT_PARA:
                pr_debug("Found parallel flash\n");
-               mcore->pflash.present = true;
-               mcore->pflash.window = SSB_FLASH2;
-               mcore->pflash.window_size = SSB_FLASH2_SZ;
+               pflash->present = true;
+               pflash->window = SSB_FLASH2;
+               pflash->window_size = SSB_FLASH2_SZ;
                if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG)
                               & SSB_CHIPCO_CFG_DS16) == 0)
-                       mcore->pflash.buswidth = 1;
+                       pflash->buswidth = 1;
                else
-                       mcore->pflash.buswidth = 2;
+                       pflash->buswidth = 2;
                break;
        }
+
+ssb_pflash:
+       if (pflash->present) {
+               ssb_pflash_data.width = pflash->buswidth;
+               ssb_pflash_resource.start = pflash->window;
+               ssb_pflash_resource.end = pflash->window + pflash->window_size;
+       }
 }
 
 u32 ssb_cpu_clock(struct ssb_mipscore *mcore)
index 24dc331b4701efd796381882e635c6ef791c45cf..3b645b8a261fa3476bdc05446ec11bc6a4d8f3de 100644 (file)
@@ -549,6 +549,14 @@ static int ssb_devices_register(struct ssb_bus *bus)
                dev_idx++;
        }
 
+#ifdef CONFIG_SSB_DRIVER_MIPS
+       if (bus->mipscore.pflash.present) {
+               err = platform_device_register(&ssb_pflash_dev);
+               if (err)
+                       pr_err("Error registering parallel flash\n");
+       }
+#endif
+
        return 0;
 error:
        /* Unwind the already registered devices. */
index da38305a2d22a2c24de7b4563da7ed0daa7354a0..466171b77f68f74d0ebfd7c488a31af9006aebb7 100644 (file)
@@ -217,6 +217,21 @@ extern u32 ssb_chipco_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt,
                                             u32 ticks);
 extern u32 ssb_chipco_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms);
 
+/* driver_chipcommon_sflash.c */
+#ifdef CONFIG_SSB_SFLASH
+int ssb_sflash_init(struct ssb_chipcommon *cc);
+#else
+static inline int ssb_sflash_init(struct ssb_chipcommon *cc)
+{
+       pr_err("Serial flash not supported\n");
+       return 0;
+}
+#endif /* CONFIG_SSB_SFLASH */
+
+#ifdef CONFIG_SSB_DRIVER_MIPS
+extern struct platform_device ssb_pflash_dev;
+#endif
+
 #ifdef CONFIG_SSB_DRIVER_EXTIF
 extern u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks);
 extern u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms);
index 329bdb42109f9a726bec9ebd74359a2f97bfd287..84eba0728844227bbb871fc7af850f649f89c357 100644 (file)
@@ -126,7 +126,7 @@ source "drivers/staging/gdm72xx/Kconfig"
 
 source "drivers/staging/csr/Kconfig"
 
-source "drivers/staging/omap-thermal/Kconfig"
+source "drivers/staging/ti-soc-thermal/Kconfig"
 
 source "drivers/staging/ramster/Kconfig"
 
index c7ec486680f7f460226bae0444e16db604d9fc79..5cce2cfe374c06fc6099cb0f7ba627d7a795f2d8 100644 (file)
@@ -55,7 +55,7 @@ obj-$(CONFIG_USB_WPAN_HCD)    += ozwpan/
 obj-$(CONFIG_USB_G_CCG)                += ccg/
 obj-$(CONFIG_WIMAX_GDM72XX)    += gdm72xx/
 obj-$(CONFIG_CSR_WIFI)         += csr/
-obj-$(CONFIG_OMAP_BANDGAP)     += omap-thermal/
+obj-$(CONFIG_TI_SOC_THERMAL)   += ti-soc-thermal/
 obj-$(CONFIG_ZCACHE2)          += ramster/
 obj-$(CONFIG_NET_VENDOR_SILICOM)       += silicom/
 obj-$(CONFIG_CED1401)          += ced1401/
diff --git a/drivers/staging/omap-thermal/Makefile b/drivers/staging/omap-thermal/Makefile
deleted file mode 100644 (file)
index 091c4d2..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-obj-$(CONFIG_OMAP_BANDGAP)     += omap-thermal.o
-omap-thermal-y                 := omap-bandgap.o
-omap-thermal-$(CONFIG_OMAP_THERMAL)    += omap-thermal-common.o
-omap-thermal-$(CONFIG_OMAP4_THERMAL)   += omap4-thermal.o
-omap-thermal-$(CONFIG_OMAP5_THERMAL)   += omap5-thermal.o
diff --git a/drivers/staging/omap-thermal/omap-bandgap.c b/drivers/staging/omap-thermal/omap-bandgap.c
deleted file mode 100644 (file)
index 8346e34..0000000
+++ /dev/null
@@ -1,1179 +0,0 @@
-/*
- * OMAP4 Bandgap temperature sensor driver
- *
- * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/
- * Author: J Keerthy <j-keerthy@ti.com>
- * Author: Moiz Sonasath <m-sonasath@ti.com>
- * Couple of fixes, DT and MFD adaptation:
- *   Eduardo Valentin <eduardo.valentin@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/export.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
-#include <linux/gpio.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/types.h>
-#include <linux/mutex.h>
-#include <linux/reboot.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
-#include <linux/of_irq.h>
-#include <linux/io.h>
-
-#include "omap-bandgap.h"
-
-static u32 omap_bandgap_readl(struct omap_bandgap *bg_ptr, u32 reg)
-{
-       return readl(bg_ptr->base + reg);
-}
-
-static void omap_bandgap_writel(struct omap_bandgap *bg_ptr, u32 val, u32 reg)
-{
-       writel(val, bg_ptr->base + reg);
-}
-
-static int omap_bandgap_power(struct omap_bandgap *bg_ptr, bool on)
-{
-       struct temp_sensor_registers *tsr;
-       int i;
-       u32 ctrl;
-
-       if (!OMAP_BANDGAP_HAS(bg_ptr, POWER_SWITCH))
-               return 0;
-
-       for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
-               tsr = bg_ptr->conf->sensors[i].registers;
-               ctrl = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
-               ctrl &= ~tsr->bgap_tempsoff_mask;
-               /* active on 0 */
-               ctrl |= !on << __ffs(tsr->bgap_tempsoff_mask);
-
-               /* write BGAP_TEMPSOFF should be reset to 0 */
-               omap_bandgap_writel(bg_ptr, ctrl, tsr->temp_sensor_ctrl);
-       }
-
-       return 0;
-}
-
-/* This is the Talert handler. Call it only if HAS(TALERT) is set */
-static irqreturn_t talert_irq_handler(int irq, void *data)
-{
-       struct omap_bandgap *bg_ptr = data;
-       struct temp_sensor_registers *tsr;
-       u32 t_hot = 0, t_cold = 0, temp, ctrl;
-       int i;
-
-       bg_ptr = data;
-       /* Read the status of t_hot */
-       for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
-               tsr = bg_ptr->conf->sensors[i].registers;
-               t_hot = omap_bandgap_readl(bg_ptr, tsr->bgap_status);
-               t_hot &= tsr->status_hot_mask;
-
-               /* Read the status of t_cold */
-               t_cold = omap_bandgap_readl(bg_ptr, tsr->bgap_status);
-               t_cold &= tsr->status_cold_mask;
-
-               if (!t_cold && !t_hot)
-                       continue;
-
-               ctrl = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl);
-               /*
-                * One TALERT interrupt: Two sources
-                * If the interrupt is due to t_hot then mask t_hot and
-                * and unmask t_cold else mask t_cold and unmask t_hot
-                */
-               if (t_hot) {
-                       ctrl &= ~tsr->mask_hot_mask;
-                       ctrl |= tsr->mask_cold_mask;
-               } else if (t_cold) {
-                       ctrl &= ~tsr->mask_cold_mask;
-                       ctrl |= tsr->mask_hot_mask;
-               }
-
-               omap_bandgap_writel(bg_ptr, ctrl, tsr->bgap_mask_ctrl);
-
-               dev_dbg(bg_ptr->dev,
-                       "%s: IRQ from %s sensor: hotevent %d coldevent %d\n",
-                       __func__, bg_ptr->conf->sensors[i].domain,
-                       t_hot, t_cold);
-
-               /* read temperature */
-               temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
-               temp &= tsr->bgap_dtemp_mask;
-
-               /* report temperature to whom may concern */
-               if (bg_ptr->conf->report_temperature)
-                       bg_ptr->conf->report_temperature(bg_ptr, i);
-       }
-
-       return IRQ_HANDLED;
-}
-
-/* This is the Tshut handler. Call it only if HAS(TSHUT) is set */
-static irqreturn_t omap_bandgap_tshut_irq_handler(int irq, void *data)
-{
-       orderly_poweroff(true);
-
-       return IRQ_HANDLED;
-}
-
-static
-int adc_to_temp_conversion(struct omap_bandgap *bg_ptr, int id, int adc_val,
-                          int *t)
-{
-       struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data;
-
-       /* look up for temperature in the table and return the temperature */
-       if (adc_val < ts_data->adc_start_val || adc_val > ts_data->adc_end_val)
-               return -ERANGE;
-
-       *t = bg_ptr->conv_table[adc_val - ts_data->adc_start_val];
-
-       return 0;
-}
-
-static int temp_to_adc_conversion(long temp, struct omap_bandgap *bg_ptr, int i,
-                                 int *adc)
-{
-       struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[i].ts_data;
-       int high, low, mid;
-
-       low = 0;
-       high = ts_data->adc_end_val - ts_data->adc_start_val;
-       mid = (high + low) / 2;
-
-       if (temp < bg_ptr->conv_table[low] || temp > bg_ptr->conv_table[high])
-               return -EINVAL;
-
-       while (low < high) {
-               if (temp < bg_ptr->conv_table[mid])
-                       high = mid - 1;
-               else
-                       low = mid + 1;
-               mid = (low + high) / 2;
-       }
-
-       *adc = ts_data->adc_start_val + low;
-
-       return 0;
-}
-
-/* Talert masks. Call it only if HAS(TALERT) is set */
-static int temp_sensor_unmask_interrupts(struct omap_bandgap *bg_ptr, int id,
-                                        u32 t_hot, u32 t_cold)
-{
-       struct temp_sensor_registers *tsr;
-       u32 temp, reg_val;
-
-       /* Read the current on die temperature */
-       tsr = bg_ptr->conf->sensors[id].registers;
-       temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
-       temp &= tsr->bgap_dtemp_mask;
-
-       reg_val = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl);
-       if (temp < t_hot)
-               reg_val |= tsr->mask_hot_mask;
-       else
-               reg_val &= ~tsr->mask_hot_mask;
-
-       if (t_cold < temp)
-               reg_val |= tsr->mask_cold_mask;
-       else
-               reg_val &= ~tsr->mask_cold_mask;
-       omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_mask_ctrl);
-
-       return 0;
-}
-
-static
-int add_hyst(int adc_val, int hyst_val, struct omap_bandgap *bg_ptr, int i,
-            u32 *sum)
-{
-       int temp, ret;
-
-       ret = adc_to_temp_conversion(bg_ptr, i, adc_val, &temp);
-       if (ret < 0)
-               return ret;
-
-       temp += hyst_val;
-
-       return temp_to_adc_conversion(temp, bg_ptr, i, sum);
-}
-
-/* Talert Thot threshold. Call it only if HAS(TALERT) is set */
-static
-int temp_sensor_configure_thot(struct omap_bandgap *bg_ptr, int id, int t_hot)
-{
-       struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data;
-       struct temp_sensor_registers *tsr;
-       u32 thresh_val, reg_val;
-       int cold, err = 0;
-
-       tsr = bg_ptr->conf->sensors[id].registers;
-
-       /* obtain the T cold value */
-       thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
-       cold = (thresh_val & tsr->threshold_tcold_mask) >>
-           __ffs(tsr->threshold_tcold_mask);
-       if (t_hot <= cold) {
-               /* change the t_cold to t_hot - 5000 millidegrees */
-               err |= add_hyst(t_hot, -ts_data->hyst_val, bg_ptr, id, &cold);
-               /* write the new t_cold value */
-               reg_val = thresh_val & (~tsr->threshold_tcold_mask);
-               reg_val |= cold << __ffs(tsr->threshold_tcold_mask);
-               omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
-               thresh_val = reg_val;
-       }
-
-       /* write the new t_hot value */
-       reg_val = thresh_val & ~tsr->threshold_thot_mask;
-       reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask));
-       omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
-       if (err) {
-               dev_err(bg_ptr->dev, "failed to reprogram thot threshold\n");
-               return -EIO;
-       }
-
-       return temp_sensor_unmask_interrupts(bg_ptr, id, t_hot, cold);
-}
-
-/* Talert Thot and Tcold thresholds. Call it only if HAS(TALERT) is set */
-static
-int temp_sensor_init_talert_thresholds(struct omap_bandgap *bg_ptr, int id,
-                                      int t_hot, int t_cold)
-{
-       struct temp_sensor_registers *tsr;
-       u32 reg_val, thresh_val;
-
-       tsr = bg_ptr->conf->sensors[id].registers;
-       thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
-
-       /* write the new t_cold value */
-       reg_val = thresh_val & ~tsr->threshold_tcold_mask;
-       reg_val |= (t_cold << __ffs(tsr->threshold_tcold_mask));
-       omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
-
-       thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
-
-       /* write the new t_hot value */
-       reg_val = thresh_val & ~tsr->threshold_thot_mask;
-       reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask));
-       omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
-
-       reg_val = omap_bandgap_readl(bg_ptr, tsr->bgap_mask_ctrl);
-       reg_val |= tsr->mask_hot_mask;
-       reg_val |= tsr->mask_cold_mask;
-       omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_mask_ctrl);
-
-       return 0;
-}
-
-/* Talert Tcold threshold. Call it only if HAS(TALERT) is set */
-static
-int temp_sensor_configure_tcold(struct omap_bandgap *bg_ptr, int id,
-                               int t_cold)
-{
-       struct temp_sensor_data *ts_data = bg_ptr->conf->sensors[id].ts_data;
-       struct temp_sensor_registers *tsr;
-       u32 thresh_val, reg_val;
-       int hot, err = 0;
-
-       tsr = bg_ptr->conf->sensors[id].registers;
-       /* obtain the T cold value */
-       thresh_val = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
-       hot = (thresh_val & tsr->threshold_thot_mask) >>
-           __ffs(tsr->threshold_thot_mask);
-
-       if (t_cold >= hot) {
-               /* change the t_hot to t_cold + 5000 millidegrees */
-               err |= add_hyst(t_cold, ts_data->hyst_val, bg_ptr, id, &hot);
-               /* write the new t_hot value */
-               reg_val = thresh_val & (~tsr->threshold_thot_mask);
-               reg_val |= hot << __ffs(tsr->threshold_thot_mask);
-               omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
-               thresh_val = reg_val;
-       }
-
-       /* write the new t_cold value */
-       reg_val = thresh_val & ~tsr->threshold_tcold_mask;
-       reg_val |= (t_cold << __ffs(tsr->threshold_tcold_mask));
-       omap_bandgap_writel(bg_ptr, reg_val, tsr->bgap_threshold);
-       if (err) {
-               dev_err(bg_ptr->dev, "failed to reprogram tcold threshold\n");
-               return -EIO;
-       }
-
-       return temp_sensor_unmask_interrupts(bg_ptr, id, hot, t_cold);
-}
-
-/* This is Tshut Thot config. Call it only if HAS(TSHUT_CONFIG) is set */
-static int temp_sensor_configure_tshut_hot(struct omap_bandgap *bg_ptr,
-                                          int id, int tshut_hot)
-{
-       struct temp_sensor_registers *tsr;
-       u32 reg_val;
-
-       tsr = bg_ptr->conf->sensors[id].registers;
-       reg_val = omap_bandgap_readl(bg_ptr, tsr->tshut_threshold);
-       reg_val &= ~tsr->tshut_hot_mask;
-       reg_val |= tshut_hot << __ffs(tsr->tshut_hot_mask);
-       omap_bandgap_writel(bg_ptr, reg_val, tsr->tshut_threshold);
-
-       return 0;
-}
-
-/* This is Tshut Tcold config. Call it only if HAS(TSHUT_CONFIG) is set */
-static int temp_sensor_configure_tshut_cold(struct omap_bandgap *bg_ptr,
-                                           int id, int tshut_cold)
-{
-       struct temp_sensor_registers *tsr;
-       u32 reg_val;
-
-       tsr = bg_ptr->conf->sensors[id].registers;
-       reg_val = omap_bandgap_readl(bg_ptr, tsr->tshut_threshold);
-       reg_val &= ~tsr->tshut_cold_mask;
-       reg_val |= tshut_cold << __ffs(tsr->tshut_cold_mask);
-       omap_bandgap_writel(bg_ptr, reg_val, tsr->tshut_threshold);
-
-       return 0;
-}
-
-/* This is counter config. Call it only if HAS(COUNTER) is set */
-static int configure_temp_sensor_counter(struct omap_bandgap *bg_ptr, int id,
-                                        u32 counter)
-{
-       struct temp_sensor_registers *tsr;
-       u32 val;
-
-       tsr = bg_ptr->conf->sensors[id].registers;
-       val = omap_bandgap_readl(bg_ptr, tsr->bgap_counter);
-       val &= ~tsr->counter_mask;
-       val |= counter << __ffs(tsr->counter_mask);
-       omap_bandgap_writel(bg_ptr, val, tsr->bgap_counter);
-
-       return 0;
-}
-
-#define bandgap_is_valid(b)                                            \
-                       (!IS_ERR_OR_NULL(b))
-#define bandgap_is_valid_sensor_id(b, i)                               \
-                       ((i) >= 0 && (i) < (b)->conf->sensor_count)
-static inline int omap_bandgap_validate(struct omap_bandgap *bg_ptr, int id)
-{
-       if (!bandgap_is_valid(bg_ptr)) {
-               pr_err("%s: invalid bandgap pointer\n", __func__);
-               return -EINVAL;
-       }
-
-       if (!bandgap_is_valid_sensor_id(bg_ptr, id)) {
-               dev_err(bg_ptr->dev, "%s: sensor id out of range (%d)\n",
-                       __func__, id);
-               return -ERANGE;
-       }
-
-       return 0;
-}
-
-/* Exposed APIs */
-/**
- * omap_bandgap_read_thot() - reads sensor current thot
- * @bg_ptr - pointer to bandgap instance
- * @id - sensor id
- * @thot - resulting current thot value
- *
- * returns 0 on success or the proper error code
- */
-int omap_bandgap_read_thot(struct omap_bandgap *bg_ptr, int id,
-                             int *thot)
-{
-       struct temp_sensor_registers *tsr;
-       u32 temp;
-       int ret;
-
-       ret = omap_bandgap_validate(bg_ptr, id);
-       if (ret)
-               return ret;
-
-       if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
-               return -ENOTSUPP;
-
-       tsr = bg_ptr->conf->sensors[id].registers;
-       temp = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
-       temp = (temp & tsr->threshold_thot_mask) >>
-               __ffs(tsr->threshold_thot_mask);
-       ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp);
-       if (ret) {
-               dev_err(bg_ptr->dev, "failed to read thot\n");
-               return -EIO;
-       }
-
-       *thot = temp;
-
-       return 0;
-}
-
-/**
- * omap_bandgap_write_thot() - sets sensor current thot
- * @bg_ptr - pointer to bandgap instance
- * @id - sensor id
- * @val - desired thot value
- *
- * returns 0 on success or the proper error code
- */
-int omap_bandgap_write_thot(struct omap_bandgap *bg_ptr, int id, int val)
-{
-       struct temp_sensor_data *ts_data;
-       struct temp_sensor_registers *tsr;
-       u32 t_hot;
-       int ret;
-
-       ret = omap_bandgap_validate(bg_ptr, id);
-       if (ret)
-               return ret;
-
-       if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
-               return -ENOTSUPP;
-
-       ts_data = bg_ptr->conf->sensors[id].ts_data;
-       tsr = bg_ptr->conf->sensors[id].registers;
-
-       if (val < ts_data->min_temp + ts_data->hyst_val)
-               return -EINVAL;
-       ret = temp_to_adc_conversion(val, bg_ptr, id, &t_hot);
-       if (ret < 0)
-               return ret;
-
-       mutex_lock(&bg_ptr->bg_mutex);
-       temp_sensor_configure_thot(bg_ptr, id, t_hot);
-       mutex_unlock(&bg_ptr->bg_mutex);
-
-       return 0;
-}
-
-/**
- * omap_bandgap_read_tcold() - reads sensor current tcold
- * @bg_ptr - pointer to bandgap instance
- * @id - sensor id
- * @tcold - resulting current tcold value
- *
- * returns 0 on success or the proper error code
- */
-int omap_bandgap_read_tcold(struct omap_bandgap *bg_ptr, int id,
-                              int *tcold)
-{
-       struct temp_sensor_registers *tsr;
-       u32 temp;
-       int ret;
-
-       ret = omap_bandgap_validate(bg_ptr, id);
-       if (ret)
-               return ret;
-
-       if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
-               return -ENOTSUPP;
-
-       tsr = bg_ptr->conf->sensors[id].registers;
-       temp = omap_bandgap_readl(bg_ptr, tsr->bgap_threshold);
-       temp = (temp & tsr->threshold_tcold_mask)
-           >> __ffs(tsr->threshold_tcold_mask);
-       ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp);
-       if (ret)
-               return -EIO;
-
-       *tcold = temp;
-
-       return 0;
-}
-
-/**
- * omap_bandgap_write_tcold() - sets the sensor tcold
- * @bg_ptr - pointer to bandgap instance
- * @id - sensor id
- * @val - desired tcold value
- *
- * returns 0 on success or the proper error code
- */
-int omap_bandgap_write_tcold(struct omap_bandgap *bg_ptr, int id, int val)
-{
-       struct temp_sensor_data *ts_data;
-       struct temp_sensor_registers *tsr;
-       u32 t_cold;
-       int ret;
-
-       ret = omap_bandgap_validate(bg_ptr, id);
-       if (ret)
-               return ret;
-
-       if (!OMAP_BANDGAP_HAS(bg_ptr, TALERT))
-               return -ENOTSUPP;
-
-       ts_data = bg_ptr->conf->sensors[id].ts_data;
-       tsr = bg_ptr->conf->sensors[id].registers;
-       if (val > ts_data->max_temp + ts_data->hyst_val)
-               return -EINVAL;
-
-       ret = temp_to_adc_conversion(val, bg_ptr, id, &t_cold);
-       if (ret < 0)
-               return ret;
-
-       mutex_lock(&bg_ptr->bg_mutex);
-       temp_sensor_configure_tcold(bg_ptr, id, t_cold);
-       mutex_unlock(&bg_ptr->bg_mutex);
-
-       return 0;
-}
-
-/**
- * omap_bandgap_read_update_interval() - read the sensor update interval
- * @bg_ptr - pointer to bandgap instance
- * @id - sensor id
- * @interval - resulting update interval in miliseconds
- *
- * returns 0 on success or the proper error code
- */
-int omap_bandgap_read_update_interval(struct omap_bandgap *bg_ptr, int id,
-                                        int *interval)
-{
-       struct temp_sensor_registers *tsr;
-       u32 time;
-       int ret;
-
-       ret = omap_bandgap_validate(bg_ptr, id);
-       if (ret)
-               return ret;
-
-       if (!OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
-               return -ENOTSUPP;
-
-       tsr = bg_ptr->conf->sensors[id].registers;
-       time = omap_bandgap_readl(bg_ptr, tsr->bgap_counter);
-       if (ret)
-               return ret;
-       time = (time & tsr->counter_mask) >> __ffs(tsr->counter_mask);
-       time = time * 1000 / bg_ptr->clk_rate;
-
-       *interval = time;
-
-       return 0;
-}
-
-/**
- * omap_bandgap_write_update_interval() - set the update interval
- * @bg_ptr - pointer to bandgap instance
- * @id - sensor id
- * @interval - desired update interval in miliseconds
- *
- * returns 0 on success or the proper error code
- */
-int omap_bandgap_write_update_interval(struct omap_bandgap *bg_ptr,
-                                         int id, u32 interval)
-{
-       int ret = omap_bandgap_validate(bg_ptr, id);
-       if (ret)
-               return ret;
-
-       if (!OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
-               return -ENOTSUPP;
-
-       interval = interval * bg_ptr->clk_rate / 1000;
-       mutex_lock(&bg_ptr->bg_mutex);
-       configure_temp_sensor_counter(bg_ptr, id, interval);
-       mutex_unlock(&bg_ptr->bg_mutex);
-
-       return 0;
-}
-
-/**
- * omap_bandgap_read_temperature() - report current temperature
- * @bg_ptr - pointer to bandgap instance
- * @id - sensor id
- * @temperature - resulting temperature
- *
- * returns 0 on success or the proper error code
- */
-int omap_bandgap_read_temperature(struct omap_bandgap *bg_ptr, int id,
-                                    int *temperature)
-{
-       struct temp_sensor_registers *tsr;
-       u32 temp;
-       int ret;
-
-       ret = omap_bandgap_validate(bg_ptr, id);
-       if (ret)
-               return ret;
-
-       tsr = bg_ptr->conf->sensors[id].registers;
-       temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
-       temp &= tsr->bgap_dtemp_mask;
-
-       ret |= adc_to_temp_conversion(bg_ptr, id, temp, &temp);
-       if (ret)
-               return -EIO;
-
-       *temperature = temp;
-
-       return 0;
-}
-
-/**
- * omap_bandgap_set_sensor_data() - helper function to store thermal
- * framework related data.
- * @bg_ptr - pointer to bandgap instance
- * @id - sensor id
- * @data - thermal framework related data to be stored
- *
- * returns 0 on success or the proper error code
- */
-int omap_bandgap_set_sensor_data(struct omap_bandgap *bg_ptr, int id,
-                               void *data)
-{
-       int ret = omap_bandgap_validate(bg_ptr, id);
-       if (ret)
-               return ret;
-
-       bg_ptr->conf->sensors[id].data = data;
-
-       return 0;
-}
-
-/**
- * omap_bandgap_get_sensor_data() - helper function to get thermal
- * framework related data.
- * @bg_ptr - pointer to bandgap instance
- * @id - sensor id
- *
- * returns data stored by set function with sensor id on success or NULL
- */
-void *omap_bandgap_get_sensor_data(struct omap_bandgap *bg_ptr, int id)
-{
-       int ret = omap_bandgap_validate(bg_ptr, id);
-       if (ret)
-               return ERR_PTR(ret);
-
-       return bg_ptr->conf->sensors[id].data;
-}
-
-static int
-omap_bandgap_force_single_read(struct omap_bandgap *bg_ptr, int id)
-{
-       struct temp_sensor_registers *tsr;
-       u32 temp = 0, counter = 1000;
-
-       tsr = bg_ptr->conf->sensors[id].registers;
-       /* Select single conversion mode */
-       if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG)) {
-               temp = omap_bandgap_readl(bg_ptr, tsr->bgap_mode_ctrl);
-               temp &= ~(1 << __ffs(tsr->mode_ctrl_mask));
-               omap_bandgap_writel(bg_ptr, temp, tsr->bgap_mode_ctrl);
-       }
-
-       /* Start of Conversion = 1 */
-       temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
-       temp |= 1 << __ffs(tsr->bgap_soc_mask);
-       omap_bandgap_writel(bg_ptr, temp, tsr->temp_sensor_ctrl);
-       /* Wait until DTEMP is updated */
-       temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
-       temp &= (tsr->bgap_dtemp_mask);
-       while ((temp == 0) && --counter) {
-               temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
-               temp &= (tsr->bgap_dtemp_mask);
-       }
-       /* Start of Conversion = 0 */
-       temp = omap_bandgap_readl(bg_ptr, tsr->temp_sensor_ctrl);
-       temp &= ~(1 << __ffs(tsr->bgap_soc_mask));
-       omap_bandgap_writel(bg_ptr, temp, tsr->temp_sensor_ctrl);
-
-       return 0;
-}
-
-/**
- * enable_continuous_mode() - One time enabling of continuous conversion mode
- * @bg_ptr - pointer to scm instance
- *
- * Call this function only if HAS(MODE_CONFIG) is set
- */
-static int enable_continuous_mode(struct omap_bandgap *bg_ptr)
-{
-       struct temp_sensor_registers *tsr;
-       int i;
-       u32 val;
-
-       for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
-               /* Perform a single read just before enabling continuous */
-               omap_bandgap_force_single_read(bg_ptr, i);
-               tsr = bg_ptr->conf->sensors[i].registers;
-               val = omap_bandgap_readl(bg_ptr, tsr->bgap_mode_ctrl);
-               val |= 1 << __ffs(tsr->mode_ctrl_mask);
-               omap_bandgap_writel(bg_ptr, val, tsr->bgap_mode_ctrl);
-       }
-
-       return 0;
-}
-
-static int omap_bandgap_tshut_init(struct omap_bandgap *bg_ptr,
-                                     struct platform_device *pdev)
-{
-       int gpio_nr = bg_ptr->tshut_gpio;
-       int status;
-
-       /* Request for gpio_86 line */
-       status = gpio_request(gpio_nr, "tshut");
-       if (status < 0) {
-               dev_err(bg_ptr->dev,
-                       "Could not request for TSHUT GPIO:%i\n", 86);
-               return status;
-       }
-       status = gpio_direction_input(gpio_nr);
-       if (status) {
-               dev_err(bg_ptr->dev,
-                       "Cannot set input TSHUT GPIO %d\n", gpio_nr);
-               return status;
-       }
-
-       status = request_irq(gpio_to_irq(gpio_nr),
-                            omap_bandgap_tshut_irq_handler,
-                            IRQF_TRIGGER_RISING, "tshut",
-                            NULL);
-       if (status) {
-               gpio_free(gpio_nr);
-               dev_err(bg_ptr->dev, "request irq failed for TSHUT");
-       }
-
-       return 0;
-}
-
-/* Initialization of Talert. Call it only if HAS(TALERT) is set */
-static int omap_bandgap_talert_init(struct omap_bandgap *bg_ptr,
-                                      struct platform_device *pdev)
-{
-       int ret;
-
-       bg_ptr->irq = platform_get_irq(pdev, 0);
-       if (bg_ptr->irq < 0) {
-               dev_err(&pdev->dev, "get_irq failed\n");
-               return bg_ptr->irq;
-       }
-       ret = request_threaded_irq(bg_ptr->irq, NULL,
-                                  talert_irq_handler,
-                                  IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
-                                  "talert", bg_ptr);
-       if (ret) {
-               dev_err(&pdev->dev, "Request threaded irq failed.\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static const struct of_device_id of_omap_bandgap_match[];
-static struct omap_bandgap *omap_bandgap_build(struct platform_device *pdev)
-{
-       struct device_node *node = pdev->dev.of_node;
-       const struct of_device_id *of_id;
-       struct omap_bandgap *bg_ptr;
-       struct resource *res;
-       u32 prop;
-       int i;
-
-       /* just for the sake */
-       if (!node) {
-               dev_err(&pdev->dev, "no platform information available\n");
-               return ERR_PTR(-EINVAL);
-       }
-
-       bg_ptr = devm_kzalloc(&pdev->dev, sizeof(struct omap_bandgap),
-                                   GFP_KERNEL);
-       if (!bg_ptr) {
-               dev_err(&pdev->dev, "Unable to allocate mem for driver ref\n");
-               return ERR_PTR(-ENOMEM);
-       }
-
-       of_id = of_match_device(of_omap_bandgap_match, &pdev->dev);
-       if (of_id)
-               bg_ptr->conf = of_id->data;
-
-       i = 0;
-       do {
-               void __iomem *chunk;
-
-               res = platform_get_resource(pdev, IORESOURCE_MEM, i);
-               if (!res)
-                       break;
-               chunk = devm_request_and_ioremap(&pdev->dev, res);
-               if (i == 0)
-                       bg_ptr->base = chunk;
-               if (!chunk) {
-                       dev_err(&pdev->dev,
-                               "failed to request the IO (%d:%pR).\n",
-                               i, res);
-                       return ERR_PTR(-EADDRNOTAVAIL);
-               }
-               i++;
-       } while (res);
-
-       if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
-               if (of_property_read_u32(node, "ti,tshut-gpio", &prop) < 0) {
-                       dev_err(&pdev->dev, "missing tshut gpio in device tree\n");
-                       return ERR_PTR(-EINVAL);
-               }
-               bg_ptr->tshut_gpio = prop;
-               if (!gpio_is_valid(bg_ptr->tshut_gpio)) {
-                       dev_err(&pdev->dev, "invalid gpio for tshut (%d)\n",
-                               bg_ptr->tshut_gpio);
-                       return ERR_PTR(-EINVAL);
-               }
-       }
-
-       return bg_ptr;
-}
-
-static
-int omap_bandgap_probe(struct platform_device *pdev)
-{
-       struct omap_bandgap *bg_ptr;
-       int clk_rate, ret = 0, i;
-
-       bg_ptr = omap_bandgap_build(pdev);
-       if (IS_ERR_OR_NULL(bg_ptr)) {
-               dev_err(&pdev->dev, "failed to fetch platform data\n");
-               return PTR_ERR(bg_ptr);
-       }
-       bg_ptr->dev = &pdev->dev;
-
-       if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
-               ret = omap_bandgap_tshut_init(bg_ptr, pdev);
-               if (ret) {
-                       dev_err(&pdev->dev,
-                               "failed to initialize system tshut IRQ\n");
-                       return ret;
-               }
-       }
-
-       bg_ptr->fclock = clk_get(NULL, bg_ptr->conf->fclock_name);
-       ret = IS_ERR_OR_NULL(bg_ptr->fclock);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to request fclock reference\n");
-               goto free_irqs;
-       }
-
-       bg_ptr->div_clk = clk_get(NULL,  bg_ptr->conf->div_ck_name);
-       ret = IS_ERR_OR_NULL(bg_ptr->div_clk);
-       if (ret) {
-               dev_err(&pdev->dev,
-                       "failed to request div_ts_ck clock ref\n");
-               goto free_irqs;
-       }
-
-       bg_ptr->conv_table = bg_ptr->conf->conv_table;
-       for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
-               struct temp_sensor_registers *tsr;
-               u32 val;
-
-               tsr = bg_ptr->conf->sensors[i].registers;
-               /*
-                * check if the efuse has a non-zero value if not
-                * it is an untrimmed sample and the temperatures
-                * may not be accurate
-                */
-               val = omap_bandgap_readl(bg_ptr, tsr->bgap_efuse);
-               if (ret || !val)
-                       dev_info(&pdev->dev,
-                                "Non-trimmed BGAP, Temp not accurate\n");
-       }
-
-       clk_rate = clk_round_rate(bg_ptr->div_clk,
-                                 bg_ptr->conf->sensors[0].ts_data->max_freq);
-       if (clk_rate < bg_ptr->conf->sensors[0].ts_data->min_freq ||
-           clk_rate == 0xffffffff) {
-               ret = -ENODEV;
-               dev_err(&pdev->dev, "wrong clock rate (%d)\n", clk_rate);
-               goto put_clks;
-       }
-
-       ret = clk_set_rate(bg_ptr->div_clk, clk_rate);
-       if (ret)
-               dev_err(&pdev->dev, "Cannot re-set clock rate. Continuing\n");
-
-       bg_ptr->clk_rate = clk_rate;
-       clk_enable(bg_ptr->fclock);
-
-       mutex_init(&bg_ptr->bg_mutex);
-       bg_ptr->dev = &pdev->dev;
-       platform_set_drvdata(pdev, bg_ptr);
-
-       omap_bandgap_power(bg_ptr, true);
-
-       /* Set default counter to 1 for now */
-       if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
-               for (i = 0; i < bg_ptr->conf->sensor_count; i++)
-                       configure_temp_sensor_counter(bg_ptr, i, 1);
-
-       for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
-               struct temp_sensor_data *ts_data;
-
-               ts_data = bg_ptr->conf->sensors[i].ts_data;
-
-               if (OMAP_BANDGAP_HAS(bg_ptr, TALERT))
-                       temp_sensor_init_talert_thresholds(bg_ptr, i,
-                                                          ts_data->t_hot,
-                                                          ts_data->t_cold);
-               if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG)) {
-                       temp_sensor_configure_tshut_hot(bg_ptr, i,
-                                                       ts_data->tshut_hot);
-                       temp_sensor_configure_tshut_cold(bg_ptr, i,
-                                                        ts_data->tshut_cold);
-               }
-       }
-
-       if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG))
-               enable_continuous_mode(bg_ptr);
-
-       /* Set .250 seconds time as default counter */
-       if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
-               for (i = 0; i < bg_ptr->conf->sensor_count; i++)
-                       configure_temp_sensor_counter(bg_ptr, i,
-                                                     bg_ptr->clk_rate / 4);
-
-       /* Every thing is good? Then expose the sensors */
-       for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
-               char *domain;
-
-               if (bg_ptr->conf->sensors[i].register_cooling)
-                       bg_ptr->conf->sensors[i].register_cooling(bg_ptr, i);
-
-               domain = bg_ptr->conf->sensors[i].domain;
-               if (bg_ptr->conf->expose_sensor)
-                       bg_ptr->conf->expose_sensor(bg_ptr, i, domain);
-       }
-
-       /*
-        * Enable the Interrupts once everything is set. Otherwise irq handler
-        * might be called as soon as it is enabled where as rest of framework
-        * is still getting initialised.
-        */
-       if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) {
-               ret = omap_bandgap_talert_init(bg_ptr, pdev);
-               if (ret) {
-                       dev_err(&pdev->dev, "failed to initialize Talert IRQ\n");
-                       i = bg_ptr->conf->sensor_count;
-                       goto disable_clk;
-               }
-       }
-
-       return 0;
-
-disable_clk:
-       clk_disable(bg_ptr->fclock);
-put_clks:
-       clk_put(bg_ptr->fclock);
-       clk_put(bg_ptr->div_clk);
-free_irqs:
-       if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
-               free_irq(gpio_to_irq(bg_ptr->tshut_gpio), NULL);
-               gpio_free(bg_ptr->tshut_gpio);
-       }
-
-       return ret;
-}
-
-static
-int omap_bandgap_remove(struct platform_device *pdev)
-{
-       struct omap_bandgap *bg_ptr = platform_get_drvdata(pdev);
-       int i;
-
-       /* First thing is to remove sensor interfaces */
-       for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
-               if (bg_ptr->conf->sensors[i].register_cooling)
-                       bg_ptr->conf->sensors[i].unregister_cooling(bg_ptr, i);
-
-               if (bg_ptr->conf->remove_sensor)
-                       bg_ptr->conf->remove_sensor(bg_ptr, i);
-       }
-
-       omap_bandgap_power(bg_ptr, false);
-
-       clk_disable(bg_ptr->fclock);
-       clk_put(bg_ptr->fclock);
-       clk_put(bg_ptr->div_clk);
-
-       if (OMAP_BANDGAP_HAS(bg_ptr, TALERT))
-               free_irq(bg_ptr->irq, bg_ptr);
-
-       if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT)) {
-               free_irq(gpio_to_irq(bg_ptr->tshut_gpio), NULL);
-               gpio_free(bg_ptr->tshut_gpio);
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int omap_bandgap_save_ctxt(struct omap_bandgap *bg_ptr)
-{
-       int i;
-
-       for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
-               struct temp_sensor_registers *tsr;
-               struct temp_sensor_regval *rval;
-
-               rval = &bg_ptr->conf->sensors[i].regval;
-               tsr = bg_ptr->conf->sensors[i].registers;
-
-               if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG))
-                       rval->bg_mode_ctrl = omap_bandgap_readl(bg_ptr,
-                                                       tsr->bgap_mode_ctrl);
-               if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
-                       rval->bg_counter = omap_bandgap_readl(bg_ptr,
-                                                       tsr->bgap_counter);
-               if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) {
-                       rval->bg_threshold = omap_bandgap_readl(bg_ptr,
-                                                       tsr->bgap_threshold);
-                       rval->bg_ctrl = omap_bandgap_readl(bg_ptr,
-                                                  tsr->bgap_mask_ctrl);
-               }
-
-               if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG))
-                       rval->tshut_threshold = omap_bandgap_readl(bg_ptr,
-                                                  tsr->tshut_threshold);
-       }
-
-       return 0;
-}
-
-static int omap_bandgap_restore_ctxt(struct omap_bandgap *bg_ptr)
-{
-       int i;
-
-       for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
-               struct temp_sensor_registers *tsr;
-               struct temp_sensor_regval *rval;
-               u32 val = 0;
-
-               rval = &bg_ptr->conf->sensors[i].regval;
-               tsr = bg_ptr->conf->sensors[i].registers;
-
-               if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
-                       val = omap_bandgap_readl(bg_ptr, tsr->bgap_counter);
-
-               if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG))
-                       omap_bandgap_writel(bg_ptr,
-                               rval->tshut_threshold,
-                                          tsr->tshut_threshold);
-               /* Force immediate temperature measurement and update
-                * of the DTEMP field
-                */
-               omap_bandgap_force_single_read(bg_ptr, i);
-
-               if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
-                       omap_bandgap_writel(bg_ptr, rval->bg_counter,
-                                                  tsr->bgap_counter);
-               if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG))
-                       omap_bandgap_writel(bg_ptr, rval->bg_mode_ctrl,
-                                                  tsr->bgap_mode_ctrl);
-               if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) {
-                       omap_bandgap_writel(bg_ptr,
-                                                  rval->bg_threshold,
-                                                  tsr->bgap_threshold);
-                       omap_bandgap_writel(bg_ptr, rval->bg_ctrl,
-                                                  tsr->bgap_mask_ctrl);
-               }
-       }
-
-       return 0;
-}
-
-static int omap_bandgap_suspend(struct device *dev)
-{
-       struct omap_bandgap *bg_ptr = dev_get_drvdata(dev);
-       int err;
-
-       err = omap_bandgap_save_ctxt(bg_ptr);
-       omap_bandgap_power(bg_ptr, false);
-       clk_disable(bg_ptr->fclock);
-
-       return err;
-}
-
-static int omap_bandgap_resume(struct device *dev)
-{
-       struct omap_bandgap *bg_ptr = dev_get_drvdata(dev);
-
-       clk_enable(bg_ptr->fclock);
-       omap_bandgap_power(bg_ptr, true);
-
-       return omap_bandgap_restore_ctxt(bg_ptr);
-}
-static const struct dev_pm_ops omap_bandgap_dev_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(omap_bandgap_suspend,
-                               omap_bandgap_resume)
-};
-
-#define DEV_PM_OPS     (&omap_bandgap_dev_pm_ops)
-#else
-#define DEV_PM_OPS     NULL
-#endif
-
-static const struct of_device_id of_omap_bandgap_match[] = {
-#ifdef CONFIG_OMAP4_THERMAL
-       {
-               .compatible = "ti,omap4430-bandgap",
-               .data = (void *)&omap4430_data,
-       },
-       {
-               .compatible = "ti,omap4460-bandgap",
-               .data = (void *)&omap4460_data,
-       },
-       {
-               .compatible = "ti,omap4470-bandgap",
-               .data = (void *)&omap4470_data,
-       },
-#endif
-#ifdef CONFIG_OMAP5_THERMAL
-       {
-               .compatible = "ti,omap5430-bandgap",
-               .data = (void *)&omap5430_data,
-       },
-#endif
-       /* Sentinel */
-       { },
-};
-MODULE_DEVICE_TABLE(of, of_omap_bandgap_match);
-
-static struct platform_driver omap_bandgap_sensor_driver = {
-       .probe = omap_bandgap_probe,
-       .remove = omap_bandgap_remove,
-       .driver = {
-                       .name = "omap-bandgap",
-                       .pm = DEV_PM_OPS,
-                       .of_match_table = of_omap_bandgap_match,
-       },
-};
-
-module_platform_driver(omap_bandgap_sensor_driver);
-
-MODULE_DESCRIPTION("OMAP4+ bandgap temperature sensor driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:omap-bandgap");
-MODULE_AUTHOR("Texas Instrument Inc.");
diff --git a/drivers/staging/omap-thermal/omap-bandgap.h b/drivers/staging/omap-thermal/omap-bandgap.h
deleted file mode 100644 (file)
index 2bb14bd..0000000
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * OMAP4 Bandgap temperature sensor driver
- *
- * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
- * Contact:
- *   Eduardo Valentin <eduardo.valentin@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-#ifndef __OMAP_BANDGAP_H
-#define __OMAP_BANDGAP_H
-
-#include <linux/mutex.h>
-#include <linux/types.h>
-#include <linux/err.h>
-
-/* TEMP_SENSOR OMAP4430 */
-#define OMAP4430_BGAP_TSHUT_SHIFT                      11
-#define OMAP4430_BGAP_TSHUT_MASK                       (1 << 11)
-
-/* TEMP_SENSOR OMAP4430 */
-#define OMAP4430_BGAP_TEMPSOFF_SHIFT                   12
-#define OMAP4430_BGAP_TEMPSOFF_MASK                    (1 << 12)
-#define OMAP4430_SINGLE_MODE_SHIFT                     10
-#define OMAP4430_SINGLE_MODE_MASK                      (1 << 10)
-#define OMAP4430_BGAP_TEMP_SENSOR_SOC_SHIFT            9
-#define OMAP4430_BGAP_TEMP_SENSOR_SOC_MASK             (1 << 9)
-#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_SHIFT           8
-#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK            (1 << 8)
-#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_SHIFT          0
-#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK           (0xff << 0)
-
-#define OMAP4430_ADC_START_VALUE                       0
-#define OMAP4430_ADC_END_VALUE                         127
-#define OMAP4430_MAX_FREQ                              32768
-#define OMAP4430_MIN_FREQ                              32768
-#define OMAP4430_MIN_TEMP                              -40000
-#define OMAP4430_MAX_TEMP                              125000
-#define OMAP4430_HYST_VAL                              5000
-
-/* TEMP_SENSOR OMAP4460 */
-#define OMAP4460_BGAP_TEMPSOFF_SHIFT                   13
-#define OMAP4460_BGAP_TEMPSOFF_MASK                    (1 << 13)
-#define OMAP4460_BGAP_TEMP_SENSOR_SOC_SHIFT            11
-#define OMAP4460_BGAP_TEMP_SENSOR_SOC_MASK             (1 << 11)
-#define OMAP4460_BGAP_TEMP_SENSOR_EOCZ_SHIFT           10
-#define OMAP4460_BGAP_TEMP_SENSOR_EOCZ_MASK            (1 << 10)
-#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_SHIFT          0
-#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK           (0x3ff << 0)
-
-/* BANDGAP_CTRL */
-#define OMAP4460_SINGLE_MODE_SHIFT                     31
-#define OMAP4460_SINGLE_MODE_MASK                      (1 << 31)
-#define OMAP4460_MASK_HOT_SHIFT                                1
-#define OMAP4460_MASK_HOT_MASK                         (1 << 1)
-#define OMAP4460_MASK_COLD_SHIFT                       0
-#define OMAP4460_MASK_COLD_MASK                                (1 << 0)
-
-/* BANDGAP_COUNTER */
-#define OMAP4460_COUNTER_SHIFT                         0
-#define OMAP4460_COUNTER_MASK                          (0xffffff << 0)
-
-/* BANDGAP_THRESHOLD */
-#define OMAP4460_T_HOT_SHIFT                           16
-#define OMAP4460_T_HOT_MASK                            (0x3ff << 16)
-#define OMAP4460_T_COLD_SHIFT                          0
-#define OMAP4460_T_COLD_MASK                           (0x3ff << 0)
-
-/* TSHUT_THRESHOLD */
-#define OMAP4460_TSHUT_HOT_SHIFT                       16
-#define OMAP4460_TSHUT_HOT_MASK                                (0x3ff << 16)
-#define OMAP4460_TSHUT_COLD_SHIFT                      0
-#define OMAP4460_TSHUT_COLD_MASK                       (0x3ff << 0)
-
-/* BANDGAP_STATUS */
-#define OMAP4460_CLEAN_STOP_SHIFT                      3
-#define OMAP4460_CLEAN_STOP_MASK                       (1 << 3)
-#define OMAP4460_BGAP_ALERT_SHIFT                      2
-#define OMAP4460_BGAP_ALERT_MASK                       (1 << 2)
-#define OMAP4460_HOT_FLAG_SHIFT                                1
-#define OMAP4460_HOT_FLAG_MASK                         (1 << 1)
-#define OMAP4460_COLD_FLAG_SHIFT                       0
-#define OMAP4460_COLD_FLAG_MASK                                (1 << 0)
-
-/* TEMP_SENSOR OMAP5430 */
-#define OMAP5430_BGAP_TEMP_SENSOR_SOC_SHIFT            12
-#define OMAP5430_BGAP_TEMP_SENSOR_SOC_MASK             (1 << 12)
-#define OMAP5430_BGAP_TEMPSOFF_SHIFT                   11
-#define OMAP5430_BGAP_TEMPSOFF_MASK                    (1 << 11)
-#define OMAP5430_BGAP_TEMP_SENSOR_EOCZ_SHIFT           10
-#define OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK            (1 << 10)
-#define OMAP5430_BGAP_TEMP_SENSOR_DTEMP_SHIFT          0
-#define OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK           (0x3ff << 0)
-
-/* BANDGAP_CTRL */
-#define OMAP5430_MASK_HOT_CORE_SHIFT                   5
-#define OMAP5430_MASK_HOT_CORE_MASK                    (1 << 5)
-#define OMAP5430_MASK_COLD_CORE_SHIFT                  4
-#define OMAP5430_MASK_COLD_CORE_MASK                   (1 << 4)
-#define OMAP5430_MASK_HOT_MM_SHIFT                     3
-#define OMAP5430_MASK_HOT_MM_MASK                      (1 << 3)
-#define OMAP5430_MASK_COLD_MM_SHIFT                    2
-#define OMAP5430_MASK_COLD_MM_MASK                     (1 << 2)
-#define OMAP5430_MASK_HOT_MPU_SHIFT                    1
-#define OMAP5430_MASK_HOT_MPU_MASK                     (1 << 1)
-#define OMAP5430_MASK_COLD_MPU_SHIFT                   0
-#define OMAP5430_MASK_COLD_MPU_MASK                    (1 << 0)
-
-/* BANDGAP_COUNTER */
-#define OMAP5430_REPEAT_MODE_SHIFT                     31
-#define OMAP5430_REPEAT_MODE_MASK                      (1 << 31)
-#define OMAP5430_COUNTER_SHIFT                         0
-#define OMAP5430_COUNTER_MASK                          (0xffffff << 0)
-
-/* BANDGAP_THRESHOLD */
-#define OMAP5430_T_HOT_SHIFT                           16
-#define OMAP5430_T_HOT_MASK                            (0x3ff << 16)
-#define OMAP5430_T_COLD_SHIFT                          0
-#define OMAP5430_T_COLD_MASK                           (0x3ff << 0)
-
-/* TSHUT_THRESHOLD */
-#define OMAP5430_TSHUT_HOT_SHIFT                       16
-#define OMAP5430_TSHUT_HOT_MASK                                (0x3ff << 16)
-#define OMAP5430_TSHUT_COLD_SHIFT                      0
-#define OMAP5430_TSHUT_COLD_MASK                       (0x3ff << 0)
-
-/* BANDGAP_STATUS */
-#define OMAP5430_BGAP_ALERT_SHIFT                      31
-#define OMAP5430_BGAP_ALERT_MASK                       (1 << 31)
-#define OMAP5430_HOT_CORE_FLAG_SHIFT                   5
-#define OMAP5430_HOT_CORE_FLAG_MASK                    (1 << 5)
-#define OMAP5430_COLD_CORE_FLAG_SHIFT                  4
-#define OMAP5430_COLD_CORE_FLAG_MASK                   (1 << 4)
-#define OMAP5430_HOT_MM_FLAG_SHIFT                     3
-#define OMAP5430_HOT_MM_FLAG_MASK                      (1 << 3)
-#define OMAP5430_COLD_MM_FLAG_SHIFT                    2
-#define OMAP5430_COLD_MM_FLAG_MASK                     (1 << 2)
-#define OMAP5430_HOT_MPU_FLAG_SHIFT                    1
-#define OMAP5430_HOT_MPU_FLAG_MASK                     (1 << 1)
-#define OMAP5430_COLD_MPU_FLAG_SHIFT                   0
-#define OMAP5430_COLD_MPU_FLAG_MASK                    (1 << 0)
-
-/* Offsets from the base of temperature sensor registers */
-
-/* 4430 - All goes relative to OPP_BGAP */
-#define OMAP4430_FUSE_OPP_BGAP                         0x0
-#define OMAP4430_TEMP_SENSOR_CTRL_OFFSET               0xCC
-
-/* 4460 - All goes relative to OPP_BGAP */
-#define OMAP4460_FUSE_OPP_BGAP                         0x0
-#define OMAP4460_TEMP_SENSOR_CTRL_OFFSET               0xCC
-#define OMAP4460_BGAP_CTRL_OFFSET                      0x118
-#define OMAP4460_BGAP_COUNTER_OFFSET                   0x11C
-#define OMAP4460_BGAP_THRESHOLD_OFFSET                 0x120
-#define OMAP4460_BGAP_TSHUT_OFFSET                     0x124
-#define OMAP4460_BGAP_STATUS_OFFSET                    0x128
-
-/* 5430 - All goes relative to OPP_BGAP_GPU */
-#define OMAP5430_FUSE_OPP_BGAP_GPU                     0x0
-#define OMAP5430_TEMP_SENSOR_GPU_OFFSET                        0x150
-#define OMAP5430_BGAP_COUNTER_GPU_OFFSET               0x1C0
-#define OMAP5430_BGAP_THRESHOLD_GPU_OFFSET             0x1A8
-#define OMAP5430_BGAP_TSHUT_GPU_OFFSET                 0x1B4
-
-#define OMAP5430_FUSE_OPP_BGAP_MPU                     0x4
-#define OMAP5430_TEMP_SENSOR_MPU_OFFSET                        0x14C
-#define OMAP5430_BGAP_CTRL_OFFSET                      0x1A0
-#define OMAP5430_BGAP_COUNTER_MPU_OFFSET               0x1BC
-#define OMAP5430_BGAP_THRESHOLD_MPU_OFFSET             0x1A4
-#define OMAP5430_BGAP_TSHUT_MPU_OFFSET                 0x1B0
-#define OMAP5430_BGAP_STATUS_OFFSET                    0x1C8
-
-#define OMAP5430_FUSE_OPP_BGAP_CORE                    0x8
-#define OMAP5430_TEMP_SENSOR_CORE_OFFSET               0x154
-#define OMAP5430_BGAP_COUNTER_CORE_OFFSET              0x1C4
-#define OMAP5430_BGAP_THRESHOLD_CORE_OFFSET            0x1AC
-#define OMAP5430_BGAP_TSHUT_CORE_OFFSET                        0x1B8
-
-#define OMAP4460_TSHUT_HOT                             900     /* 122 deg C */
-#define OMAP4460_TSHUT_COLD                            895     /* 100 deg C */
-#define OMAP4460_T_HOT                                 800     /* 73 deg C */
-#define OMAP4460_T_COLD                                        795     /* 71 deg C */
-#define OMAP4460_MAX_FREQ                              1500000
-#define OMAP4460_MIN_FREQ                              1000000
-#define OMAP4460_MIN_TEMP                              -40000
-#define OMAP4460_MAX_TEMP                              123000
-#define OMAP4460_HYST_VAL                              5000
-#define OMAP4460_ADC_START_VALUE                       530
-#define OMAP4460_ADC_END_VALUE                         932
-
-#define OMAP5430_MPU_TSHUT_HOT                         915
-#define OMAP5430_MPU_TSHUT_COLD                                900
-#define OMAP5430_MPU_T_HOT                             800
-#define OMAP5430_MPU_T_COLD                            795
-#define OMAP5430_MPU_MAX_FREQ                          1500000
-#define OMAP5430_MPU_MIN_FREQ                          1000000
-#define OMAP5430_MPU_MIN_TEMP                          -40000
-#define OMAP5430_MPU_MAX_TEMP                          125000
-#define OMAP5430_MPU_HYST_VAL                          5000
-#define OMAP5430_ADC_START_VALUE                       532
-#define OMAP5430_ADC_END_VALUE                         934
-
-
-#define OMAP5430_GPU_TSHUT_HOT                         915
-#define OMAP5430_GPU_TSHUT_COLD                                900
-#define OMAP5430_GPU_T_HOT                             800
-#define OMAP5430_GPU_T_COLD                            795
-#define OMAP5430_GPU_MAX_FREQ                          1500000
-#define OMAP5430_GPU_MIN_FREQ                          1000000
-#define OMAP5430_GPU_MIN_TEMP                          -40000
-#define OMAP5430_GPU_MAX_TEMP                          125000
-#define OMAP5430_GPU_HYST_VAL                          5000
-
-#define OMAP5430_CORE_TSHUT_HOT                                915
-#define OMAP5430_CORE_TSHUT_COLD                       900
-#define OMAP5430_CORE_T_HOT                            800
-#define OMAP5430_CORE_T_COLD                           795
-#define OMAP5430_CORE_MAX_FREQ                         1500000
-#define OMAP5430_CORE_MIN_FREQ                         1000000
-#define OMAP5430_CORE_MIN_TEMP                         -40000
-#define OMAP5430_CORE_MAX_TEMP                         125000
-#define OMAP5430_CORE_HYST_VAL                         5000
-
-/**
- * The register offsets and bit fields might change across
- * OMAP versions hence populating them in this structure.
- */
-
-struct temp_sensor_registers {
-       u32     temp_sensor_ctrl;
-       u32     bgap_tempsoff_mask;
-       u32     bgap_soc_mask;
-       u32     bgap_eocz_mask;
-       u32     bgap_dtemp_mask;
-
-       u32     bgap_mask_ctrl;
-       u32     mask_hot_mask;
-       u32     mask_cold_mask;
-
-       u32     bgap_mode_ctrl;
-       u32     mode_ctrl_mask;
-
-       u32     bgap_counter;
-       u32     counter_mask;
-
-       u32     bgap_threshold;
-       u32     threshold_thot_mask;
-       u32     threshold_tcold_mask;
-
-       u32     tshut_threshold;
-       u32     tshut_hot_mask;
-       u32     tshut_cold_mask;
-
-       u32     bgap_status;
-       u32     status_clean_stop_mask;
-       u32     status_bgap_alert_mask;
-       u32     status_hot_mask;
-       u32     status_cold_mask;
-
-       u32     bgap_efuse;
-};
-
-/**
- * The thresholds and limits for temperature sensors.
- */
-struct temp_sensor_data {
-       u32     tshut_hot;
-       u32     tshut_cold;
-       u32     t_hot;
-       u32     t_cold;
-       u32     min_freq;
-       u32     max_freq;
-       int     max_temp;
-       int     min_temp;
-       int     hyst_val;
-       u32     adc_start_val;
-       u32     adc_end_val;
-       u32     update_int1;
-       u32     update_int2;
-};
-
-struct omap_bandgap_data;
-
-/**
- * struct omap_bandgap - bandgap device structure
- * @dev: device pointer
- * @conf: platform data with sensor data
- * @fclock: pointer to functional clock of temperature sensor
- * @div_clk: pointer to parent clock of temperature sensor fclk
- * @conv_table: Pointer to adc to temperature conversion table
- * @bg_mutex: Mutex for sysfs, irq and PM
- * @irq: MPU Irq number for thermal alert
- * @tshut_gpio: GPIO where Tshut signal is routed
- * @clk_rate: Holds current clock rate
- */
-struct omap_bandgap {
-       struct device                   *dev;
-       void __iomem                    *base;
-       struct omap_bandgap_data        *conf;
-       struct clk                      *fclock;
-       struct clk                      *div_clk;
-       const int                       *conv_table;
-       struct mutex                    bg_mutex; /* Mutex for irq and PM */
-       int                             irq;
-       int                             tshut_gpio;
-       u32                             clk_rate;
-};
-
-/**
- * struct temp_sensor_regval - temperature sensor register values
- * @bg_mode_ctrl: temp sensor control register value
- * @bg_ctrl: bandgap ctrl register value
- * @bg_counter: bandgap counter value
- * @bg_threshold: bandgap threshold register value
- * @tshut_threshold: bandgap tshut register value
- */
-struct temp_sensor_regval {
-       u32                     bg_mode_ctrl;
-       u32                     bg_ctrl;
-       u32                     bg_counter;
-       u32                     bg_threshold;
-       u32                     tshut_threshold;
-};
-
-/**
- * struct omap_temp_sensor - bandgap temperature sensor platform data
- * @ts_data: pointer to struct with thresholds, limits of temperature sensor
- * @registers: pointer to the list of register offsets and bitfields
- * @regval: temperature sensor register values
- * @domain: the name of the domain where the sensor is located
- * @cooling_data: description on how the zone should be cooled off.
- * @slope: sensor gradient slope info for hotspot extrapolation
- * @const: sensor gradient const info for hotspot extrapolation
- * @slope_pcb: sensor gradient slope info for hotspot extrapolation
- *             with no external influence
- * @const_pcb: sensor gradient const info for hotspot extrapolation
- *             with no external influence
- * @data: private data
- * @register_cooling: function to describe how this sensor is going to be cooled
- * @unregister_cooling: function to release cooling data
- */
-struct omap_temp_sensor {
-       struct temp_sensor_data         *ts_data;
-       struct temp_sensor_registers    *registers;
-       struct temp_sensor_regval       regval;
-       char                            *domain;
-       /* for hotspot extrapolation */
-       const int                       slope;
-       const int                       constant;
-       const int                       slope_pcb;
-       const int                       constant_pcb;
-       void                            *data;
-       int (*register_cooling)(struct omap_bandgap *bg_ptr, int id);
-       int (*unregister_cooling)(struct omap_bandgap *bg_ptr, int id);
-};
-
-/**
- * struct omap_bandgap_data - bandgap platform data structure
- * @features: a bitwise flag set to describe the device features
- * @conv_table: Pointer to adc to temperature conversion table
- * @fclock_name: clock name of the functional clock
- * @div_ck_nme: clock name of the clock divisor
- * @sensor_count: count of temperature sensor device in scm
- * @sensors: array of sensors present in this bandgap instance
- * @expose_sensor: callback to export sensor to thermal API
- */
-struct omap_bandgap_data {
-#define OMAP_BANDGAP_FEATURE_TSHUT             (1 << 0)
-#define OMAP_BANDGAP_FEATURE_TSHUT_CONFIG      (1 << 1)
-#define OMAP_BANDGAP_FEATURE_TALERT            (1 << 2)
-#define OMAP_BANDGAP_FEATURE_MODE_CONFIG       (1 << 3)
-#define OMAP_BANDGAP_FEATURE_COUNTER           (1 << 4)
-#define OMAP_BANDGAP_FEATURE_POWER_SWITCH      (1 << 5)
-#define OMAP_BANDGAP_HAS(b, f)                 \
-                       ((b)->conf->features & OMAP_BANDGAP_FEATURE_ ## f)
-       unsigned int                    features;
-       const int                       *conv_table;
-       char                            *fclock_name;
-       char                            *div_ck_name;
-       int                             sensor_count;
-       int (*report_temperature)(struct omap_bandgap *bg_ptr, int id);
-       int (*expose_sensor)(struct omap_bandgap *bg_ptr, int id, char *domain);
-       int (*remove_sensor)(struct omap_bandgap *bg_ptr, int id);
-
-       /* this needs to be at the end */
-       struct omap_temp_sensor         sensors[];
-};
-
-int omap_bandgap_read_thot(struct omap_bandgap *bg_ptr, int id, int *thot);
-int omap_bandgap_write_thot(struct omap_bandgap *bg_ptr, int id, int val);
-int omap_bandgap_read_tcold(struct omap_bandgap *bg_ptr, int id, int *tcold);
-int omap_bandgap_write_tcold(struct omap_bandgap *bg_ptr, int id, int val);
-int omap_bandgap_read_update_interval(struct omap_bandgap *bg_ptr, int id,
-                                     int *interval);
-int omap_bandgap_write_update_interval(struct omap_bandgap *bg_ptr, int id,
-                                      u32 interval);
-int omap_bandgap_read_temperature(struct omap_bandgap *bg_ptr, int id,
-                                 int *temperature);
-int omap_bandgap_set_sensor_data(struct omap_bandgap *bg_ptr, int id,
-                                void *data);
-void *omap_bandgap_get_sensor_data(struct omap_bandgap *bg_ptr, int id);
-
-#ifdef CONFIG_OMAP4_THERMAL
-extern const struct omap_bandgap_data omap4430_data;
-extern const struct omap_bandgap_data omap4460_data;
-extern const struct omap_bandgap_data omap4470_data;
-#else
-#define omap4430_data                                  NULL
-#define omap4460_data                                  NULL
-#define omap4470_data                                  NULL
-#endif
-
-#ifdef CONFIG_OMAP5_THERMAL
-extern const struct omap_bandgap_data omap5430_data;
-#else
-#define omap5430_data                                  NULL
-#endif
-
-#endif
diff --git a/drivers/staging/omap-thermal/omap-thermal-common.c b/drivers/staging/omap-thermal/omap-thermal-common.c
deleted file mode 100644 (file)
index 61f1070..0000000
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * OMAP thermal driver interface
- *
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
- * Contact:
- *   Eduardo Valentin <eduardo.valentin@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/mutex.h>
-#include <linux/gfp.h>
-#include <linux/kernel.h>
-#include <linux/workqueue.h>
-#include <linux/thermal.h>
-#include <linux/cpufreq.h>
-#include <linux/cpumask.h>
-#include <linux/cpu_cooling.h>
-
-#include "omap-thermal.h"
-#include "omap-bandgap.h"
-
-/* common data structures */
-struct omap_thermal_data {
-       struct thermal_zone_device *omap_thermal;
-       struct thermal_cooling_device *cool_dev;
-       struct omap_bandgap *bg_ptr;
-       enum thermal_device_mode mode;
-       struct work_struct thermal_wq;
-       int sensor_id;
-};
-
-static void omap_thermal_work(struct work_struct *work)
-{
-       struct omap_thermal_data *data = container_of(work,
-                                       struct omap_thermal_data, thermal_wq);
-
-       thermal_zone_device_update(data->omap_thermal);
-
-       dev_dbg(&data->omap_thermal->device, "updated thermal zone %s\n",
-               data->omap_thermal->type);
-}
-
-/**
- * omap_thermal_hotspot_temperature - returns sensor extrapolated temperature
- * @t: omap sensor temperature
- * @s: omap sensor slope value
- * @c: omap sensor const value
- */
-static inline int omap_thermal_hotspot_temperature(int t, int s, int c)
-{
-       int delta = t * s / 1000 + c;
-
-       if (delta < 0)
-               delta = 0;
-
-       return t + delta;
-}
-
-/* thermal zone ops */
-/* Get temperature callback function for thermal zone*/
-static inline int omap_thermal_get_temp(struct thermal_zone_device *thermal,
-                                        unsigned long *temp)
-{
-       struct omap_thermal_data *data = thermal->devdata;
-       struct omap_bandgap *bg_ptr;
-       struct omap_temp_sensor *s;
-       int ret, tmp, pcb_temp, slope, constant;
-
-       if (!data)
-               return 0;
-
-       bg_ptr = data->bg_ptr;
-       s = &bg_ptr->conf->sensors[data->sensor_id];
-
-       ret = omap_bandgap_read_temperature(bg_ptr, data->sensor_id, &tmp);
-       if (ret)
-               return ret;
-
-       pcb_temp = 0;
-       /* TODO: Introduce pcb temperature lookup */
-       /* In case pcb zone is available, use the extrapolation rule with it */
-       if (pcb_temp) {
-               tmp -= pcb_temp;
-               slope = s->slope_pcb;
-               constant = s->constant_pcb;
-       } else {
-               slope = s->slope;
-               constant = s->constant;
-       }
-       *temp = omap_thermal_hotspot_temperature(tmp, slope, constant);
-
-       return ret;
-}
-
-/* Bind callback functions for thermal zone */
-static int omap_thermal_bind(struct thermal_zone_device *thermal,
-                             struct thermal_cooling_device *cdev)
-{
-       struct omap_thermal_data *data = thermal->devdata;
-       int id;
-
-       if (IS_ERR_OR_NULL(data))
-               return -ENODEV;
-
-       /* check if this is the cooling device we registered */
-       if (data->cool_dev != cdev)
-               return 0;
-
-       id = data->sensor_id;
-
-       /* TODO: bind with min and max states */
-       /* Simple thing, two trips, one passive another critical */
-       return thermal_zone_bind_cooling_device(thermal, 0, cdev,
-                                               THERMAL_NO_LIMIT,
-                                               THERMAL_NO_LIMIT);
-}
-
-/* Unbind callback functions for thermal zone */
-static int omap_thermal_unbind(struct thermal_zone_device *thermal,
-                               struct thermal_cooling_device *cdev)
-{
-       struct omap_thermal_data *data = thermal->devdata;
-
-       if (IS_ERR_OR_NULL(data))
-               return -ENODEV;
-
-       /* check if this is the cooling device we registered */
-       if (data->cool_dev != cdev)
-               return 0;
-
-       /* Simple thing, two trips, one passive another critical */
-       return thermal_zone_unbind_cooling_device(thermal, 0, cdev);
-}
-
-/* Get mode callback functions for thermal zone */
-static int omap_thermal_get_mode(struct thermal_zone_device *thermal,
-                                 enum thermal_device_mode *mode)
-{
-       struct omap_thermal_data *data = thermal->devdata;
-
-       if (data)
-               *mode = data->mode;
-
-       return 0;
-}
-
-/* Set mode callback functions for thermal zone */
-static int omap_thermal_set_mode(struct thermal_zone_device *thermal,
-                                 enum thermal_device_mode mode)
-{
-       struct omap_thermal_data *data = thermal->devdata;
-
-       if (!data->omap_thermal) {
-               dev_notice(&thermal->device, "thermal zone not registered\n");
-               return 0;
-       }
-
-       mutex_lock(&data->omap_thermal->lock);
-
-       if (mode == THERMAL_DEVICE_ENABLED)
-               data->omap_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
-       else
-               data->omap_thermal->polling_delay = 0;
-
-       mutex_unlock(&data->omap_thermal->lock);
-
-       data->mode = mode;
-       thermal_zone_device_update(data->omap_thermal);
-       dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n",
-               data->omap_thermal->polling_delay);
-
-       return 0;
-}
-
-/* Get trip type callback functions for thermal zone */
-static int omap_thermal_get_trip_type(struct thermal_zone_device *thermal,
-                                      int trip, enum thermal_trip_type *type)
-{
-       if (!omap_thermal_is_valid_trip(trip))
-               return -EINVAL;
-
-       if (trip + 1 == OMAP_TRIP_NUMBER)
-               *type = THERMAL_TRIP_CRITICAL;
-       else
-               *type = THERMAL_TRIP_PASSIVE;
-
-       return 0;
-}
-
-/* Get trip temperature callback functions for thermal zone */
-static int omap_thermal_get_trip_temp(struct thermal_zone_device *thermal,
-                                      int trip, unsigned long *temp)
-{
-       if (!omap_thermal_is_valid_trip(trip))
-               return -EINVAL;
-
-       *temp = omap_thermal_get_trip_value(trip);
-
-       return 0;
-}
-
-/* Get critical temperature callback functions for thermal zone */
-static int omap_thermal_get_crit_temp(struct thermal_zone_device *thermal,
-                                      unsigned long *temp)
-{
-       /* shutdown zone */
-       return omap_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp);
-}
-
-static struct thermal_zone_device_ops omap_thermal_ops = {
-       .get_temp = omap_thermal_get_temp,
-       /* TODO: add .get_trend */
-       .bind = omap_thermal_bind,
-       .unbind = omap_thermal_unbind,
-       .get_mode = omap_thermal_get_mode,
-       .set_mode = omap_thermal_set_mode,
-       .get_trip_type = omap_thermal_get_trip_type,
-       .get_trip_temp = omap_thermal_get_trip_temp,
-       .get_crit_temp = omap_thermal_get_crit_temp,
-};
-
-static struct omap_thermal_data
-*omap_thermal_build_data(struct omap_bandgap *bg_ptr, int id)
-{
-       struct omap_thermal_data *data;
-
-       data = devm_kzalloc(bg_ptr->dev, sizeof(*data), GFP_KERNEL);
-       if (!data) {
-               dev_err(bg_ptr->dev, "kzalloc fail\n");
-               return NULL;
-       }
-       data->sensor_id = id;
-       data->bg_ptr = bg_ptr;
-       data->mode = THERMAL_DEVICE_ENABLED;
-       INIT_WORK(&data->thermal_wq, omap_thermal_work);
-
-       return data;
-}
-
-int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id,
-                              char *domain)
-{
-       struct omap_thermal_data *data;
-
-       data = omap_bandgap_get_sensor_data(bg_ptr, id);
-
-       if (!data)
-               data = omap_thermal_build_data(bg_ptr, id);
-
-       if (!data)
-               return -EINVAL;
-
-       /* TODO: remove TC1 TC2 */
-       /* Create thermal zone */
-       data->omap_thermal = thermal_zone_device_register(domain,
-                               OMAP_TRIP_NUMBER, 0, data, &omap_thermal_ops,
-                               NULL, FAST_TEMP_MONITORING_RATE,
-                               FAST_TEMP_MONITORING_RATE);
-       if (IS_ERR_OR_NULL(data->omap_thermal)) {
-               dev_err(bg_ptr->dev, "thermal zone device is NULL\n");
-               return PTR_ERR(data->omap_thermal);
-       }
-       data->omap_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
-       omap_bandgap_set_sensor_data(bg_ptr, id, data);
-
-       return 0;
-}
-
-int omap_thermal_remove_sensor(struct omap_bandgap *bg_ptr, int id)
-{
-       struct omap_thermal_data *data;
-
-       data = omap_bandgap_get_sensor_data(bg_ptr, id);
-
-       thermal_zone_device_unregister(data->omap_thermal);
-
-       return 0;
-}
-
-int omap_thermal_report_sensor_temperature(struct omap_bandgap *bg_ptr, int id)
-{
-       struct omap_thermal_data *data;
-
-       data = omap_bandgap_get_sensor_data(bg_ptr, id);
-
-       schedule_work(&data->thermal_wq);
-
-       return 0;
-}
-
-int omap_thermal_register_cpu_cooling(struct omap_bandgap *bg_ptr, int id)
-{
-       struct omap_thermal_data *data;
-
-       data = omap_bandgap_get_sensor_data(bg_ptr, id);
-       if (!data)
-               data = omap_thermal_build_data(bg_ptr, id);
-
-       if (!data)
-               return -EINVAL;
-
-       /* Register cooling device */
-       data->cool_dev = cpufreq_cooling_register(cpu_present_mask);
-       if (IS_ERR_OR_NULL(data->cool_dev)) {
-               dev_err(bg_ptr->dev,
-                       "Failed to register cpufreq cooling device\n");
-               return PTR_ERR(data->cool_dev);
-       }
-       omap_bandgap_set_sensor_data(bg_ptr, id, data);
-
-       return 0;
-}
-
-int omap_thermal_unregister_cpu_cooling(struct omap_bandgap *bg_ptr, int id)
-{
-       struct omap_thermal_data *data;
-
-       data = omap_bandgap_get_sensor_data(bg_ptr, id);
-       cpufreq_cooling_unregister(data->cool_dev);
-
-       return 0;
-}
diff --git a/drivers/staging/omap-thermal/omap5-thermal.c b/drivers/staging/omap-thermal/omap5-thermal.c
deleted file mode 100644 (file)
index 2f3a498..0000000
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * OMAP5 thermal driver.
- *
- * Copyright (C) 2011-2012 Texas Instruments Inc.
- * Contact:
- *     Eduardo Valentin <eduardo.valentin@ti.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include "omap-bandgap.h"
-#include "omap-thermal.h"
-
-/*
- * omap5430 has one instance of thermal sensor for MPU
- * need to describe the individual bit fields
- */
-static struct temp_sensor_registers
-omap5430_mpu_temp_sensor_registers = {
-       .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_MPU_OFFSET,
-       .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK,
-       .bgap_soc_mask = OMAP5430_BGAP_TEMP_SENSOR_SOC_MASK,
-       .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK,
-       .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK,
-
-       .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET,
-       .mask_hot_mask = OMAP5430_MASK_HOT_MPU_MASK,
-       .mask_cold_mask = OMAP5430_MASK_COLD_MPU_MASK,
-
-       .bgap_mode_ctrl = OMAP5430_BGAP_COUNTER_MPU_OFFSET,
-       .mode_ctrl_mask = OMAP5430_REPEAT_MODE_MASK,
-
-       .bgap_counter = OMAP5430_BGAP_COUNTER_MPU_OFFSET,
-       .counter_mask = OMAP5430_COUNTER_MASK,
-
-       .bgap_threshold = OMAP5430_BGAP_THRESHOLD_MPU_OFFSET,
-       .threshold_thot_mask = OMAP5430_T_HOT_MASK,
-       .threshold_tcold_mask = OMAP5430_T_COLD_MASK,
-
-       .tshut_threshold = OMAP5430_BGAP_TSHUT_MPU_OFFSET,
-       .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK,
-       .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK,
-
-       .bgap_status = OMAP5430_BGAP_STATUS_OFFSET,
-       .status_clean_stop_mask = 0x0,
-       .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK,
-       .status_hot_mask = OMAP5430_HOT_MPU_FLAG_MASK,
-       .status_cold_mask = OMAP5430_COLD_MPU_FLAG_MASK,
-
-       .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_MPU,
-};
-
-/*
- * omap5430 has one instance of thermal sensor for GPU
- * need to describe the individual bit fields
- */
-static struct temp_sensor_registers
-omap5430_gpu_temp_sensor_registers = {
-       .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_GPU_OFFSET,
-       .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK,
-       .bgap_soc_mask = OMAP5430_BGAP_TEMP_SENSOR_SOC_MASK,
-       .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK,
-       .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK,
-
-       .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET,
-       .mask_hot_mask = OMAP5430_MASK_HOT_MM_MASK,
-       .mask_cold_mask = OMAP5430_MASK_COLD_MM_MASK,
-
-       .bgap_mode_ctrl = OMAP5430_BGAP_COUNTER_GPU_OFFSET,
-       .mode_ctrl_mask = OMAP5430_REPEAT_MODE_MASK,
-
-       .bgap_counter = OMAP5430_BGAP_COUNTER_GPU_OFFSET,
-       .counter_mask = OMAP5430_COUNTER_MASK,
-
-       .bgap_threshold = OMAP5430_BGAP_THRESHOLD_GPU_OFFSET,
-       .threshold_thot_mask = OMAP5430_T_HOT_MASK,
-       .threshold_tcold_mask = OMAP5430_T_COLD_MASK,
-
-       .tshut_threshold = OMAP5430_BGAP_TSHUT_GPU_OFFSET,
-       .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK,
-       .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK,
-
-       .bgap_status = OMAP5430_BGAP_STATUS_OFFSET,
-       .status_clean_stop_mask = 0x0,
-       .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK,
-       .status_hot_mask = OMAP5430_HOT_MM_FLAG_MASK,
-       .status_cold_mask = OMAP5430_COLD_MM_FLAG_MASK,
-
-       .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_GPU,
-};
-
-/*
- * omap5430 has one instance of thermal sensor for CORE
- * need to describe the individual bit fields
- */
-static struct temp_sensor_registers
-omap5430_core_temp_sensor_registers = {
-       .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_CORE_OFFSET,
-       .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK,
-       .bgap_soc_mask = OMAP5430_BGAP_TEMP_SENSOR_SOC_MASK,
-       .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK,
-       .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK,
-
-       .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET,
-       .mask_hot_mask = OMAP5430_MASK_HOT_CORE_MASK,
-       .mask_cold_mask = OMAP5430_MASK_COLD_CORE_MASK,
-
-       .bgap_mode_ctrl = OMAP5430_BGAP_COUNTER_CORE_OFFSET,
-       .mode_ctrl_mask = OMAP5430_REPEAT_MODE_MASK,
-
-       .bgap_counter = OMAP5430_BGAP_COUNTER_CORE_OFFSET,
-       .counter_mask = OMAP5430_COUNTER_MASK,
-
-       .bgap_threshold = OMAP5430_BGAP_THRESHOLD_CORE_OFFSET,
-       .threshold_thot_mask = OMAP5430_T_HOT_MASK,
-       .threshold_tcold_mask = OMAP5430_T_COLD_MASK,
-
-       .tshut_threshold = OMAP5430_BGAP_TSHUT_CORE_OFFSET,
-       .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK,
-       .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK,
-
-       .bgap_status = OMAP5430_BGAP_STATUS_OFFSET,
-       .status_clean_stop_mask = 0x0,
-       .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK,
-       .status_hot_mask = OMAP5430_HOT_CORE_FLAG_MASK,
-       .status_cold_mask = OMAP5430_COLD_CORE_FLAG_MASK,
-
-       .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_CORE,
-};
-
-/* Thresholds and limits for OMAP5430 MPU temperature sensor */
-static struct temp_sensor_data omap5430_mpu_temp_sensor_data = {
-       .tshut_hot = OMAP5430_MPU_TSHUT_HOT,
-       .tshut_cold = OMAP5430_MPU_TSHUT_COLD,
-       .t_hot = OMAP5430_MPU_T_HOT,
-       .t_cold = OMAP5430_MPU_T_COLD,
-       .min_freq = OMAP5430_MPU_MIN_FREQ,
-       .max_freq = OMAP5430_MPU_MAX_FREQ,
-       .max_temp = OMAP5430_MPU_MAX_TEMP,
-       .min_temp = OMAP5430_MPU_MIN_TEMP,
-       .hyst_val = OMAP5430_MPU_HYST_VAL,
-       .adc_start_val = OMAP5430_ADC_START_VALUE,
-       .adc_end_val = OMAP5430_ADC_END_VALUE,
-       .update_int1 = 1000,
-       .update_int2 = 2000,
-};
-
-/* Thresholds and limits for OMAP5430 GPU temperature sensor */
-static struct temp_sensor_data omap5430_gpu_temp_sensor_data = {
-       .tshut_hot = OMAP5430_GPU_TSHUT_HOT,
-       .tshut_cold = OMAP5430_GPU_TSHUT_COLD,
-       .t_hot = OMAP5430_GPU_T_HOT,
-       .t_cold = OMAP5430_GPU_T_COLD,
-       .min_freq = OMAP5430_GPU_MIN_FREQ,
-       .max_freq = OMAP5430_GPU_MAX_FREQ,
-       .max_temp = OMAP5430_GPU_MAX_TEMP,
-       .min_temp = OMAP5430_GPU_MIN_TEMP,
-       .hyst_val = OMAP5430_GPU_HYST_VAL,
-       .adc_start_val = OMAP5430_ADC_START_VALUE,
-       .adc_end_val = OMAP5430_ADC_END_VALUE,
-       .update_int1 = 1000,
-       .update_int2 = 2000,
-};
-
-/* Thresholds and limits for OMAP5430 CORE temperature sensor */
-static struct temp_sensor_data omap5430_core_temp_sensor_data = {
-       .tshut_hot = OMAP5430_CORE_TSHUT_HOT,
-       .tshut_cold = OMAP5430_CORE_TSHUT_COLD,
-       .t_hot = OMAP5430_CORE_T_HOT,
-       .t_cold = OMAP5430_CORE_T_COLD,
-       .min_freq = OMAP5430_CORE_MIN_FREQ,
-       .max_freq = OMAP5430_CORE_MAX_FREQ,
-       .max_temp = OMAP5430_CORE_MAX_TEMP,
-       .min_temp = OMAP5430_CORE_MIN_TEMP,
-       .hyst_val = OMAP5430_CORE_HYST_VAL,
-       .adc_start_val = OMAP5430_ADC_START_VALUE,
-       .adc_end_val = OMAP5430_ADC_END_VALUE,
-       .update_int1 = 1000,
-       .update_int2 = 2000,
-};
-
-static const int
-omap5430_adc_to_temp[OMAP5430_ADC_END_VALUE - OMAP5430_ADC_START_VALUE + 1] = {
-       -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600,
-       -38200, -37800, -37300, -36800,
-       -36400, -36000, -35600, -35200, -34800, -34300, -33800, -33400, -33000,
-       -32600,
-       -32200, -31800, -31300, -30800, -30400, -30000, -29600, -29200, -28700,
-       -28200, -27800, -27400, -27000, -26600, -26200, -25700, -25200, -24800,
-       -24400, -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000,
-       -20600, -20200, -19700, -19200, -9300, -18400, -18000, -17600, -17200,
-       -16700, -16200, -15800, -15400, -15000, -14600, -14200, -13700, -13200,
-       -12800, -12400, -12000, -11600, -11200, -10700, -10200, -9800, -9400,
-       -9000,
-       -8600, -8200, -7700, -7200, -6800, -6400, -6000, -5600, -5200, -4800,
-       -4300,
-       -3800, -3400, -3000, -2600, -2200, -1800, -1300, -800, -400, 0, 400,
-       800,
-       1200, 1600, 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000,
-       6400, 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10800,
-       11100,
-       11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800, 15300,
-       15800,
-       16200, 16600, 17000, 17400, 17800, 18200, 18700, 19200, 19600, 20000,
-       20400,
-       20800, 21200, 21600, 22100, 22600, 23000, 23400, 23800, 24200, 24600,
-       25000,
-       25400, 25900, 26400, 26800, 27200, 27600, 28000, 28400, 28800, 29300,
-       29800,
-       30200, 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000,
-       34400,
-       34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800, 38200, 38600,
-       39000,
-       39400, 39800, 40200, 40600, 41100, 41600, 42000, 42400, 42800, 43200,
-       43600,
-       44000, 44400, 44800, 45300, 45800, 46200, 46600, 47000, 47400, 47800,
-       48200,
-       48600, 49000, 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400,
-       52800,
-       53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600, 57000,
-       57400,
-       57800, 58200, 58700, 59200, 59600, 60000, 60400, 60800, 61200, 61600,
-       62000,
-       62400, 62800, 63300, 63800, 64200, 64600, 65000, 65400, 65800, 66200,
-       66600,
-       67000, 67400, 67800, 68200, 68700, 69200, 69600, 70000, 70400, 70800,
-       71200,
-       71600, 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400,
-       75800,
-       76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000, 79400, 79800,
-       80300,
-       80800, 81200, 81600, 82000, 82400, 82800, 83200, 83600, 84000, 84400,
-       84800,
-       85200, 85600, 86000, 86400, 86800, 87300, 87800, 88200, 88600, 89000,
-       89400,
-       89800, 90200, 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400,
-       93800,
-       94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600, 98000,
-       98400,
-       98800, 99200, 99600, 100000, 100400, 100800, 101200, 101600, 102000,
-       102400,
-       102800, 103200, 103600, 104000, 104400, 104800, 105200, 105600, 106100,
-       106600, 107000, 107400, 107800, 108200, 108600, 109000, 109400, 109800,
-       110200, 110600, 111000, 111400, 111800, 112200, 112600, 113000, 113400,
-       113800, 114200, 114600, 115000, 115400, 115800, 116200, 116600, 117000,
-       117400, 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600,
-       121000, 121400, 121800, 122200, 122600, 123000, 123400, 123800, 124200,
-       124600, 124900, 125000, 125000, 125000, 125000,
-};
-
-const struct omap_bandgap_data omap5430_data = {
-       .features = OMAP_BANDGAP_FEATURE_TSHUT_CONFIG |
-                       OMAP_BANDGAP_FEATURE_TALERT |
-                       OMAP_BANDGAP_FEATURE_MODE_CONFIG |
-                       OMAP_BANDGAP_FEATURE_COUNTER,
-       .fclock_name = "ts_clk_div_ck",
-       .div_ck_name = "ts_clk_div_ck",
-       .conv_table = omap5430_adc_to_temp,
-       .expose_sensor = omap_thermal_expose_sensor,
-       .remove_sensor = omap_thermal_remove_sensor,
-       .sensors = {
-               {
-               .registers = &omap5430_mpu_temp_sensor_registers,
-               .ts_data = &omap5430_mpu_temp_sensor_data,
-               .domain = "cpu",
-               .register_cooling = omap_thermal_register_cpu_cooling,
-               .unregister_cooling = omap_thermal_unregister_cpu_cooling,
-               .slope = OMAP_GRADIENT_SLOPE_5430_CPU,
-               .constant = OMAP_GRADIENT_CONST_5430_CPU,
-               .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU,
-               .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_CPU,
-               },
-               {
-               .registers = &omap5430_gpu_temp_sensor_registers,
-               .ts_data = &omap5430_gpu_temp_sensor_data,
-               .domain = "gpu",
-               .slope = OMAP_GRADIENT_SLOPE_5430_GPU,
-               .constant = OMAP_GRADIENT_CONST_5430_GPU,
-               .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU,
-               .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_GPU,
-               },
-               {
-               .registers = &omap5430_core_temp_sensor_registers,
-               .ts_data = &omap5430_core_temp_sensor_data,
-               .domain = "core",
-               },
-       },
-       .sensor_count = 3,
-};
index d85e058f2845a014c0216cabeda52d70cc7bf0b6..a6bddd858eb8531914f600c3199eb8b693f41d23 100644 (file)
@@ -16,7 +16,7 @@ omapdrm-y := omap_drv.o \
        omap_gem.o \
        omap_gem_dmabuf.o \
        omap_dmm_tiler.o \
-       tcm-sita.o
+       sita.o
 
 # temporary:
 omapdrm-y += omap_gem_helpers.o
index 273ec12c028abd13f3257cbdc7bb24628b156bf1..58bcd6ae0255dace4ae845c366af35cf62be4f20 100644 (file)
@@ -118,6 +118,11 @@ struct pat {
 #define DESCR_SIZE 128
 #define REFILL_BUFFER_SIZE ((4 * 128 * 256) + (3 * DESCR_SIZE))
 
+/* For OMAP5, a fixed offset is added to all Y coordinates for 1D buffers.
+ * This is used in programming to address the upper portion of the LUT
+*/
+#define OMAP5_LUT_OFFSET       128
+
 struct dmm;
 
 struct dmm_txn {
index 59bf43899fc09d20ac5c01a0c1894f4b166bc189..9e43ae2372770e179219b737be2820bc86f2ec85 100644 (file)
@@ -213,6 +213,11 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
                txn->last_pat->next_pa = (uint32_t)pat_pa;
 
        pat->area = *area;
+
+       /* adjust Y coordinates based off of container parameters */
+       pat->area.y0 += engine->tcm->y_offset;
+       pat->area.y1 += engine->tcm->y_offset;
+
        pat->ctrl = (struct pat_ctrl){
                        .start = 1,
                        .lut_id = engine->tcm->lut_id,
@@ -348,6 +353,7 @@ struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w,
        u32 min_align = 128;
        int ret;
        unsigned long flags;
+       size_t slot_bytes;
 
        BUG_ON(!validfmt(fmt));
 
@@ -356,13 +362,15 @@ struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w,
        h = DIV_ROUND_UP(h, geom[fmt].slot_h);
 
        /* convert alignment to slots */
-       min_align = max(min_align, (geom[fmt].slot_w * geom[fmt].cpp));
-       align = ALIGN(align, min_align);
-       align /= geom[fmt].slot_w * geom[fmt].cpp;
+       slot_bytes = geom[fmt].slot_w * geom[fmt].cpp;
+       min_align = max(min_align, slot_bytes);
+       align = (align > min_align) ? ALIGN(align, min_align) : min_align;
+       align /= slot_bytes;
 
        block->fmt = fmt;
 
-       ret = tcm_reserve_2d(containers[fmt], w, h, align, &block->area);
+       ret = tcm_reserve_2d(containers[fmt], w, h, align, -1, slot_bytes,
+                       &block->area);
        if (ret) {
                kfree(block);
                return ERR_PTR(-ENOMEM);
@@ -622,6 +630,11 @@ static int omap_dmm_probe(struct platform_device *dev)
        omap_dmm->lut_width = ((pat_geom >> 16) & 0xF) << 5;
        omap_dmm->lut_height = ((pat_geom >> 24) & 0xF) << 5;
 
+       /* increment LUT by one if on OMAP5 */
+       /* LUT has twice the height, and is split into a separate container */
+       if (omap_dmm->lut_height != omap_dmm->container_height)
+               omap_dmm->num_lut++;
+
        /* initialize DMM registers */
        writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__0);
        writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__1);
@@ -701,10 +714,12 @@ static int omap_dmm_probe(struct platform_device *dev)
        }
 
        /* init containers */
+       /* Each LUT is associated with a TCM (container manager).  We use the
+          lut_id to denote the lut_id used to identify the correct LUT for
+          programming during reill operations */
        for (i = 0; i < omap_dmm->num_lut; i++) {
                omap_dmm->tcm[i] = sita_init(omap_dmm->container_width,
-                                               omap_dmm->container_height,
-                                               NULL);
+                                               omap_dmm->container_height);
 
                if (!omap_dmm->tcm[i]) {
                        dev_err(&dev->dev, "failed to allocate container\n");
@@ -717,13 +732,23 @@ static int omap_dmm_probe(struct platform_device *dev)
 
        /* assign access mode containers to applicable tcm container */
        /* OMAP 4 has 1 container for all 4 views */
+       /* OMAP 5 has 2 containers, 1 for 2D and 1 for 1D */
        containers[TILFMT_8BIT] = omap_dmm->tcm[0];
        containers[TILFMT_16BIT] = omap_dmm->tcm[0];
        containers[TILFMT_32BIT] = omap_dmm->tcm[0];
-       containers[TILFMT_PAGE] = omap_dmm->tcm[0];
+
+       if (omap_dmm->container_height != omap_dmm->lut_height) {
+               /* second LUT is used for PAGE mode.  Programming must use
+                  y offset that is added to all y coordinates.  LUT id is still
+                  0, because it is the same LUT, just the upper 128 lines */
+               containers[TILFMT_PAGE] = omap_dmm->tcm[1];
+               omap_dmm->tcm[1]->y_offset = OMAP5_LUT_OFFSET;
+               omap_dmm->tcm[1]->lut_id = 0;
+       } else {
+               containers[TILFMT_PAGE] = omap_dmm->tcm[0];
+       }
 
        area = (struct tcm_area) {
-               .is2d = true,
                .tcm = NULL,
                .p1.x = omap_dmm->container_width - 1,
                .p1.y = omap_dmm->container_height - 1,
@@ -835,64 +860,81 @@ int tiler_map_show(struct seq_file *s, void *arg)
        int h_adj;
        int w_adj;
        unsigned long flags;
+       int lut_idx;
+
 
        if (!omap_dmm) {
                /* early return if dmm/tiler device is not initialized */
                return 0;
        }
 
-       h_adj = omap_dmm->lut_height / ydiv;
-       w_adj = omap_dmm->lut_width / xdiv;
+       h_adj = omap_dmm->container_height / ydiv;
+       w_adj = omap_dmm->container_width / xdiv;
 
-       map = kzalloc(h_adj * sizeof(*map), GFP_KERNEL);
-       global_map = kzalloc((w_adj + 1) * h_adj, GFP_KERNEL);
+       map = kmalloc(h_adj * sizeof(*map), GFP_KERNEL);
+       global_map = kmalloc((w_adj + 1) * h_adj, GFP_KERNEL);
 
        if (!map || !global_map)
                goto error;
 
-       memset(global_map, ' ', (w_adj + 1) * h_adj);
-       for (i = 0; i < omap_dmm->lut_height; i++) {
-               map[i] = global_map + i * (w_adj + 1);
-               map[i][w_adj] = 0;
-       }
-       spin_lock_irqsave(&list_lock, flags);
+       for (lut_idx = 0; lut_idx < omap_dmm->num_lut; lut_idx++) {
+               memset(map, 0, sizeof(h_adj * sizeof(*map)));
+               memset(global_map, ' ', (w_adj + 1) * h_adj);
 
-       list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) {
-               if (block->fmt != TILFMT_PAGE) {
-                       fill_map(map, xdiv, ydiv, &block->area, *m2dp, true);
-                       if (!*++a2dp)
-                               a2dp = a2d;
-                       if (!*++m2dp)
-                               m2dp = m2d;
-                       map_2d_info(map, xdiv, ydiv, nice, &block->area);
-               } else {
-                       bool start = read_map_pt(map, xdiv, ydiv,
-                                                       &block->area.p0)
-                                                                       == ' ';
-                       bool end = read_map_pt(map, xdiv, ydiv, &block->area.p1)
-                                                                       == ' ';
-                       tcm_for_each_slice(a, block->area, p)
-                               fill_map(map, xdiv, ydiv, &a, '=', true);
-                       fill_map_pt(map, xdiv, ydiv, &block->area.p0,
+               for (i = 0; i < omap_dmm->container_height; i++) {
+                       map[i] = global_map + i * (w_adj + 1);
+                       map[i][w_adj] = 0;
+               }
+
+               spin_lock_irqsave(&list_lock, flags);
+
+               list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) {
+                       if (block->area.tcm == omap_dmm->tcm[lut_idx]) {
+                               if (block->fmt != TILFMT_PAGE) {
+                                       fill_map(map, xdiv, ydiv, &block->area,
+                                               *m2dp, true);
+                                       if (!*++a2dp)
+                                               a2dp = a2d;
+                                       if (!*++m2dp)
+                                               m2dp = m2d;
+                                       map_2d_info(map, xdiv, ydiv, nice,
+                                                       &block->area);
+                               } else {
+                                       bool start = read_map_pt(map, xdiv,
+                                               ydiv, &block->area.p0) == ' ';
+                                       bool end = read_map_pt(map, xdiv, ydiv,
+                                                       &block->area.p1) == ' ';
+
+                                       tcm_for_each_slice(a, block->area, p)
+                                               fill_map(map, xdiv, ydiv, &a,
+                                                       '=', true);
+                                       fill_map_pt(map, xdiv, ydiv,
+                                                       &block->area.p0,
                                                        start ? '<' : 'X');
-                       fill_map_pt(map, xdiv, ydiv, &block->area.p1,
+                                       fill_map_pt(map, xdiv, ydiv,
+                                                       &block->area.p1,
                                                        end ? '>' : 'X');
-                       map_1d_info(map, xdiv, ydiv, nice, &block->area);
+                                       map_1d_info(map, xdiv, ydiv, nice,
+                                                       &block->area);
+                               }
+                       }
                }
-       }
 
-       spin_unlock_irqrestore(&list_lock, flags);
+               spin_unlock_irqrestore(&list_lock, flags);
 
-       if (s) {
-               seq_printf(s, "BEGIN DMM TILER MAP\n");
-               for (i = 0; i < 128; i++)
-                       seq_printf(s, "%03d:%s\n", i, map[i]);
-               seq_printf(s, "END TILER MAP\n");
-       } else {
-               dev_dbg(omap_dmm->dev, "BEGIN DMM TILER MAP\n");
-               for (i = 0; i < 128; i++)
-                       dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]);
-               dev_dbg(omap_dmm->dev, "END TILER MAP\n");
+               if (s) {
+                       seq_printf(s, "CONTAINER %d DUMP BEGIN\n", lut_idx);
+                       for (i = 0; i < 128; i++)
+                               seq_printf(s, "%03d:%s\n", i, map[i]);
+                       seq_printf(s, "CONTAINER %d DUMP END\n", lut_idx);
+               } else {
+                       dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP BEGIN\n",
+                               lut_idx);
+                       for (i = 0; i < 128; i++)
+                               dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]);
+                       dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP END\n",
+                               lut_idx);
+               }
        }
 
 error:
@@ -903,12 +945,45 @@ error:
 }
 #endif
 
+#ifdef CONFIG_PM
+static int omap_dmm_resume(struct device *dev)
+{
+       struct tcm_area area;
+       int i;
+
+       if (!omap_dmm)
+               return -ENODEV;
+
+       area = (struct tcm_area) {
+               .tcm = NULL,
+               .p1.x = omap_dmm->container_width - 1,
+               .p1.y = omap_dmm->container_height - 1,
+       };
+
+       /* initialize all LUTs to dummy page entries */
+       for (i = 0; i < omap_dmm->num_lut; i++) {
+               area.tcm = omap_dmm->tcm[i];
+               if (fill(&area, NULL, 0, 0, true))
+                       dev_err(dev, "refill failed");
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops omap_dmm_pm_ops = {
+       .resume = omap_dmm_resume,
+};
+#endif
+
 struct platform_driver omap_dmm_driver = {
        .probe = omap_dmm_probe,
        .remove = omap_dmm_remove,
        .driver = {
                .owner = THIS_MODULE,
                .name = DMM_DRIVER_NAME,
+#ifdef CONFIG_PM
+               .pm = &omap_dmm_pm_ops,
+#endif
        },
 };
 
index ae5ecc2efbc758268d692d732482d1f49f13c352..d246f8543d2ee20b8fe5df7a9386d969da1dba95 100644 (file)
@@ -368,6 +368,9 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
                /* well, limp along without an fbdev.. maybe X11 will work? */
        }
 
+       /* store off drm_device for use in pm ops */
+       dev_set_drvdata(dev->dev, dev);
+
        drm_kms_helper_poll_init(dev);
 
        return 0;
@@ -393,6 +396,8 @@ static int dev_unload(struct drm_device *dev)
        kfree(dev->dev_private);
        dev->dev_private = NULL;
 
+       dev_set_drvdata(dev->dev, NULL);
+
        return 0;
 }
 
@@ -558,10 +563,19 @@ static int pdev_remove(struct platform_device *device)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static const struct dev_pm_ops omapdrm_pm_ops = {
+       .resume = omap_gem_resume,
+};
+#endif
+
 struct platform_driver pdev = {
                .driver = {
                        .name = DRIVER_NAME,
                        .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+                       .pm = &omapdrm_pm_ops,
+#endif
                },
                .probe = pdev_probe,
                .remove = pdev_remove,
index cd1f22b0b124ddbf9eb1eff0efe5b77ddb7fd061..f921027e7500efb8de4fc350f330ec34ea552044 100644 (file)
@@ -135,6 +135,10 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
 void omap_gem_describe_objects(struct list_head *list, struct seq_file *m);
 #endif
 
+#ifdef CONFIG_PM
+int omap_gem_resume(struct device *dev);
+#endif
+
 int omap_irq_enable_vblank(struct drm_device *dev, int crtc);
 void omap_irq_disable_vblank(struct drm_device *dev, int crtc);
 irqreturn_t omap_irq_handler(DRM_IRQ_ARGS);
index c38992b76fc9f86bf9a4664dfc2e90516041b422..08f1e292ed201c57a65bddf67e84de6695a5bf0a 100644 (file)
@@ -964,6 +964,34 @@ void *omap_gem_vaddr(struct drm_gem_object *obj)
        return omap_obj->vaddr;
 }
 
+#ifdef CONFIG_PM
+/* re-pin objects in DMM in resume path: */
+int omap_gem_resume(struct device *dev)
+{
+       struct drm_device *drm_dev = dev_get_drvdata(dev);
+       struct omap_drm_private *priv = drm_dev->dev_private;
+       struct omap_gem_object *omap_obj;
+       int ret = 0;
+
+       list_for_each_entry(omap_obj, &priv->obj_list, mm_list) {
+               if (omap_obj->block) {
+                       struct drm_gem_object *obj = &omap_obj->base;
+                       uint32_t npages = obj->size >> PAGE_SHIFT;
+                       WARN_ON(!omap_obj->pages);  /* this can't happen */
+                       ret = tiler_pin(omap_obj->block,
+                                       omap_obj->pages, npages,
+                                       omap_obj->roll, true);
+                       if (ret) {
+                               dev_err(dev, "could not repin: %d\n", ret);
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+#endif
+
 #ifdef CONFIG_DEBUG_FS
 void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 {
diff --git a/drivers/staging/omapdrm/sita.c b/drivers/staging/omapdrm/sita.c
new file mode 100644 (file)
index 0000000..07fc7ee
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * sita.c
+ *
+ * SImple Tiler Allocator (SiTA): 2D and 1D allocation(reservation) algorithm
+ *
+ * Authors: Ravi Ramachandra <r.ramachandra@ti.com>,
+ *          Lajos Molnar <molnar@ti.com>
+ *          Andy Gross <andy.gross@ti.com>
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/bitmap.h>
+#include <linux/slab.h>
+#include "tcm.h"
+
+static unsigned long mask[8];
+/*
+ * pos         position in bitmap
+ * w           width in slots
+ * h           height in slots
+ * map         ptr to bitmap
+ * stride              slots in a row
+ */
+static void free_slots(unsigned long pos, uint16_t w, uint16_t h,
+               unsigned long *map, uint16_t stride)
+{
+       int i;
+
+       for (i = 0; i < h; i++, pos += stride)
+               bitmap_clear(map, pos, w);
+}
+
+/*
+ * w           width in slots
+ * pos         ptr to position
+ * map         ptr to bitmap
+ * num_bits    number of bits in bitmap
+ */
+static int r2l_b2t_1d(uint16_t w, unsigned long *pos, unsigned long *map,
+               size_t num_bits)
+{
+       unsigned long search_count = 0;
+       unsigned long bit;
+       bool area_found = false;
+
+       *pos = num_bits - w;
+
+       while (search_count < num_bits) {
+               bit = find_next_bit(map, num_bits, *pos);
+
+               if (bit - *pos >= w) {
+                       /* found a long enough free area */
+                       bitmap_set(map, *pos, w);
+                       area_found = true;
+                       break;
+               }
+
+               search_count = num_bits - bit + w;
+               *pos = bit - w;
+       }
+
+       return (area_found) ? 0 : -ENOMEM;
+}
+
+/*
+ * w = width in slots
+ * h = height in slots
+ * a = align in slots  (mask, 2^n-1, 0 is unaligned)
+ * offset = offset in bytes from 4KiB
+ * pos = position in bitmap for buffer
+ * map = bitmap ptr
+ * num_bits = size of bitmap
+ * stride = bits in one row of container
+ */
+static int l2r_t2b(uint16_t w, uint16_t h, uint16_t a, int16_t offset,
+               unsigned long *pos, unsigned long slot_bytes,
+               unsigned long *map, size_t num_bits, size_t slot_stride)
+{
+       int i;
+       unsigned long index;
+       bool area_free;
+       unsigned long slots_per_band = PAGE_SIZE / slot_bytes;
+       unsigned long bit_offset = (offset > 0) ? offset / slot_bytes : 0;
+       unsigned long curr_bit = bit_offset;
+
+       /* reset alignment to 1 if we are matching a specific offset */
+       /* adjust alignment - 1 to get to the format expected in bitmaps */
+       a = (offset > 0) ? 0 : a - 1;
+
+       /* FIXME Return error if slots_per_band > stride */
+
+       while (curr_bit < num_bits) {
+               *pos = bitmap_find_next_zero_area(map, num_bits, curr_bit, w,
+                               a);
+
+               /* skip forward if we are not at right offset */
+               if (bit_offset > 0 && (*pos % slots_per_band != bit_offset)) {
+                       curr_bit = ALIGN(*pos, slots_per_band) + bit_offset;
+                       continue;
+               }
+
+               /* skip forward to next row if we overlap end of row */
+               if ((*pos % slot_stride) + w > slot_stride) {
+                       curr_bit = ALIGN(*pos, slot_stride) + bit_offset;
+                       continue;
+               }
+
+               /* TODO: Handle overlapping 4K boundaries */
+
+               /* break out of look if we will go past end of container */
+               if ((*pos + slot_stride * h) > num_bits)
+                       break;
+
+               /* generate mask that represents out matching pattern */
+               bitmap_clear(mask, 0, slot_stride);
+               bitmap_set(mask, (*pos % BITS_PER_LONG), w);
+
+               /* assume the area is free until we find an overlap */
+               area_free = true;
+
+               /* check subsequent rows to see if complete area is free */
+               for (i = 1; i < h; i++) {
+                       index = *pos / BITS_PER_LONG + i * 8;
+                       if (bitmap_intersects(&map[index], mask,
+                               (*pos % BITS_PER_LONG) + w)) {
+                               area_free = false;
+                               break;
+                       }
+               }
+
+               if (area_free)
+                       break;
+
+               /* go forward past this match */
+               if (bit_offset > 0)
+                       curr_bit = ALIGN(*pos, slots_per_band) + bit_offset;
+               else
+                       curr_bit = *pos + a + 1;
+       }
+
+       if (area_free) {
+               /* set area as in-use. iterate over rows */
+               for (i = 0, index = *pos; i < h; i++, index += slot_stride)
+                       bitmap_set(map, index, w);
+       }
+
+       return (area_free) ? 0 : -ENOMEM;
+}
+
+static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots,
+                          struct tcm_area *area)
+{
+       unsigned long pos;
+       int ret;
+
+       spin_lock(&(tcm->lock));
+       ret = r2l_b2t_1d(num_slots, &pos, tcm->bitmap, tcm->map_size);
+       if (!ret) {
+               area->p0.x = pos % tcm->width;
+               area->p0.y = pos / tcm->width;
+               area->p1.x = (pos + num_slots - 1) % tcm->width;
+               area->p1.y = (pos + num_slots - 1) / tcm->width;
+       }
+       spin_unlock(&(tcm->lock));
+
+       return ret;
+}
+
+static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u16 align,
+                               int16_t offset, uint16_t slot_bytes,
+                               struct tcm_area *area)
+{
+       unsigned long pos;
+       int ret;
+
+       spin_lock(&(tcm->lock));
+       ret = l2r_t2b(w, h, align, offset, &pos, slot_bytes, tcm->bitmap,
+                       tcm->map_size, tcm->width);
+
+       if (!ret) {
+               area->p0.x = pos % tcm->width;
+               area->p0.y = pos / tcm->width;
+               area->p1.x = area->p0.x + w - 1;
+               area->p1.y = area->p0.y + h - 1;
+       }
+       spin_unlock(&(tcm->lock));
+
+       return ret;
+}
+
+static void sita_deinit(struct tcm *tcm)
+{
+       kfree(tcm);
+}
+
+static s32 sita_free(struct tcm *tcm, struct tcm_area *area)
+{
+       unsigned long pos;
+       uint16_t w, h;
+
+       pos = area->p0.x + area->p0.y * tcm->width;
+       if (area->is2d) {
+               w = area->p1.x - area->p0.x + 1;
+               h = area->p1.y - area->p0.y + 1;
+       } else {
+               w = area->p1.x + area->p1.y * tcm->width - pos + 1;
+               h = 1;
+       }
+
+       spin_lock(&(tcm->lock));
+       free_slots(pos, w, h, tcm->bitmap, tcm->width);
+       spin_unlock(&(tcm->lock));
+       return 0;
+}
+
+struct tcm *sita_init(u16 width, u16 height)
+{
+       struct tcm *tcm;
+       size_t map_size = BITS_TO_LONGS(width*height) * sizeof(unsigned long);
+
+       if (width == 0 || height == 0)
+               return NULL;
+
+       tcm = kzalloc(sizeof(*tcm) + map_size, GFP_KERNEL);
+       if (!tcm)
+               goto error;
+
+       /* Updating the pointers to SiTA implementation APIs */
+       tcm->height = height;
+       tcm->width = width;
+       tcm->reserve_2d = sita_reserve_2d;
+       tcm->reserve_1d = sita_reserve_1d;
+       tcm->free = sita_free;
+       tcm->deinit = sita_deinit;
+
+       spin_lock_init(&tcm->lock);
+       tcm->bitmap = (unsigned long *)(tcm + 1);
+       bitmap_clear(tcm->bitmap, 0, width*height);
+
+       tcm->map_size = width*height;
+
+       return tcm;
+
+error:
+       kfree(tcm);
+       return NULL;
+}
diff --git a/drivers/staging/omapdrm/tcm-sita.c b/drivers/staging/omapdrm/tcm-sita.c
deleted file mode 100644 (file)
index efb6095..0000000
+++ /dev/null
@@ -1,703 +0,0 @@
-/*
- * tcm-sita.c
- *
- * SImple Tiler Allocator (SiTA): 2D and 1D allocation(reservation) algorithm
- *
- * Authors: Ravi Ramachandra <r.ramachandra@ti.com>,
- *          Lajos Molnar <molnar@ti.com>
- *
- * Copyright (C) 2009-2010 Texas Instruments, Inc.
- *
- * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- */
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-
-#include "tcm-sita.h"
-
-#define ALIGN_DOWN(value, align) ((value) & ~((align) - 1))
-
-/* Individual selection criteria for different scan areas */
-static s32 CR_L2R_T2B = CR_BIAS_HORIZONTAL;
-static s32 CR_R2L_T2B = CR_DIAGONAL_BALANCE;
-
-/*********************************************
- *     TCM API - Sita Implementation
- *********************************************/
-static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
-                          struct tcm_area *area);
-static s32 sita_reserve_1d(struct tcm *tcm, u32 slots, struct tcm_area *area);
-static s32 sita_free(struct tcm *tcm, struct tcm_area *area);
-static void sita_deinit(struct tcm *tcm);
-
-/*********************************************
- *     Main Scanner functions
- *********************************************/
-static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
-                                  struct tcm_area *area);
-
-static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
-                       struct tcm_area *field, struct tcm_area *area);
-
-static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
-                       struct tcm_area *field, struct tcm_area *area);
-
-static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
-                       struct tcm_area *field, struct tcm_area *area);
-
-/*********************************************
- *     Support Infrastructure Methods
- *********************************************/
-static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h);
-
-static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
-                           struct tcm_area *field, s32 criteria,
-                           struct score *best);
-
-static void get_nearness_factor(struct tcm_area *field,
-                               struct tcm_area *candidate,
-                               struct nearness_factor *nf);
-
-static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
-                              struct neighbor_stats *stat);
-
-static void fill_area(struct tcm *tcm,
-                               struct tcm_area *area, struct tcm_area *parent);
-
-
-/*********************************************/
-
-/*********************************************
- *     Utility Methods
- *********************************************/
-struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr)
-{
-       struct tcm *tcm;
-       struct sita_pvt *pvt;
-       struct tcm_area area = {0};
-       s32 i;
-
-       if (width == 0 || height == 0)
-               return NULL;
-
-       tcm = kmalloc(sizeof(*tcm), GFP_KERNEL);
-       pvt = kmalloc(sizeof(*pvt), GFP_KERNEL);
-       if (!tcm || !pvt)
-               goto error;
-
-       memset(tcm, 0, sizeof(*tcm));
-       memset(pvt, 0, sizeof(*pvt));
-
-       /* Updating the pointers to SiTA implementation APIs */
-       tcm->height = height;
-       tcm->width = width;
-       tcm->reserve_2d = sita_reserve_2d;
-       tcm->reserve_1d = sita_reserve_1d;
-       tcm->free = sita_free;
-       tcm->deinit = sita_deinit;
-       tcm->pvt = (void *)pvt;
-
-       spin_lock_init(&(pvt->lock));
-
-       /* Creating tam map */
-       pvt->map = kmalloc(sizeof(*pvt->map) * tcm->width, GFP_KERNEL);
-       if (!pvt->map)
-               goto error;
-
-       for (i = 0; i < tcm->width; i++) {
-               pvt->map[i] =
-                       kmalloc(sizeof(**pvt->map) * tcm->height,
-                                                               GFP_KERNEL);
-               if (pvt->map[i] == NULL) {
-                       while (i--)
-                               kfree(pvt->map[i]);
-                       kfree(pvt->map);
-                       goto error;
-               }
-       }
-
-       if (attr && attr->x <= tcm->width && attr->y <= tcm->height) {
-               pvt->div_pt.x = attr->x;
-               pvt->div_pt.y = attr->y;
-
-       } else {
-               /* Defaulting to 3:1 ratio on width for 2D area split */
-               /* Defaulting to 3:1 ratio on height for 2D and 1D split */
-               pvt->div_pt.x = (tcm->width * 3) / 4;
-               pvt->div_pt.y = (tcm->height * 3) / 4;
-       }
-
-       spin_lock(&(pvt->lock));
-       assign(&area, 0, 0, width - 1, height - 1);
-       fill_area(tcm, &area, NULL);
-       spin_unlock(&(pvt->lock));
-       return tcm;
-
-error:
-       kfree(tcm);
-       kfree(pvt);
-       return NULL;
-}
-
-static void sita_deinit(struct tcm *tcm)
-{
-       struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
-       struct tcm_area area = {0};
-       s32 i;
-
-       area.p1.x = tcm->width - 1;
-       area.p1.y = tcm->height - 1;
-
-       spin_lock(&(pvt->lock));
-       fill_area(tcm, &area, NULL);
-       spin_unlock(&(pvt->lock));
-
-       for (i = 0; i < tcm->height; i++)
-               kfree(pvt->map[i]);
-       kfree(pvt->map);
-       kfree(pvt);
-}
-
-/**
- * Reserve a 1D area in the container
- *
- * @param num_slots    size of 1D area
- * @param area         pointer to the area that will be populated with the
- *                     reserved area
- *
- * @return 0 on success, non-0 error value on failure.
- */
-static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots,
-                          struct tcm_area *area)
-{
-       s32 ret;
-       struct tcm_area field = {0};
-       struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
-
-       spin_lock(&(pvt->lock));
-
-       /* Scanning entire container */
-       assign(&field, tcm->width - 1, tcm->height - 1, 0, 0);
-
-       ret = scan_r2l_b2t_one_dim(tcm, num_slots, &field, area);
-       if (!ret)
-               /* update map */
-               fill_area(tcm, area, area);
-
-       spin_unlock(&(pvt->lock));
-       return ret;
-}
-
-/**
- * Reserve a 2D area in the container
- *
- * @param w    width
- * @param h    height
- * @param area pointer to the area that will be populated with the reserved
- *             area
- *
- * @return 0 on success, non-0 error value on failure.
- */
-static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
-                          struct tcm_area *area)
-{
-       s32 ret;
-       struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
-
-       /* not supporting more than 64 as alignment */
-       if (align > 64)
-               return -EINVAL;
-
-       /* we prefer 1, 32 and 64 as alignment */
-       align = align <= 1 ? 1 : align <= 32 ? 32 : 64;
-
-       spin_lock(&(pvt->lock));
-       ret = scan_areas_and_find_fit(tcm, w, h, align, area);
-       if (!ret)
-               /* update map */
-               fill_area(tcm, area, area);
-
-       spin_unlock(&(pvt->lock));
-       return ret;
-}
-
-/**
- * Unreserve a previously allocated 2D or 1D area
- * @param area area to be freed
- * @return 0 - success
- */
-static s32 sita_free(struct tcm *tcm, struct tcm_area *area)
-{
-       struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
-
-       spin_lock(&(pvt->lock));
-
-       /* check that this is in fact an existing area */
-       WARN_ON(pvt->map[area->p0.x][area->p0.y] != area ||
-               pvt->map[area->p1.x][area->p1.y] != area);
-
-       /* Clear the contents of the associated tiles in the map */
-       fill_area(tcm, area, NULL);
-
-       spin_unlock(&(pvt->lock));
-
-       return 0;
-}
-
-/**
- * Note: In general the cordinates in the scan field area relevant to the can
- * sweep directions. The scan origin (e.g. top-left corner) will always be
- * the p0 member of the field.  Therfore, for a scan from top-left p0.x <= p1.x
- * and p0.y <= p1.y; whereas, for a scan from bottom-right p1.x <= p0.x and p1.y
- * <= p0.y
- */
-
-/**
- * Raster scan horizontally right to left from top to bottom to find a place for
- * a 2D area of given size inside a scan field.
- *
- * @param w    width of desired area
- * @param h    height of desired area
- * @param align        desired area alignment
- * @param area pointer to the area that will be set to the best position
- * @param field        area to scan (inclusive)
- *
- * @return 0 on success, non-0 error value on failure.
- */
-static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
-                       struct tcm_area *field, struct tcm_area *area)
-{
-       s32 x, y;
-       s16 start_x, end_x, start_y, end_y, found_x = -1;
-       struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map;
-       struct score best = {{0}, {0}, {0}, 0};
-
-       start_x = field->p0.x;
-       end_x = field->p1.x;
-       start_y = field->p0.y;
-       end_y = field->p1.y;
-
-       /* check scan area co-ordinates */
-       if (field->p0.x < field->p1.x ||
-           field->p1.y < field->p0.y)
-               return -EINVAL;
-
-       /* check if allocation would fit in scan area */
-       if (w > LEN(start_x, end_x) || h > LEN(end_y, start_y))
-               return -ENOSPC;
-
-       /* adjust start_x and end_y, as allocation would not fit beyond */
-       start_x = ALIGN_DOWN(start_x - w + 1, align); /* - 1 to be inclusive */
-       end_y = end_y - h + 1;
-
-       /* check if allocation would still fit in scan area */
-       if (start_x < end_x)
-               return -ENOSPC;
-
-       /* scan field top-to-bottom, right-to-left */
-       for (y = start_y; y <= end_y; y++) {
-               for (x = start_x; x >= end_x; x -= align) {
-                       if (is_area_free(map, x, y, w, h)) {
-                               found_x = x;
-
-                               /* update best candidate */
-                               if (update_candidate(tcm, x, y, w, h, field,
-                                                       CR_R2L_T2B, &best))
-                                       goto done;
-
-                               /* change upper x bound */
-                               end_x = x + 1;
-                               break;
-                       } else if (map[x][y] && map[x][y]->is2d) {
-                               /* step over 2D areas */
-                               x = ALIGN(map[x][y]->p0.x - w + 1, align);
-                       }
-               }
-
-               /* break if you find a free area shouldering the scan field */
-               if (found_x == start_x)
-                       break;
-       }
-
-       if (!best.a.tcm)
-               return -ENOSPC;
-done:
-       assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
-       return 0;
-}
-
-/**
- * Raster scan horizontally left to right from top to bottom to find a place for
- * a 2D area of given size inside a scan field.
- *
- * @param w    width of desired area
- * @param h    height of desired area
- * @param align        desired area alignment
- * @param area pointer to the area that will be set to the best position
- * @param field        area to scan (inclusive)
- *
- * @return 0 on success, non-0 error value on failure.
- */
-static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
-                       struct tcm_area *field, struct tcm_area *area)
-{
-       s32 x, y;
-       s16 start_x, end_x, start_y, end_y, found_x = -1;
-       struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map;
-       struct score best = {{0}, {0}, {0}, 0};
-
-       start_x = field->p0.x;
-       end_x = field->p1.x;
-       start_y = field->p0.y;
-       end_y = field->p1.y;
-
-       /* check scan area co-ordinates */
-       if (field->p1.x < field->p0.x ||
-           field->p1.y < field->p0.y)
-               return -EINVAL;
-
-       /* check if allocation would fit in scan area */
-       if (w > LEN(end_x, start_x) || h > LEN(end_y, start_y))
-               return -ENOSPC;
-
-       start_x = ALIGN(start_x, align);
-
-       /* check if allocation would still fit in scan area */
-       if (w > LEN(end_x, start_x))
-               return -ENOSPC;
-
-       /* adjust end_x and end_y, as allocation would not fit beyond */
-       end_x = end_x - w + 1; /* + 1 to be inclusive */
-       end_y = end_y - h + 1;
-
-       /* scan field top-to-bottom, left-to-right */
-       for (y = start_y; y <= end_y; y++) {
-               for (x = start_x; x <= end_x; x += align) {
-                       if (is_area_free(map, x, y, w, h)) {
-                               found_x = x;
-
-                               /* update best candidate */
-                               if (update_candidate(tcm, x, y, w, h, field,
-                                                       CR_L2R_T2B, &best))
-                                       goto done;
-                               /* change upper x bound */
-                               end_x = x - 1;
-
-                               break;
-                       } else if (map[x][y] && map[x][y]->is2d) {
-                               /* step over 2D areas */
-                               x = ALIGN_DOWN(map[x][y]->p1.x, align);
-                       }
-               }
-
-               /* break if you find a free area shouldering the scan field */
-               if (found_x == start_x)
-                       break;
-       }
-
-       if (!best.a.tcm)
-               return -ENOSPC;
-done:
-       assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
-       return 0;
-}
-
-/**
- * Raster scan horizontally right to left from bottom to top to find a place
- * for a 1D area of given size inside a scan field.
- *
- * @param num_slots    size of desired area
- * @param align                desired area alignment
- * @param area         pointer to the area that will be set to the best
- *                     position
- * @param field                area to scan (inclusive)
- *
- * @return 0 on success, non-0 error value on failure.
- */
-static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
-                               struct tcm_area *field, struct tcm_area *area)
-{
-       s32 found = 0;
-       s16 x, y;
-       struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
-       struct tcm_area *p;
-
-       /* check scan area co-ordinates */
-       if (field->p0.y < field->p1.y)
-               return -EINVAL;
-
-       /**
-        * Currently we only support full width 1D scan field, which makes sense
-        * since 1D slot-ordering spans the full container width.
-        */
-       if (tcm->width != field->p0.x - field->p1.x + 1)
-               return -EINVAL;
-
-       /* check if allocation would fit in scan area */
-       if (num_slots > tcm->width * LEN(field->p0.y, field->p1.y))
-               return -ENOSPC;
-
-       x = field->p0.x;
-       y = field->p0.y;
-
-       /* find num_slots consecutive free slots to the left */
-       while (found < num_slots) {
-               if (y < 0)
-                       return -ENOSPC;
-
-               /* remember bottom-right corner */
-               if (found == 0) {
-                       area->p1.x = x;
-                       area->p1.y = y;
-               }
-
-               /* skip busy regions */
-               p = pvt->map[x][y];
-               if (p) {
-                       /* move to left of 2D areas, top left of 1D */
-                       x = p->p0.x;
-                       if (!p->is2d)
-                               y = p->p0.y;
-
-                       /* start over */
-                       found = 0;
-               } else {
-                       /* count consecutive free slots */
-                       found++;
-                       if (found == num_slots)
-                               break;
-               }
-
-               /* move to the left */
-               if (x == 0)
-                       y--;
-               x = (x ? : tcm->width) - 1;
-
-       }
-
-       /* set top-left corner */
-       area->p0.x = x;
-       area->p0.y = y;
-       return 0;
-}
-
-/**
- * Find a place for a 2D area of given size inside a scan field based on its
- * alignment needs.
- *
- * @param w    width of desired area
- * @param h    height of desired area
- * @param align        desired area alignment
- * @param area pointer to the area that will be set to the best position
- *
- * @return 0 on success, non-0 error value on failure.
- */
-static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
-                                  struct tcm_area *area)
-{
-       s32 ret = 0;
-       struct tcm_area field = {0};
-       u16 boundary_x, boundary_y;
-       struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
-
-       if (align > 1) {
-               /* prefer top-left corner */
-               boundary_x = pvt->div_pt.x - 1;
-               boundary_y = pvt->div_pt.y - 1;
-
-               /* expand width and height if needed */
-               if (w > pvt->div_pt.x)
-                       boundary_x = tcm->width - 1;
-               if (h > pvt->div_pt.y)
-                       boundary_y = tcm->height - 1;
-
-               assign(&field, 0, 0, boundary_x, boundary_y);
-               ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
-
-               /* scan whole container if failed, but do not scan 2x */
-               if (ret != 0 && (boundary_x != tcm->width - 1 ||
-                                boundary_y != tcm->height - 1)) {
-                       /* scan the entire container if nothing found */
-                       assign(&field, 0, 0, tcm->width - 1, tcm->height - 1);
-                       ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
-               }
-       } else if (align == 1) {
-               /* prefer top-right corner */
-               boundary_x = pvt->div_pt.x;
-               boundary_y = pvt->div_pt.y - 1;
-
-               /* expand width and height if needed */
-               if (w > (tcm->width - pvt->div_pt.x))
-                       boundary_x = 0;
-               if (h > pvt->div_pt.y)
-                       boundary_y = tcm->height - 1;
-
-               assign(&field, tcm->width - 1, 0, boundary_x, boundary_y);
-               ret = scan_r2l_t2b(tcm, w, h, align, &field, area);
-
-               /* scan whole container if failed, but do not scan 2x */
-               if (ret != 0 && (boundary_x != 0 ||
-                                boundary_y != tcm->height - 1)) {
-                       /* scan the entire container if nothing found */
-                       assign(&field, tcm->width - 1, 0, 0, tcm->height - 1);
-                       ret = scan_r2l_t2b(tcm, w, h, align, &field,
-                                          area);
-               }
-       }
-
-       return ret;
-}
-
-/* check if an entire area is free */
-static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h)
-{
-       u16 x = 0, y = 0;
-       for (y = y0; y < y0 + h; y++) {
-               for (x = x0; x < x0 + w; x++) {
-                       if (map[x][y])
-                               return false;
-               }
-       }
-       return true;
-}
-
-/* fills an area with a parent tcm_area */
-static void fill_area(struct tcm *tcm, struct tcm_area *area,
-                       struct tcm_area *parent)
-{
-       s32 x, y;
-       struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
-       struct tcm_area a, a_;
-
-       /* set area's tcm; otherwise, enumerator considers it invalid */
-       area->tcm = tcm;
-
-       tcm_for_each_slice(a, *area, a_) {
-               for (x = a.p0.x; x <= a.p1.x; ++x)
-                       for (y = a.p0.y; y <= a.p1.y; ++y)
-                               pvt->map[x][y] = parent;
-
-       }
-}
-
-/**
- * Compares a candidate area to the current best area, and if it is a better
- * fit, it updates the best to this one.
- *
- * @param x0, y0, w, h         top, left, width, height of candidate area
- * @param field                        scan field
- * @param criteria             scan criteria
- * @param best                 best candidate and its scores
- *
- * @return 1 (true) if the candidate area is known to be the final best, so no
- * more searching should be performed
- */
-static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
-                           struct tcm_area *field, s32 criteria,
-                           struct score *best)
-{
-       struct score me;        /* score for area */
-
-       /*
-        * NOTE: For horizontal bias we always give the first found, because our
-        * scan is horizontal-raster-based and the first candidate will always
-        * have the horizontal bias.
-        */
-       bool first = criteria & CR_BIAS_HORIZONTAL;
-
-       assign(&me.a, x0, y0, x0 + w - 1, y0 + h - 1);
-
-       /* calculate score for current candidate */
-       if (!first) {
-               get_neighbor_stats(tcm, &me.a, &me.n);
-               me.neighs = me.n.edge + me.n.busy;
-               get_nearness_factor(field, &me.a, &me.f);
-       }
-
-       /* the 1st candidate is always the best */
-       if (!best->a.tcm)
-               goto better;
-
-       BUG_ON(first);
-
-       /* diagonal balance check */
-       if ((criteria & CR_DIAGONAL_BALANCE) &&
-               best->neighs <= me.neighs &&
-               (best->neighs < me.neighs ||
-                /* this implies that neighs and occupied match */
-                best->n.busy < me.n.busy ||
-                (best->n.busy == me.n.busy &&
-                 /* check the nearness factor */
-                 best->f.x + best->f.y > me.f.x + me.f.y)))
-               goto better;
-
-       /* not better, keep going */
-       return 0;
-
-better:
-       /* save current area as best */
-       memcpy(best, &me, sizeof(me));
-       best->a.tcm = tcm;
-       return first;
-}
-
-/**
- * Calculate the nearness factor of an area in a search field.  The nearness
- * factor is smaller if the area is closer to the search origin.
- */
-static void get_nearness_factor(struct tcm_area *field, struct tcm_area *area,
-                               struct nearness_factor *nf)
-{
-       /**
-        * Using signed math as field coordinates may be reversed if
-        * search direction is right-to-left or bottom-to-top.
-        */
-       nf->x = (s32)(area->p0.x - field->p0.x) * 1000 /
-               (field->p1.x - field->p0.x);
-       nf->y = (s32)(area->p0.y - field->p0.y) * 1000 /
-               (field->p1.y - field->p0.y);
-}
-
-/* get neighbor statistics */
-static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
-                        struct neighbor_stats *stat)
-{
-       s16 x = 0, y = 0;
-       struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
-
-       /* Clearing any exisiting values */
-       memset(stat, 0, sizeof(*stat));
-
-       /* process top & bottom edges */
-       for (x = area->p0.x; x <= area->p1.x; x++) {
-               if (area->p0.y == 0)
-                       stat->edge++;
-               else if (pvt->map[x][area->p0.y - 1])
-                       stat->busy++;
-
-               if (area->p1.y == tcm->height - 1)
-                       stat->edge++;
-               else if (pvt->map[x][area->p1.y + 1])
-                       stat->busy++;
-       }
-
-       /* process left & right edges */
-       for (y = area->p0.y; y <= area->p1.y; ++y) {
-               if (area->p0.x == 0)
-                       stat->edge++;
-               else if (pvt->map[area->p0.x - 1][y])
-                       stat->busy++;
-
-               if (area->p1.x == tcm->width - 1)
-                       stat->edge++;
-               else if (pvt->map[area->p1.x + 1][y])
-                       stat->busy++;
-       }
-}
index d273e3ee0b4c7d64737b1e52fbbcb068a0be089a..ef7df7d6fc84c7faa2b92b94e93be1853a8c4c55 100644 (file)
@@ -59,18 +59,19 @@ struct tcm {
        u16 width, height;      /* container dimensions */
        int lut_id;             /* Lookup table identifier */
 
-       /* 'pvt' structure shall contain any tcm details (attr) along with
-       linked list of allocated areas and mutex for mutually exclusive access
-       to the list.  It may also contain copies of width and height to notice
-       any changes to the publicly available width and height fields. */
-       void *pvt;
+       unsigned int y_offset;  /* offset to use for y coordinates */
+
+       spinlock_t lock;
+       unsigned long *bitmap;
+       size_t map_size;
 
        /* function table */
-       s32 (*reserve_2d)(struct tcm *tcm, u16 height, u16 width, u8 align,
+       s32 (*reserve_2d)(struct tcm *tcm, u16 height, u16 width, u16 align,
+                         int16_t offset, uint16_t slot_bytes,
                          struct tcm_area *area);
        s32 (*reserve_1d)(struct tcm *tcm, u32 slots, struct tcm_area *area);
-       s32 (*free)      (struct tcm *tcm, struct tcm_area *area);
-       void (*deinit)   (struct tcm *tcm);
+       s32 (*free)(struct tcm *tcm, struct tcm_area *area);
+       void (*deinit)(struct tcm *tcm);
 };
 
 /*=============================================================================
@@ -89,7 +90,7 @@ struct tcm {
  *
  */
 
-struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr);
+struct tcm *sita_init(u16 width, u16 height);
 
 
 /**
@@ -118,6 +119,9 @@ static inline void tcm_deinit(struct tcm *tcm)
  *                     all values may be supported by the container manager,
  *                     but it must support 0 (1), 32 and 64.
  *                     0 value is equivalent to 1.
+ * @param offset       Offset requirement, in bytes.  This is the offset
+ *                     from a 4KiB aligned virtual address.
+ * @param slot_bytes   Width of slot in bytes
  * @param area         Pointer to where the reserved area should be stored.
  *
  * @return 0 on success.  Non-0 error code on failure.  Also,
@@ -127,7 +131,8 @@ static inline void tcm_deinit(struct tcm *tcm)
  *         allocation.
  */
 static inline s32 tcm_reserve_2d(struct tcm *tcm, u16 width, u16 height,
-                                u16 align, struct tcm_area *area)
+                               u16 align, int16_t offset, uint16_t slot_bytes,
+                               struct tcm_area *area)
 {
        /* perform rudimentary error checking */
        s32 res = tcm  == NULL ? -ENODEV :
@@ -138,7 +143,8 @@ static inline s32 tcm_reserve_2d(struct tcm *tcm, u16 width, u16 height,
 
        if (!res) {
                area->is2d = true;
-               res = tcm->reserve_2d(tcm, height, width, align, area);
+               res = tcm->reserve_2d(tcm, height, width, align, offset,
+                                       slot_bytes, area);
                area->tcm = res ? NULL : tcm;
        }
 
similarity index 70%
rename from drivers/staging/omap-thermal/Kconfig
rename to drivers/staging/ti-soc-thermal/Kconfig
index 30cbc3bc8dfa3d434815b4f633b31b8871a88c6e..e81375fb21552413f3df4689e4c950aab820eb8e 100644 (file)
@@ -1,7 +1,7 @@
-config OMAP_BANDGAP
-       tristate "Texas Instruments OMAP4+ temperature sensor driver"
+config TI_SOC_THERMAL
+       tristate "Texas Instruments SoCs temperature sensor driver"
        depends on THERMAL
-       depends on ARCH_OMAP4 || SOC_OMAP5
+       depends on ARCH_HAS_BANDGAP
        help
          If you say yes here you get support for the Texas Instruments
          OMAP4460+ on die bandgap temperature sensor support. The register
@@ -10,18 +10,20 @@ config OMAP_BANDGAP
          This includes alert interrupts generation and also the TSHUT
          support.
 
-config OMAP_THERMAL
-       bool "Texas Instruments OMAP4+ thermal framework support"
-       depends on OMAP_BANDGAP
+config TI_THERMAL
+       bool "Texas Instruments SoCs thermal framework support"
+       depends on TI_SOC_THERMAL
        depends on CPU_THERMAL
        help
          If you say yes here you want to get support for generic thermal
-         framework for the Texas Instruments OMAP4460+ on die bandgap
-         temperature sensor.
+         framework for the Texas Instruments on die bandgap temperature sensor.
+
+         This includes trip points definitions, extrapolation rules and
+         CPU cooling device bindings.
 
 config OMAP4_THERMAL
        bool "Texas Instruments OMAP4 thermal support"
-       depends on OMAP_BANDGAP
+       depends on TI_SOC_THERMAL
        depends on ARCH_OMAP4
        help
          If you say yes here you get thermal support for the Texas Instruments
@@ -35,7 +37,7 @@ config OMAP4_THERMAL
 
 config OMAP5_THERMAL
        bool "Texas Instruments OMAP5 thermal support"
-       depends on OMAP_BANDGAP
+       depends on TI_SOC_THERMAL
        depends on SOC_OMAP5
        help
          If you say yes here you get thermal support for the Texas Instruments
diff --git a/drivers/staging/ti-soc-thermal/Makefile b/drivers/staging/ti-soc-thermal/Makefile
new file mode 100644 (file)
index 0000000..0ca034f
--- /dev/null
@@ -0,0 +1,5 @@
+obj-$(CONFIG_TI_SOC_THERMAL)           += ti-soc-thermal.o
+ti-soc-thermal-y                       := ti-bandgap.o
+ti-soc-thermal-$(CONFIG_TI_THERMAL)    += ti-thermal-common.o
+ti-soc-thermal-$(CONFIG_OMAP4_THERMAL) += omap4-thermal-data.o
+ti-soc-thermal-$(CONFIG_OMAP5_THERMAL) += omap5-thermal-data.o
similarity index 72%
rename from drivers/staging/omap-thermal/TODO
rename to drivers/staging/ti-soc-thermal/TODO
index 9e23cc4d551bfc7949824be9dc1d673a72ffb315..9b4c84170a7c58802033aa0412d79fa136338b32 100644 (file)
@@ -1,16 +1,13 @@
 List of TODOs (by Eduardo Valentin)
 
-on omap-bandgap.c:
-- Rework locking
-- Improve driver code by adding usage of regmap-mmio
+on ti-bandgap.c:
 - Test every exposed API to userland
 - Add support to hwmon
-- Review and revisit all API exposed
 - Revisit PM support
 - Revisit data structures and simplify them
 - Once SCM-core api settles, update this driver accordingly
 
-on omap-thermal-common.c/omap-thermal.h:
+on ti-thermal-common.c/ti-thermal.h:
 - Revisit extrapolation constants for O4/O5
 - Revisit need for locking
 - Revisit trips and its definitions
@@ -20,7 +17,6 @@ on omap5-thermal.c
 - Add support for GPU cooling
 
 generally:
-- write Kconfig dependencies so that omap variants are covered
 - make checkpatch.pl and sparse happy
 - make sure this code works on OMAP4430, OMAP4460 and OMAP5430
 - update documentation
similarity index 85%
rename from drivers/staging/omap-thermal/omap4-thermal.c
rename to drivers/staging/ti-soc-thermal/omap4-thermal-data.c
index 04c02b6c007719f57aa7ec58423ab07b011d189a..49d032467739106b3ce3281195c553e9d9c2c7ed 100644 (file)
@@ -16,8 +16,9 @@
  *
  */
 
-#include "omap-thermal.h"
-#include "omap-bandgap.h"
+#include "ti-thermal.h"
+#include "ti-bandgap.h"
+#include "omap4xxx-bandgap.h"
 
 /*
  * OMAP4430 has one instance of thermal sensor for MPU
@@ -44,8 +45,6 @@ static struct temp_sensor_data omap4430_mpu_temp_sensor_data = {
        .max_temp = OMAP4430_MAX_TEMP,
        .min_temp = OMAP4430_MIN_TEMP,
        .hyst_val = OMAP4430_HYST_VAL,
-       .adc_start_val = OMAP4430_ADC_START_VALUE,
-       .adc_end_val = OMAP4430_ADC_END_VALUE,
 };
 
 /*
@@ -67,14 +66,17 @@ omap4430_adc_to_temp[OMAP4430_ADC_END_VALUE - OMAP4430_ADC_START_VALUE + 1] = {
 };
 
 /* OMAP4430 data */
-const struct omap_bandgap_data omap4430_data = {
-       .features = OMAP_BANDGAP_FEATURE_MODE_CONFIG |
-                       OMAP_BANDGAP_FEATURE_POWER_SWITCH,
+const struct ti_bandgap_data omap4430_data = {
+       .features = TI_BANDGAP_FEATURE_MODE_CONFIG |
+                       TI_BANDGAP_FEATURE_CLK_CTRL |
+                       TI_BANDGAP_FEATURE_POWER_SWITCH,
        .fclock_name = "bandgap_fclk",
        .div_ck_name = "bandgap_fclk",
        .conv_table = omap4430_adc_to_temp,
-       .expose_sensor = omap_thermal_expose_sensor,
-       .remove_sensor = omap_thermal_remove_sensor,
+       .adc_start_val = OMAP4430_ADC_START_VALUE,
+       .adc_end_val = OMAP4430_ADC_END_VALUE,
+       .expose_sensor = ti_thermal_expose_sensor,
+       .remove_sensor = ti_thermal_remove_sensor,
        .sensors = {
                {
                .registers = &omap4430_mpu_temp_sensor_registers,
@@ -84,8 +86,8 @@ const struct omap_bandgap_data omap4430_data = {
                .constant = 20000,
                .slope_pcb = 0,
                .constant_pcb = 20000,
-               .register_cooling = omap_thermal_register_cpu_cooling,
-               .unregister_cooling = omap_thermal_unregister_cpu_cooling,
+               .register_cooling = ti_thermal_register_cpu_cooling,
+               .unregister_cooling = ti_thermal_unregister_cpu_cooling,
                },
        },
        .sensor_count = 1,
@@ -140,8 +142,6 @@ static struct temp_sensor_data omap4460_mpu_temp_sensor_data = {
        .max_temp = OMAP4460_MAX_TEMP,
        .min_temp = OMAP4460_MIN_TEMP,
        .hyst_val = OMAP4460_HYST_VAL,
-       .adc_start_val = OMAP4460_ADC_START_VALUE,
-       .adc_end_val = OMAP4460_ADC_END_VALUE,
        .update_int1 = 1000,
        .update_int2 = 2000,
 };
@@ -201,18 +201,21 @@ omap4460_adc_to_temp[OMAP4460_ADC_END_VALUE - OMAP4460_ADC_START_VALUE + 1] = {
 };
 
 /* OMAP4460 data */
-const struct omap_bandgap_data omap4460_data = {
-       .features = OMAP_BANDGAP_FEATURE_TSHUT |
-                       OMAP_BANDGAP_FEATURE_TSHUT_CONFIG |
-                       OMAP_BANDGAP_FEATURE_TALERT |
-                       OMAP_BANDGAP_FEATURE_MODE_CONFIG |
-                       OMAP_BANDGAP_FEATURE_POWER_SWITCH |
-                       OMAP_BANDGAP_FEATURE_COUNTER,
+const struct ti_bandgap_data omap4460_data = {
+       .features = TI_BANDGAP_FEATURE_TSHUT |
+                       TI_BANDGAP_FEATURE_TSHUT_CONFIG |
+                       TI_BANDGAP_FEATURE_TALERT |
+                       TI_BANDGAP_FEATURE_MODE_CONFIG |
+                       TI_BANDGAP_FEATURE_POWER_SWITCH |
+                       TI_BANDGAP_FEATURE_CLK_CTRL |
+                       TI_BANDGAP_FEATURE_COUNTER,
        .fclock_name = "bandgap_ts_fclk",
        .div_ck_name = "div_ts_ck",
        .conv_table = omap4460_adc_to_temp,
-       .expose_sensor = omap_thermal_expose_sensor,
-       .remove_sensor = omap_thermal_remove_sensor,
+       .adc_start_val = OMAP4460_ADC_START_VALUE,
+       .adc_end_val = OMAP4460_ADC_END_VALUE,
+       .expose_sensor = ti_thermal_expose_sensor,
+       .remove_sensor = ti_thermal_remove_sensor,
        .sensors = {
                {
                .registers = &omap4460_mpu_temp_sensor_registers,
@@ -222,26 +225,29 @@ const struct omap_bandgap_data omap4460_data = {
                .constant = OMAP_GRADIENT_CONST_4460,
                .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4460,
                .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4460,
-               .register_cooling = omap_thermal_register_cpu_cooling,
-               .unregister_cooling = omap_thermal_unregister_cpu_cooling,
+               .register_cooling = ti_thermal_register_cpu_cooling,
+               .unregister_cooling = ti_thermal_unregister_cpu_cooling,
                },
        },
        .sensor_count = 1,
 };
 
 /* OMAP4470 data */
-const struct omap_bandgap_data omap4470_data = {
-       .features = OMAP_BANDGAP_FEATURE_TSHUT |
-                       OMAP_BANDGAP_FEATURE_TSHUT_CONFIG |
-                       OMAP_BANDGAP_FEATURE_TALERT |
-                       OMAP_BANDGAP_FEATURE_MODE_CONFIG |
-                       OMAP_BANDGAP_FEATURE_POWER_SWITCH |
-                       OMAP_BANDGAP_FEATURE_COUNTER,
+const struct ti_bandgap_data omap4470_data = {
+       .features = TI_BANDGAP_FEATURE_TSHUT |
+                       TI_BANDGAP_FEATURE_TSHUT_CONFIG |
+                       TI_BANDGAP_FEATURE_TALERT |
+                       TI_BANDGAP_FEATURE_MODE_CONFIG |
+                       TI_BANDGAP_FEATURE_POWER_SWITCH |
+                       TI_BANDGAP_FEATURE_CLK_CTRL |
+                       TI_BANDGAP_FEATURE_COUNTER,
        .fclock_name = "bandgap_ts_fclk",
        .div_ck_name = "div_ts_ck",
        .conv_table = omap4460_adc_to_temp,
-       .expose_sensor = omap_thermal_expose_sensor,
-       .remove_sensor = omap_thermal_remove_sensor,
+       .adc_start_val = OMAP4460_ADC_START_VALUE,
+       .adc_end_val = OMAP4460_ADC_END_VALUE,
+       .expose_sensor = ti_thermal_expose_sensor,
+       .remove_sensor = ti_thermal_remove_sensor,
        .sensors = {
                {
                .registers = &omap4460_mpu_temp_sensor_registers,
@@ -251,8 +257,8 @@ const struct omap_bandgap_data omap4470_data = {
                .constant = OMAP_GRADIENT_CONST_4470,
                .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4470,
                .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4470,
-               .register_cooling = omap_thermal_register_cpu_cooling,
-               .unregister_cooling = omap_thermal_unregister_cpu_cooling,
+               .register_cooling = ti_thermal_register_cpu_cooling,
+               .unregister_cooling = ti_thermal_unregister_cpu_cooling,
                },
        },
        .sensor_count = 1,
diff --git a/drivers/staging/ti-soc-thermal/omap4xxx-bandgap.h b/drivers/staging/ti-soc-thermal/omap4xxx-bandgap.h
new file mode 100644 (file)
index 0000000..6f2de3a
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * OMAP4xxx bandgap registers, bitfields and temperature definitions
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ * Contact:
+ *   Eduardo Valentin <eduardo.valentin@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifndef __OMAP4XXX_BANDGAP_H
+#define __OMAP4XXX_BANDGAP_H
+
+/**
+ * *** OMAP4430 ***
+ *
+ * Below, in sequence, are the Register definitions,
+ * the bitfields and the temperature definitions for OMAP4430.
+ */
+
+/**
+ * OMAP4430 register definitions
+ *
+ * Registers are defined as offsets. The offsets are
+ * relative to FUSE_OPP_BGAP on 4430.
+ */
+
+/* OMAP4430.FUSE_OPP_BGAP */
+#define OMAP4430_FUSE_OPP_BGAP                         0x0
+
+/* OMAP4430.TEMP_SENSOR  */
+#define OMAP4430_TEMP_SENSOR_CTRL_OFFSET               0xCC
+
+/**
+ * Register and bit definitions for OMAP4430
+ *
+ * All the macros bellow define the required bits for
+ * controlling temperature on OMAP4430. Bit defines are
+ * grouped by register.
+ */
+
+/* OMAP4430.TEMP_SENSOR bits */
+#define OMAP4430_BGAP_TEMPSOFF_MASK                    BIT(12)
+#define OMAP4430_BGAP_TSHUT_MASK                       BIT(11)
+#define OMAP4430_SINGLE_MODE_MASK                      BIT(10)
+#define OMAP4430_BGAP_TEMP_SENSOR_SOC_MASK             BIT(9)
+#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK            BIT(8)
+#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK           (0xff << 0)
+
+/**
+ * Temperature limits and thresholds for OMAP4430
+ *
+ * All the macros bellow are definitions for handling the
+ * ADC conversions and representation of temperature limits
+ * and thresholds for OMAP4430.
+ */
+
+/* ADC conversion table limits */
+#define OMAP4430_ADC_START_VALUE                       0
+#define OMAP4430_ADC_END_VALUE                         127
+/* bandgap clock limits (no control on 4430) */
+#define OMAP4430_MAX_FREQ                              32768
+#define OMAP4430_MIN_FREQ                              32768
+/* sensor limits */
+#define OMAP4430_MIN_TEMP                              -40000
+#define OMAP4430_MAX_TEMP                              125000
+#define OMAP4430_HYST_VAL                              5000
+
+/**
+ * *** OMAP4460 *** Applicable for OMAP4470
+ *
+ * Below, in sequence, are the Register definitions,
+ * the bitfields and the temperature definitions for OMAP4460.
+ */
+
+/**
+ * OMAP4460 register definitions
+ *
+ * Registers are defined as offsets. The offsets are
+ * relative to FUSE_OPP_BGAP on 4460.
+ */
+
+/* OMAP4460.FUSE_OPP_BGAP */
+#define OMAP4460_FUSE_OPP_BGAP                         0x0
+
+/* OMAP4460.TEMP_SENSOR */
+#define OMAP4460_TEMP_SENSOR_CTRL_OFFSET               0xCC
+
+/* OMAP4460.BANDGAP_CTRL */
+#define OMAP4460_BGAP_CTRL_OFFSET                      0x118
+
+/* OMAP4460.BANDGAP_COUNTER */
+#define OMAP4460_BGAP_COUNTER_OFFSET                   0x11C
+
+/* OMAP4460.BANDGAP_THRESHOLD */
+#define OMAP4460_BGAP_THRESHOLD_OFFSET                 0x120
+
+/* OMAP4460.TSHUT_THRESHOLD */
+#define OMAP4460_BGAP_TSHUT_OFFSET                     0x124
+
+/* OMAP4460.BANDGAP_STATUS */
+#define OMAP4460_BGAP_STATUS_OFFSET                    0x128
+
+/**
+ * Register bitfields for OMAP4460
+ *
+ * All the macros bellow define the required bits for
+ * controlling temperature on OMAP4460. Bit defines are
+ * grouped by register.
+ */
+/* OMAP4460.TEMP_SENSOR bits */
+#define OMAP4460_BGAP_TEMPSOFF_MASK                    BIT(13)
+#define OMAP4460_BGAP_TEMP_SENSOR_SOC_MASK             BIT(11)
+#define OMAP4460_BGAP_TEMP_SENSOR_EOCZ_MASK            BIT(10)
+#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK           (0x3ff << 0)
+
+/* OMAP4460.BANDGAP_CTRL bits */
+#define OMAP4460_SINGLE_MODE_MASK                      BIT(31)
+#define OMAP4460_MASK_HOT_MASK                         BIT(1)
+#define OMAP4460_MASK_COLD_MASK                                BIT(0)
+
+/* OMAP4460.BANDGAP_COUNTER bits */
+#define OMAP4460_COUNTER_MASK                          (0xffffff << 0)
+
+/* OMAP4460.BANDGAP_THRESHOLD bits */
+#define OMAP4460_T_HOT_MASK                            (0x3ff << 16)
+#define OMAP4460_T_COLD_MASK                           (0x3ff << 0)
+
+/* OMAP4460.TSHUT_THRESHOLD bits */
+#define OMAP4460_TSHUT_HOT_MASK                                (0x3ff << 16)
+#define OMAP4460_TSHUT_COLD_MASK                       (0x3ff << 0)
+
+/* OMAP4460.BANDGAP_STATUS bits */
+#define OMAP4460_CLEAN_STOP_MASK                       BIT(3)
+#define OMAP4460_BGAP_ALERT_MASK                       BIT(2)
+#define OMAP4460_HOT_FLAG_MASK                         BIT(1)
+#define OMAP4460_COLD_FLAG_MASK                                BIT(0)
+
+/**
+ * Temperature limits and thresholds for OMAP4460
+ *
+ * All the macros bellow are definitions for handling the
+ * ADC conversions and representation of temperature limits
+ * and thresholds for OMAP4460.
+ */
+
+/* ADC conversion table limits */
+#define OMAP4460_ADC_START_VALUE                       530
+#define OMAP4460_ADC_END_VALUE                         932
+/* bandgap clock limits */
+#define OMAP4460_MAX_FREQ                              1500000
+#define OMAP4460_MIN_FREQ                              1000000
+/* sensor limits */
+#define OMAP4460_MIN_TEMP                              -40000
+#define OMAP4460_MAX_TEMP                              123000
+#define OMAP4460_HYST_VAL                              5000
+/* interrupts thresholds */
+#define OMAP4460_TSHUT_HOT                             900     /* 122 deg C */
+#define OMAP4460_TSHUT_COLD                            895     /* 100 deg C */
+#define OMAP4460_T_HOT                                 800     /* 73 deg C */
+#define OMAP4460_T_COLD                                        795     /* 71 deg C */
+
+#endif /* __OMAP4XXX_BANDGAP_H */
diff --git a/drivers/staging/ti-soc-thermal/omap5-thermal-data.c b/drivers/staging/ti-soc-thermal/omap5-thermal-data.c
new file mode 100644 (file)
index 0000000..0afe9c8
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * OMAP5 thermal driver.
+ *
+ * Copyright (C) 2011-2012 Texas Instruments Inc.
+ * Contact:
+ *     Eduardo Valentin <eduardo.valentin@ti.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "ti-thermal.h"
+#include "ti-bandgap.h"
+#include "omap5xxx-bandgap.h"
+
+/*
+ * OMAP5430 has three instances of thermal sensor for MPU, GPU & CORE,
+ * need to describe the individual registers and bit fields.
+ */
+
+/*
+ * OMAP5430 MPU thermal sensor register offset and bit-fields
+ */
+static struct temp_sensor_registers
+omap5430_mpu_temp_sensor_registers = {
+       .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_MPU_OFFSET,
+       .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK,
+       .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK,
+       .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK,
+
+       .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET,
+       .mask_hot_mask = OMAP5430_MASK_HOT_MPU_MASK,
+       .mask_cold_mask = OMAP5430_MASK_COLD_MPU_MASK,
+       .mask_sidlemode_mask = OMAP5430_MASK_SIDLEMODE_MASK,
+       .mask_freeze_mask = OMAP5430_MASK_FREEZE_MPU_MASK,
+       .mask_clear_mask = OMAP5430_MASK_CLEAR_MPU_MASK,
+       .mask_clear_accum_mask = OMAP5430_MASK_CLEAR_ACCUM_MPU_MASK,
+
+
+       .bgap_counter = OMAP5430_BGAP_CTRL_OFFSET,
+       .counter_mask = OMAP5430_COUNTER_MASK,
+
+       .bgap_threshold = OMAP5430_BGAP_THRESHOLD_MPU_OFFSET,
+       .threshold_thot_mask = OMAP5430_T_HOT_MASK,
+       .threshold_tcold_mask = OMAP5430_T_COLD_MASK,
+
+       .tshut_threshold = OMAP5430_BGAP_TSHUT_MPU_OFFSET,
+       .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK,
+       .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK,
+
+       .bgap_status = OMAP5430_BGAP_STATUS_OFFSET,
+       .status_clean_stop_mask = 0x0,
+       .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK,
+       .status_hot_mask = OMAP5430_HOT_MPU_FLAG_MASK,
+       .status_cold_mask = OMAP5430_COLD_MPU_FLAG_MASK,
+
+       .bgap_cumul_dtemp = OMAP5430_BGAP_CUMUL_DTEMP_MPU_OFFSET,
+       .ctrl_dtemp_0 = OMAP5430_BGAP_DTEMP_MPU_0_OFFSET,
+       .ctrl_dtemp_1 = OMAP5430_BGAP_DTEMP_MPU_1_OFFSET,
+       .ctrl_dtemp_2 = OMAP5430_BGAP_DTEMP_MPU_2_OFFSET,
+       .ctrl_dtemp_3 = OMAP5430_BGAP_DTEMP_MPU_3_OFFSET,
+       .ctrl_dtemp_4 = OMAP5430_BGAP_DTEMP_MPU_4_OFFSET,
+       .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_MPU,
+};
+
+/*
+ * OMAP5430 GPU thermal sensor register offset and bit-fields
+ */
+static struct temp_sensor_registers
+omap5430_gpu_temp_sensor_registers = {
+       .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_GPU_OFFSET,
+       .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK,
+       .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK,
+       .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK,
+
+       .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET,
+       .mask_hot_mask = OMAP5430_MASK_HOT_GPU_MASK,
+       .mask_cold_mask = OMAP5430_MASK_COLD_GPU_MASK,
+       .mask_sidlemode_mask = OMAP5430_MASK_SIDLEMODE_MASK,
+       .mask_freeze_mask = OMAP5430_MASK_FREEZE_GPU_MASK,
+       .mask_clear_mask = OMAP5430_MASK_CLEAR_GPU_MASK,
+       .mask_clear_accum_mask = OMAP5430_MASK_CLEAR_ACCUM_GPU_MASK,
+
+       .bgap_counter = OMAP5430_BGAP_CTRL_OFFSET,
+       .counter_mask = OMAP5430_COUNTER_MASK,
+
+       .bgap_threshold = OMAP5430_BGAP_THRESHOLD_GPU_OFFSET,
+       .threshold_thot_mask = OMAP5430_T_HOT_MASK,
+       .threshold_tcold_mask = OMAP5430_T_COLD_MASK,
+
+       .tshut_threshold = OMAP5430_BGAP_TSHUT_GPU_OFFSET,
+       .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK,
+       .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK,
+
+       .bgap_status = OMAP5430_BGAP_STATUS_OFFSET,
+       .status_clean_stop_mask = 0x0,
+       .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK,
+       .status_hot_mask = OMAP5430_HOT_GPU_FLAG_MASK,
+       .status_cold_mask = OMAP5430_COLD_GPU_FLAG_MASK,
+
+       .bgap_cumul_dtemp = OMAP5430_BGAP_CUMUL_DTEMP_GPU_OFFSET,
+       .ctrl_dtemp_0 = OMAP5430_BGAP_DTEMP_GPU_0_OFFSET,
+       .ctrl_dtemp_1 = OMAP5430_BGAP_DTEMP_GPU_1_OFFSET,
+       .ctrl_dtemp_2 = OMAP5430_BGAP_DTEMP_GPU_2_OFFSET,
+       .ctrl_dtemp_3 = OMAP5430_BGAP_DTEMP_GPU_3_OFFSET,
+       .ctrl_dtemp_4 = OMAP5430_BGAP_DTEMP_GPU_4_OFFSET,
+
+       .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_GPU,
+};
+
+/*
+ * OMAP5430 CORE thermal sensor register offset and bit-fields
+ */
+static struct temp_sensor_registers
+omap5430_core_temp_sensor_registers = {
+       .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_CORE_OFFSET,
+       .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK,
+       .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK,
+       .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK,
+
+       .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET,
+       .mask_hot_mask = OMAP5430_MASK_HOT_CORE_MASK,
+       .mask_cold_mask = OMAP5430_MASK_COLD_CORE_MASK,
+       .mask_sidlemode_mask = OMAP5430_MASK_SIDLEMODE_MASK,
+       .mask_freeze_mask = OMAP5430_MASK_FREEZE_CORE_MASK,
+       .mask_clear_mask = OMAP5430_MASK_CLEAR_CORE_MASK,
+       .mask_clear_accum_mask = OMAP5430_MASK_CLEAR_ACCUM_CORE_MASK,
+
+       .bgap_counter = OMAP5430_BGAP_CTRL_OFFSET,
+       .counter_mask = OMAP5430_COUNTER_MASK,
+
+       .bgap_threshold = OMAP5430_BGAP_THRESHOLD_CORE_OFFSET,
+       .threshold_thot_mask = OMAP5430_T_HOT_MASK,
+       .threshold_tcold_mask = OMAP5430_T_COLD_MASK,
+
+       .tshut_threshold = OMAP5430_BGAP_TSHUT_CORE_OFFSET,
+       .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK,
+       .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK,
+
+       .bgap_status = OMAP5430_BGAP_STATUS_OFFSET,
+       .status_clean_stop_mask = 0x0,
+       .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK,
+       .status_hot_mask = OMAP5430_HOT_CORE_FLAG_MASK,
+       .status_cold_mask = OMAP5430_COLD_CORE_FLAG_MASK,
+
+       .bgap_cumul_dtemp = OMAP5430_BGAP_CUMUL_DTEMP_CORE_OFFSET,
+       .ctrl_dtemp_0 = OMAP5430_BGAP_DTEMP_CORE_0_OFFSET,
+       .ctrl_dtemp_1 = OMAP5430_BGAP_DTEMP_CORE_1_OFFSET,
+       .ctrl_dtemp_2 = OMAP5430_BGAP_DTEMP_CORE_2_OFFSET,
+       .ctrl_dtemp_3 = OMAP5430_BGAP_DTEMP_CORE_3_OFFSET,
+       .ctrl_dtemp_4 = OMAP5430_BGAP_DTEMP_CORE_4_OFFSET,
+
+       .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_CORE,
+};
+
+/* Thresholds and limits for OMAP5430 MPU temperature sensor */
+static struct temp_sensor_data omap5430_mpu_temp_sensor_data = {
+       .tshut_hot = OMAP5430_MPU_TSHUT_HOT,
+       .tshut_cold = OMAP5430_MPU_TSHUT_COLD,
+       .t_hot = OMAP5430_MPU_T_HOT,
+       .t_cold = OMAP5430_MPU_T_COLD,
+       .min_freq = OMAP5430_MPU_MIN_FREQ,
+       .max_freq = OMAP5430_MPU_MAX_FREQ,
+       .max_temp = OMAP5430_MPU_MAX_TEMP,
+       .min_temp = OMAP5430_MPU_MIN_TEMP,
+       .hyst_val = OMAP5430_MPU_HYST_VAL,
+       .update_int1 = 1000,
+       .update_int2 = 2000,
+};
+
+/* Thresholds and limits for OMAP5430 GPU temperature sensor */
+static struct temp_sensor_data omap5430_gpu_temp_sensor_data = {
+       .tshut_hot = OMAP5430_GPU_TSHUT_HOT,
+       .tshut_cold = OMAP5430_GPU_TSHUT_COLD,
+       .t_hot = OMAP5430_GPU_T_HOT,
+       .t_cold = OMAP5430_GPU_T_COLD,
+       .min_freq = OMAP5430_GPU_MIN_FREQ,
+       .max_freq = OMAP5430_GPU_MAX_FREQ,
+       .max_temp = OMAP5430_GPU_MAX_TEMP,
+       .min_temp = OMAP5430_GPU_MIN_TEMP,
+       .hyst_val = OMAP5430_GPU_HYST_VAL,
+       .update_int1 = 1000,
+       .update_int2 = 2000,
+};
+
+/* Thresholds and limits for OMAP5430 CORE temperature sensor */
+static struct temp_sensor_data omap5430_core_temp_sensor_data = {
+       .tshut_hot = OMAP5430_CORE_TSHUT_HOT,
+       .tshut_cold = OMAP5430_CORE_TSHUT_COLD,
+       .t_hot = OMAP5430_CORE_T_HOT,
+       .t_cold = OMAP5430_CORE_T_COLD,
+       .min_freq = OMAP5430_CORE_MIN_FREQ,
+       .max_freq = OMAP5430_CORE_MAX_FREQ,
+       .max_temp = OMAP5430_CORE_MAX_TEMP,
+       .min_temp = OMAP5430_CORE_MIN_TEMP,
+       .hyst_val = OMAP5430_CORE_HYST_VAL,
+       .update_int1 = 1000,
+       .update_int2 = 2000,
+};
+
+/*
+ * OMAP54xx ES2.0 : Temperature values in milli degree celsius
+ * ADC code values from 540 to 945
+ */
+static int
+omap5430_adc_to_temp[
+       OMAP5430_ADC_END_VALUE - OMAP5430_ADC_START_VALUE + 1] = {
+       /* Index 540 - 549 */
+       -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
+       -37800,
+       /* Index 550 - 559 */
+       -37400, -37000, -36600, -36200, -35800, -35300, -34700, -34200, -33800,
+       -33400,
+       /* Index 560 - 569 */
+       -33000, -32600, -32200, -31800, -31400, -31000, -30600, -30200, -29800,
+       -29400,
+       /* Index 570 - 579 */
+       -29000, -28600, -28200, -27700, -27100, -26600, -26200, -25800, -25400,
+       -25000,
+       /* Index 580 - 589 */
+       -24600, -24200, -23800, -23400, -23000, -22600, -22200, -21600, -21400,
+       -21000,
+       /* Index 590 - 599 */
+       -20500, -19900, -19400, -19000, -18600, -18200, -17800, -17400, -17000,
+       -16600,
+       /* Index 600 - 609 */
+       -16200, -15800, -15400, -15000, -14600, -14200, -13800, -13400, -13000,
+       -12500,
+       /* Index 610 - 619 */
+       -11900, -11400, -11000, -10600, -10200, -9800, -9400, -9000, -8600,
+       -8200,
+       /* Index 620 - 629 */
+       -7800, -7400, -7000, -6600, -6200, -5800, -5400, -5000, -4500, -3900,
+       /* Index 630 - 639 */
+       -3400, -3000, -2600, -2200, -1800, -1400, -1000, -600, -200, 200,
+       /* Index 640 - 649 */
+       600, 1000, 1400, 1800, 2200, 2600, 3000, 3400, 3900, 4500,
+       /* Index 650 - 659 */
+       5000, 5400, 5800, 6200, 6600, 7000, 7400, 7800, 8200, 8600,
+       /* Index 660 - 669 */
+       9000, 9400, 9800, 10200, 10600, 11000, 11400, 11800, 12200, 12700,
+       /* Index 670 - 679 */
+       13300, 13800, 14200, 14600, 15000, 15400, 15800, 16200, 16600, 17000,
+       /* Index 680 - 689 */
+       17400, 17800, 18200, 18600, 19000, 19400, 19800, 20200, 20600, 21100,
+       /* Index 690 - 699 */
+       21400, 21900, 22500, 23000, 23400, 23800, 24200, 24600, 25000, 25400,
+       /* Index 700 - 709 */
+       25800, 26200, 26600, 27000, 27400, 27800, 28200, 28600, 29000, 29400,
+       /* Index 710 - 719 */
+       29800, 30200, 30600, 31000, 31400, 31900, 32500, 33000, 33400, 33800,
+       /* Index 720 - 729 */
+       34200, 34600, 35000, 35400, 35800, 36200, 36600, 37000, 37400, 37800,
+       /* Index 730 - 739 */
+       38200, 38600, 39000, 39400, 39800, 40200, 40600, 41000, 41400, 41800,
+       /* Index 740 - 749 */
+       42200, 42600, 43100, 43700, 44200, 44600, 45000, 45400, 45800, 46200,
+       /* Index 750 - 759 */
+       46600, 47000, 47400, 47800, 48200, 48600, 49000, 49400, 49800, 50200,
+       /* Index 760 - 769 */
+       50600, 51000, 51400, 51800, 52200, 52600, 53000, 53400, 53800, 54200,
+       /* Index 770 - 779 */
+       54600, 55000, 55400, 55900, 56500, 57000, 57400, 57800, 58200, 58600,
+       /* Index 780 - 789 */
+       59000, 59400, 59800, 60200, 60600, 61000, 61400, 61800, 62200, 62600,
+       /* Index 790 - 799 */
+       63000, 63400, 63800, 64200, 64600, 65000, 65400, 65800, 66200, 66600,
+       /* Index 800 - 809 */
+       67000, 67400, 67800, 68200, 68600, 69000, 69400, 69800, 70200, 70600,
+       /* Index 810 - 819 */
+       71000, 71500, 72100, 72600, 73000, 73400, 73800, 74200, 74600, 75000,
+       /* Index 820 - 829 */
+       75400, 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000,
+       /* Index 830 - 839 */
+       79400, 79800, 80200, 80600, 81000, 81400, 81800, 82200, 82600, 83000,
+       /* Index 840 - 849 */
+       83400, 83800, 84200, 84600, 85000, 85400, 85800, 86200, 86600, 87000,
+       /* Index 850 - 859 */
+       87400, 87800, 88200, 88600, 89000, 89400, 89800, 90200, 90600, 91000,
+       /* Index 860 - 869 */
+       91400, 91800, 92200, 92600, 93000, 93400, 93800, 94200, 94600, 95000,
+       /* Index 870 - 879 */
+       95400, 95800, 96200, 96600, 97000, 97500, 98100, 98600, 99000, 99400,
+       /* Index 880 - 889 */
+       99800, 100200, 100600, 101000, 101400, 101800, 102200, 102600, 103000,
+       103400,
+       /* Index 890 - 899 */
+       103800, 104200, 104600, 105000, 105400, 105800, 106200, 106600, 107000,
+       107400,
+       /* Index 900 - 909 */
+       107800, 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
+       111400,
+       /* Index 910 - 919 */
+       111800, 112200, 112600, 113000, 113400, 113800, 114200, 114600, 115000,
+       115400,
+       /* Index 920 - 929 */
+       115800, 116200, 116600, 117000, 117400, 117800, 118200, 118600, 119000,
+       119400,
+       /* Index 930 - 939 */
+       119800, 120200, 120600, 121000, 121400, 121800, 122400, 122600, 123000,
+       123400,
+       /* Index 940 - 945 */
+       123800, 1242000, 124600, 124900, 125000, 125000,
+};
+
+/* OMAP54xx ES2.0 data */
+/* TODO : Need to update the slope/constant for ES2.0 silicon */
+const struct ti_bandgap_data omap5430_data = {
+       .features = TI_BANDGAP_FEATURE_TSHUT_CONFIG |
+                       TI_BANDGAP_FEATURE_FREEZE_BIT |
+                       TI_BANDGAP_FEATURE_TALERT,
+       .fclock_name = "l3instr_ts_gclk_div",
+       .div_ck_name = "l3instr_ts_gclk_div",
+       .conv_table = omap5430_adc_to_temp,
+       .adc_start_val = OMAP5430_ADC_START_VALUE,
+       .adc_end_val = OMAP5430_ADC_END_VALUE,
+       .expose_sensor = ti_thermal_expose_sensor,
+       .remove_sensor = ti_thermal_remove_sensor,
+       .sensors = {
+               {
+               .registers = &omap5430_mpu_temp_sensor_registers,
+               .ts_data = &omap5430_mpu_temp_sensor_data,
+               .domain = "cpu",
+               .register_cooling = ti_thermal_register_cpu_cooling,
+               .unregister_cooling = ti_thermal_unregister_cpu_cooling,
+               .slope = OMAP_GRADIENT_SLOPE_5430_CPU,
+               .constant = OMAP_GRADIENT_CONST_5430_CPU,
+               .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU,
+               .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_CPU,
+               },
+               {
+               .registers = &omap5430_gpu_temp_sensor_registers,
+               .ts_data = &omap5430_gpu_temp_sensor_data,
+               .domain = "gpu",
+               .slope = OMAP_GRADIENT_SLOPE_5430_GPU,
+               .constant = OMAP_GRADIENT_CONST_5430_GPU,
+               .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU,
+               .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_GPU,
+               },
+               {
+               .registers = &omap5430_core_temp_sensor_registers,
+               .ts_data = &omap5430_core_temp_sensor_data,
+               .domain = "core",
+               },
+       },
+       .sensor_count = 3,
+};
diff --git a/drivers/staging/ti-soc-thermal/omap5xxx-bandgap.h b/drivers/staging/ti-soc-thermal/omap5xxx-bandgap.h
new file mode 100644 (file)
index 0000000..8824db4
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * OMAP5xxx bandgap registers, bitfields and temperature definitions
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ * Contact:
+ *   Eduardo Valentin <eduardo.valentin@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifndef __OMAP5XXX_BANDGAP_H
+#define __OMAP5XXX_BANDGAP_H
+
+/**
+ * *** OMAP5430 ***
+ *
+ * Below, in sequence, are the Register definitions,
+ * the bitfields and the temperature definitions for OMAP5430.
+ */
+
+/**
+ * OMAP5430 register definitions
+ *
+ * Registers are defined as offsets. The offsets are
+ * relative to FUSE_OPP_BGAP_GPU on 5430.
+ *
+ * Register below are grouped by domain (not necessarily in offset order)
+ */
+
+/* OMAP5430.GPU register offsets */
+#define OMAP5430_FUSE_OPP_BGAP_GPU                     0x0
+#define OMAP5430_TEMP_SENSOR_GPU_OFFSET                        0x150
+#define OMAP5430_BGAP_THRESHOLD_GPU_OFFSET             0x1A8
+#define OMAP5430_BGAP_TSHUT_GPU_OFFSET                 0x1B4
+#define OMAP5430_BGAP_CUMUL_DTEMP_GPU_OFFSET           0x1C0
+#define OMAP5430_BGAP_DTEMP_GPU_0_OFFSET               0x1F4
+#define OMAP5430_BGAP_DTEMP_GPU_1_OFFSET               0x1F8
+#define OMAP5430_BGAP_DTEMP_GPU_2_OFFSET               0x1FC
+#define OMAP5430_BGAP_DTEMP_GPU_3_OFFSET               0x200
+#define OMAP5430_BGAP_DTEMP_GPU_4_OFFSET               0x204
+
+/* OMAP5430.MPU register offsets */
+#define OMAP5430_FUSE_OPP_BGAP_MPU                     0x4
+#define OMAP5430_TEMP_SENSOR_MPU_OFFSET                        0x14C
+#define OMAP5430_BGAP_THRESHOLD_MPU_OFFSET             0x1A4
+#define OMAP5430_BGAP_TSHUT_MPU_OFFSET                 0x1B0
+#define OMAP5430_BGAP_CUMUL_DTEMP_MPU_OFFSET           0x1BC
+#define OMAP5430_BGAP_DTEMP_MPU_0_OFFSET               0x1E0
+#define OMAP5430_BGAP_DTEMP_MPU_1_OFFSET               0x1E4
+#define OMAP5430_BGAP_DTEMP_MPU_2_OFFSET               0x1E8
+#define OMAP5430_BGAP_DTEMP_MPU_3_OFFSET               0x1EC
+#define OMAP5430_BGAP_DTEMP_MPU_4_OFFSET               0x1F0
+
+/* OMAP5430.MPU register offsets */
+#define OMAP5430_FUSE_OPP_BGAP_CORE                    0x8
+#define OMAP5430_TEMP_SENSOR_CORE_OFFSET               0x154
+#define OMAP5430_BGAP_THRESHOLD_CORE_OFFSET            0x1AC
+#define OMAP5430_BGAP_TSHUT_CORE_OFFSET                        0x1B8
+#define OMAP5430_BGAP_CUMUL_DTEMP_CORE_OFFSET          0x1C4
+#define OMAP5430_BGAP_DTEMP_CORE_0_OFFSET              0x208
+#define OMAP5430_BGAP_DTEMP_CORE_1_OFFSET              0x20C
+#define OMAP5430_BGAP_DTEMP_CORE_2_OFFSET              0x210
+#define OMAP5430_BGAP_DTEMP_CORE_3_OFFSET              0x214
+#define OMAP5430_BGAP_DTEMP_CORE_4_OFFSET              0x218
+
+/* OMAP5430.common register offsets */
+#define OMAP5430_BGAP_CTRL_OFFSET                      0x1A0
+#define OMAP5430_BGAP_STATUS_OFFSET                    0x1C8
+
+/**
+ * Register bitfields for OMAP5430
+ *
+ * All the macros bellow define the required bits for
+ * controlling temperature on OMAP5430. Bit defines are
+ * grouped by register.
+ */
+
+/* OMAP5430.TEMP_SENSOR */
+#define OMAP5430_BGAP_TEMP_SENSOR_SOC_MASK             BIT(12)
+#define OMAP5430_BGAP_TEMPSOFF_MASK                    BIT(11)
+#define OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK            BIT(10)
+#define OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK           (0x3ff << 0)
+
+/* OMAP5430.BANDGAP_CTRL */
+#define OMAP5430_MASK_SIDLEMODE_MASK                   (0x3 << 30)
+#define OMAP5430_MASK_FREEZE_CORE_MASK                 BIT(23)
+#define OMAP5430_MASK_FREEZE_GPU_MASK                  BIT(22)
+#define OMAP5430_MASK_FREEZE_MPU_MASK                  BIT(21)
+#define OMAP5430_MASK_CLEAR_CORE_MASK                  BIT(20)
+#define OMAP5430_MASK_CLEAR_GPU_MASK                   BIT(19)
+#define OMAP5430_MASK_CLEAR_MPU_MASK                   BIT(18)
+#define OMAP5430_MASK_CLEAR_ACCUM_CORE_MASK            BIT(17)
+#define OMAP5430_MASK_CLEAR_ACCUM_GPU_MASK             BIT(16)
+#define OMAP5430_MASK_CLEAR_ACCUM_MPU_MASK             BIT(15)
+#define OMAP5430_MASK_HOT_CORE_MASK                    BIT(5)
+#define OMAP5430_MASK_COLD_CORE_MASK                   BIT(4)
+#define OMAP5430_MASK_HOT_GPU_MASK                     BIT(3)
+#define OMAP5430_MASK_COLD_GPU_MASK                    BIT(2)
+#define OMAP5430_MASK_HOT_MPU_MASK                     BIT(1)
+#define OMAP5430_MASK_COLD_MPU_MASK                    BIT(0)
+
+/* OMAP5430.BANDGAP_COUNTER */
+#define OMAP5430_COUNTER_MASK                          (0xffffff << 0)
+
+/* OMAP5430.BANDGAP_THRESHOLD */
+#define OMAP5430_T_HOT_MASK                            (0x3ff << 16)
+#define OMAP5430_T_COLD_MASK                           (0x3ff << 0)
+
+/* OMAP5430.TSHUT_THRESHOLD */
+#define OMAP5430_TSHUT_HOT_MASK                                (0x3ff << 16)
+#define OMAP5430_TSHUT_COLD_MASK                       (0x3ff << 0)
+
+/* OMAP5430.BANDGAP_CUMUL_DTEMP_MPU */
+#define OMAP5430_CUMUL_DTEMP_MPU_MASK                  (0xffffffff << 0)
+
+/* OMAP5430.BANDGAP_CUMUL_DTEMP_GPU */
+#define OMAP5430_CUMUL_DTEMP_GPU_MASK                  (0xffffffff << 0)
+
+/* OMAP5430.BANDGAP_CUMUL_DTEMP_CORE */
+#define OMAP5430_CUMUL_DTEMP_CORE_MASK                 (0xffffffff << 0)
+
+/* OMAP5430.BANDGAP_STATUS */
+#define OMAP5430_BGAP_ALERT_MASK                       BIT(31)
+#define OMAP5430_HOT_CORE_FLAG_MASK                    BIT(5)
+#define OMAP5430_COLD_CORE_FLAG_MASK                   BIT(4)
+#define OMAP5430_HOT_GPU_FLAG_MASK                     BIT(3)
+#define OMAP5430_COLD_GPU_FLAG_MASK                    BIT(2)
+#define OMAP5430_HOT_MPU_FLAG_MASK                     BIT(1)
+#define OMAP5430_COLD_MPU_FLAG_MASK                    BIT(0)
+
+/**
+ * Temperature limits and thresholds for OMAP5430
+ *
+ * All the macros bellow are definitions for handling the
+ * ADC conversions and representation of temperature limits
+ * and thresholds for OMAP5430. Definitions are grouped
+ * by temperature domain.
+ */
+
+/* OMAP5430.common temperature definitions */
+/* ADC conversion table limits */
+#define OMAP5430_ADC_START_VALUE                       540
+#define OMAP5430_ADC_END_VALUE                         945
+
+/* OMAP5430.GPU temperature definitions */
+/* bandgap clock limits */
+#define OMAP5430_GPU_MAX_FREQ                          1500000
+#define OMAP5430_GPU_MIN_FREQ                          1000000
+/* sensor limits */
+#define OMAP5430_GPU_MIN_TEMP                          -40000
+#define OMAP5430_GPU_MAX_TEMP                          125000
+#define OMAP5430_GPU_HYST_VAL                          5000
+/* interrupts thresholds */
+#define OMAP5430_GPU_TSHUT_HOT                         915
+#define OMAP5430_GPU_TSHUT_COLD                                900
+#define OMAP5430_GPU_T_HOT                             800
+#define OMAP5430_GPU_T_COLD                            795
+
+/* OMAP5430.MPU temperature definitions */
+/* bandgap clock limits */
+#define OMAP5430_MPU_MAX_FREQ                          1500000
+#define OMAP5430_MPU_MIN_FREQ                          1000000
+/* sensor limits */
+#define OMAP5430_MPU_MIN_TEMP                          -40000
+#define OMAP5430_MPU_MAX_TEMP                          125000
+#define OMAP5430_MPU_HYST_VAL                          5000
+/* interrupts thresholds */
+#define OMAP5430_MPU_TSHUT_HOT                         915
+#define OMAP5430_MPU_TSHUT_COLD                                900
+#define OMAP5430_MPU_T_HOT                             800
+#define OMAP5430_MPU_T_COLD                            795
+
+/* OMAP5430.CORE temperature definitions */
+/* bandgap clock limits */
+#define OMAP5430_CORE_MAX_FREQ                         1500000
+#define OMAP5430_CORE_MIN_FREQ                         1000000
+/* sensor limits */
+#define OMAP5430_CORE_MIN_TEMP                         -40000
+#define OMAP5430_CORE_MAX_TEMP                         125000
+#define OMAP5430_CORE_HYST_VAL                         5000
+/* interrupts thresholds */
+#define OMAP5430_CORE_TSHUT_HOT                                915
+#define OMAP5430_CORE_TSHUT_COLD                       900
+#define OMAP5430_CORE_T_HOT                            800
+#define OMAP5430_CORE_T_COLD                           795
+
+#endif /* __OMAP5XXX_BANDGAP_H */
diff --git a/drivers/staging/ti-soc-thermal/ti-bandgap.c b/drivers/staging/ti-soc-thermal/ti-bandgap.c
new file mode 100644 (file)
index 0000000..14049c2
--- /dev/null
@@ -0,0 +1,1338 @@
+/*
+ * TI Bandgap temperature sensor driver
+ *
+ * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: J Keerthy <j-keerthy@ti.com>
+ * Author: Moiz Sonasath <m-sonasath@ti.com>
+ * Couple of fixes, DT and MFD adaptation:
+ *   Eduardo Valentin <eduardo.valentin@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/reboot.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+
+#include "ti-bandgap.h"
+
+/***   Helper functions to access registers and their bitfields   ***/
+
+/**
+ * ti_bandgap_readl() - simple read helper function
+ * @bgp: pointer to ti_bandgap structure
+ * @reg: desired register (offset) to be read
+ *
+ * Helper function to read bandgap registers. It uses the io remapped area.
+ * Return: the register value.
+ */
+static u32 ti_bandgap_readl(struct ti_bandgap *bgp, u32 reg)
+{
+       return readl(bgp->base + reg);
+}
+
+/**
+ * ti_bandgap_writel() - simple write helper function
+ * @bgp: pointer to ti_bandgap structure
+ * @val: desired register value to be written
+ * @reg: desired register (offset) to be written
+ *
+ * Helper function to write bandgap registers. It uses the io remapped area.
+ */
+static void ti_bandgap_writel(struct ti_bandgap *bgp, u32 val, u32 reg)
+{
+       writel(val, bgp->base + reg);
+}
+
+/**
+ * DOC: macro to update bits.
+ *
+ * RMW_BITS() - used to read, modify and update bandgap bitfields.
+ *            The value passed will be shifted.
+ */
+#define RMW_BITS(bgp, id, reg, mask, val)                      \
+do {                                                           \
+       struct temp_sensor_registers *t;                        \
+       u32 r;                                                  \
+                                                               \
+       t = bgp->conf->sensors[(id)].registers;         \
+       r = ti_bandgap_readl(bgp, t->reg);                      \
+       r &= ~t->mask;                                          \
+       r |= (val) << __ffs(t->mask);                           \
+       ti_bandgap_writel(bgp, r, t->reg);                      \
+} while (0)
+
+/***   Basic helper functions   ***/
+
+/**
+ * ti_bandgap_power() - controls the power state of a bandgap device
+ * @bgp: pointer to ti_bandgap structure
+ * @on: desired power state (1 - on, 0 - off)
+ *
+ * Used to power on/off a bandgap device instance. Only used on those
+ * that features tempsoff bit.
+ *
+ * Return: 0 on success, -ENOTSUPP if tempsoff is not supported.
+ */
+static int ti_bandgap_power(struct ti_bandgap *bgp, bool on)
+{
+       int i, ret = 0;
+
+       if (!TI_BANDGAP_HAS(bgp, POWER_SWITCH)) {
+               ret = -ENOTSUPP;
+               goto exit;
+       }
+
+       for (i = 0; i < bgp->conf->sensor_count; i++)
+               /* active on 0 */
+               RMW_BITS(bgp, i, temp_sensor_ctrl, bgap_tempsoff_mask, !on);
+
+exit:
+       return ret;
+}
+
+/**
+ * ti_bandgap_read_temp() - helper function to read sensor temperature
+ * @bgp: pointer to ti_bandgap structure
+ * @id: bandgap sensor id
+ *
+ * Function to concentrate the steps to read sensor temperature register.
+ * This function is desired because, depending on bandgap device version,
+ * it might be needed to freeze the bandgap state machine, before fetching
+ * the register value.
+ *
+ * Return: temperature in ADC values.
+ */
+static u32 ti_bandgap_read_temp(struct ti_bandgap *bgp, int id)
+{
+       struct temp_sensor_registers *tsr;
+       u32 temp, reg;
+
+       tsr = bgp->conf->sensors[id].registers;
+       reg = tsr->temp_sensor_ctrl;
+
+       if (TI_BANDGAP_HAS(bgp, FREEZE_BIT)) {
+               RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 1);
+               /*
+                * In case we cannot read from cur_dtemp / dtemp_0,
+                * then we read from the last valid temp read
+                */
+               reg = tsr->ctrl_dtemp_1;
+       }
+
+       /* read temperature */
+       temp = ti_bandgap_readl(bgp, reg);
+       temp &= tsr->bgap_dtemp_mask;
+
+       if (TI_BANDGAP_HAS(bgp, FREEZE_BIT))
+               RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 0);
+
+       return temp;
+}
+
+/***   IRQ handlers   ***/
+
+/**
+ * ti_bandgap_talert_irq_handler() - handles Temperature alert IRQs
+ * @irq: IRQ number
+ * @data: private data (struct ti_bandgap *)
+ *
+ * This is the Talert handler. Use it only if bandgap device features
+ * HAS(TALERT). This handler goes over all sensors and checks their
+ * conditions and acts accordingly. In case there are events pending,
+ * it will reset the event mask to wait for the opposite event (next event).
+ * Every time there is a new event, it will be reported to thermal layer.
+ *
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t ti_bandgap_talert_irq_handler(int irq, void *data)
+{
+       struct ti_bandgap *bgp = data;
+       struct temp_sensor_registers *tsr;
+       u32 t_hot = 0, t_cold = 0, ctrl;
+       int i;
+
+       spin_lock(&bgp->lock);
+       for (i = 0; i < bgp->conf->sensor_count; i++) {
+               tsr = bgp->conf->sensors[i].registers;
+               ctrl = ti_bandgap_readl(bgp, tsr->bgap_status);
+
+               /* Read the status of t_hot */
+               t_hot = ctrl & tsr->status_hot_mask;
+
+               /* Read the status of t_cold */
+               t_cold = ctrl & tsr->status_cold_mask;
+
+               if (!t_cold && !t_hot)
+                       continue;
+
+               ctrl = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl);
+               /*
+                * One TALERT interrupt: Two sources
+                * If the interrupt is due to t_hot then mask t_hot and
+                * and unmask t_cold else mask t_cold and unmask t_hot
+                */
+               if (t_hot) {
+                       ctrl &= ~tsr->mask_hot_mask;
+                       ctrl |= tsr->mask_cold_mask;
+               } else if (t_cold) {
+                       ctrl &= ~tsr->mask_cold_mask;
+                       ctrl |= tsr->mask_hot_mask;
+               }
+
+               ti_bandgap_writel(bgp, ctrl, tsr->bgap_mask_ctrl);
+
+               dev_dbg(bgp->dev,
+                       "%s: IRQ from %s sensor: hotevent %d coldevent %d\n",
+                       __func__, bgp->conf->sensors[i].domain,
+                       t_hot, t_cold);
+
+               /* report temperature to whom may concern */
+               if (bgp->conf->report_temperature)
+                       bgp->conf->report_temperature(bgp, i);
+       }
+       spin_unlock(&bgp->lock);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ti_bandgap_tshut_irq_handler() - handles Temperature shutdown signal
+ * @irq: IRQ number
+ * @data: private data (unused)
+ *
+ * This is the Tshut handler. Use it only if bandgap device features
+ * HAS(TSHUT). If any sensor fires the Tshut signal, we simply shutdown
+ * the system.
+ *
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t ti_bandgap_tshut_irq_handler(int irq, void *data)
+{
+       pr_emerg("%s: TSHUT temperature reached. Needs shut down...\n",
+                __func__);
+
+       orderly_poweroff(true);
+
+       return IRQ_HANDLED;
+}
+
+/***   Helper functions which manipulate conversion ADC <-> mi Celsius   ***/
+
+/**
+ * ti_bandgap_adc_to_mcelsius() - converts an ADC value to mCelsius scale
+ * @bgp: struct ti_bandgap pointer
+ * @adc_val: value in ADC representation
+ * @t: address where to write the resulting temperature in mCelsius
+ *
+ * Simple conversion from ADC representation to mCelsius. In case the ADC value
+ * is out of the ADC conv table range, it returns -ERANGE, 0 on success.
+ * The conversion table is indexed by the ADC values.
+ *
+ * Return: 0 if conversion was successful, else -ERANGE in case the @adc_val
+ * argument is out of the ADC conv table range.
+ */
+static
+int ti_bandgap_adc_to_mcelsius(struct ti_bandgap *bgp, int adc_val, int *t)
+{
+       const struct ti_bandgap_data *conf = bgp->conf;
+       int ret = 0;
+
+       /* look up for temperature in the table and return the temperature */
+       if (adc_val < conf->adc_start_val || adc_val > conf->adc_end_val) {
+               ret = -ERANGE;
+               goto exit;
+       }
+
+       *t = bgp->conf->conv_table[adc_val - conf->adc_start_val];
+
+exit:
+       return ret;
+}
+
+/**
+ * ti_bandgap_mcelsius_to_adc() - converts a mCelsius value to ADC scale
+ * @bgp: struct ti_bandgap pointer
+ * @temp: value in mCelsius
+ * @adc: address where to write the resulting temperature in ADC representation
+ *
+ * Simple conversion from mCelsius to ADC values. In case the temp value
+ * is out of the ADC conv table range, it returns -ERANGE, 0 on success.
+ * The conversion table is indexed by the ADC values.
+ *
+ * Return: 0 if conversion was successful, else -ERANGE in case the @temp
+ * argument is out of the ADC conv table range.
+ */
+static
+int ti_bandgap_mcelsius_to_adc(struct ti_bandgap *bgp, long temp, int *adc)
+{
+       const struct ti_bandgap_data *conf = bgp->conf;
+       const int *conv_table = bgp->conf->conv_table;
+       int high, low, mid, ret = 0;
+
+       low = 0;
+       high = conf->adc_end_val - conf->adc_start_val;
+       mid = (high + low) / 2;
+
+       if (temp < conv_table[low] || temp > conv_table[high]) {
+               ret = -ERANGE;
+               goto exit;
+       }
+
+       while (low < high) {
+               if (temp < conv_table[mid])
+                       high = mid - 1;
+               else
+                       low = mid + 1;
+               mid = (low + high) / 2;
+       }
+
+       *adc = conf->adc_start_val + low;
+
+exit:
+       return ret;
+}
+
+/**
+ * ti_bandgap_add_hyst() - add hysteresis (in mCelsius) to an ADC value
+ * @bgp: struct ti_bandgap pointer
+ * @adc_val: temperature value in ADC representation
+ * @hyst_val: hysteresis value in mCelsius
+ * @sum: address where to write the resulting temperature (in ADC scale)
+ *
+ * Adds an hysteresis value (in mCelsius) to a ADC temperature value.
+ *
+ * Return: 0 on success, -ERANGE otherwise.
+ */
+static
+int ti_bandgap_add_hyst(struct ti_bandgap *bgp, int adc_val, int hyst_val,
+                       u32 *sum)
+{
+       int temp, ret;
+
+       /*
+        * Need to add in the mcelsius domain, so we have a temperature
+        * the conv_table range
+        */
+       ret = ti_bandgap_adc_to_mcelsius(bgp, adc_val, &temp);
+       if (ret < 0)
+               goto exit;
+
+       temp += hyst_val;
+
+       ret = ti_bandgap_mcelsius_to_adc(bgp, temp, sum);
+
+exit:
+       return ret;
+}
+
+/***   Helper functions handling device Alert/Shutdown signals   ***/
+
+/**
+ * ti_bandgap_unmask_interrupts() - unmasks the events of thot & tcold
+ * @bgp: struct ti_bandgap pointer
+ * @id: bandgap sensor id
+ * @t_hot: hot temperature value to trigger alert signal
+ * @t_cold: cold temperature value to trigger alert signal
+ *
+ * Checks the requested t_hot and t_cold values and configures the IRQ event
+ * masks accordingly. Call this function only if bandgap features HAS(TALERT).
+ */
+static void ti_bandgap_unmask_interrupts(struct ti_bandgap *bgp, int id,
+                                        u32 t_hot, u32 t_cold)
+{
+       struct temp_sensor_registers *tsr;
+       u32 temp, reg_val;
+
+       /* Read the current on die temperature */
+       temp = ti_bandgap_read_temp(bgp, id);
+
+       tsr = bgp->conf->sensors[id].registers;
+       reg_val = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl);
+
+       if (temp < t_hot)
+               reg_val |= tsr->mask_hot_mask;
+       else
+               reg_val &= ~tsr->mask_hot_mask;
+
+       if (t_cold < temp)
+               reg_val |= tsr->mask_cold_mask;
+       else
+               reg_val &= ~tsr->mask_cold_mask;
+       ti_bandgap_writel(bgp, reg_val, tsr->bgap_mask_ctrl);
+}
+
+/**
+ * ti_bandgap_update_alert_threshold() - sequence to update thresholds
+ * @bgp: struct ti_bandgap pointer
+ * @id: bandgap sensor id
+ * @val: value (ADC) of a new threshold
+ * @hot: desired threshold to be updated. true if threshold hot, false if
+ *       threshold cold
+ *
+ * It will program the required thresholds (hot and cold) for TALERT signal.
+ * This function can be used to update t_hot or t_cold, depending on @hot value.
+ * It checks the resulting t_hot and t_cold values, based on the new passed @val
+ * and configures the thresholds so that t_hot is always greater than t_cold.
+ * Call this function only if bandgap features HAS(TALERT).
+ *
+ * Return: 0 if no error, else corresponding error
+ */
+static int ti_bandgap_update_alert_threshold(struct ti_bandgap *bgp, int id,
+                                            int val, bool hot)
+{
+       struct temp_sensor_data *ts_data = bgp->conf->sensors[id].ts_data;
+       struct temp_sensor_registers *tsr;
+       u32 thresh_val, reg_val, t_hot, t_cold;
+       int err = 0;
+
+       tsr = bgp->conf->sensors[id].registers;
+
+       /* obtain the current value */
+       thresh_val = ti_bandgap_readl(bgp, tsr->bgap_threshold);
+       t_cold = (thresh_val & tsr->threshold_tcold_mask) >>
+               __ffs(tsr->threshold_tcold_mask);
+       t_hot = (thresh_val & tsr->threshold_thot_mask) >>
+               __ffs(tsr->threshold_thot_mask);
+       if (hot)
+               t_hot = val;
+       else
+               t_cold = val;
+
+       if (t_cold > t_hot) {
+               if (hot)
+                       err = ti_bandgap_add_hyst(bgp, t_hot,
+                                                 -ts_data->hyst_val,
+                                                 &t_cold);
+               else
+                       err = ti_bandgap_add_hyst(bgp, t_cold,
+                                                 ts_data->hyst_val,
+                                                 &t_hot);
+       }
+
+       /* write the new threshold values */
+       reg_val = thresh_val &
+                 ~(tsr->threshold_thot_mask | tsr->threshold_tcold_mask);
+       reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask)) |
+                  (t_cold << __ffs(tsr->threshold_tcold_mask));
+       ti_bandgap_writel(bgp, reg_val, tsr->bgap_threshold);
+
+       if (err) {
+               dev_err(bgp->dev, "failed to reprogram thot threshold\n");
+               err = -EIO;
+               goto exit;
+       }
+
+       ti_bandgap_unmask_interrupts(bgp, id, t_hot, t_cold);
+exit:
+       return err;
+}
+
+/**
+ * ti_bandgap_validate() - helper to check the sanity of a struct ti_bandgap
+ * @bgp: struct ti_bandgap pointer
+ * @id: bandgap sensor id
+ *
+ * Checks if the bandgap pointer is valid and if the sensor id is also
+ * applicable.
+ *
+ * Return: 0 if no errors, -EINVAL for invalid @bgp pointer or -ERANGE if
+ * @id cannot index @bgp sensors.
+ */
+static inline int ti_bandgap_validate(struct ti_bandgap *bgp, int id)
+{
+       int ret = 0;
+
+       if (IS_ERR_OR_NULL(bgp)) {
+               pr_err("%s: invalid bandgap pointer\n", __func__);
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       if ((id < 0) || (id >= bgp->conf->sensor_count)) {
+               dev_err(bgp->dev, "%s: sensor id out of range (%d)\n",
+                       __func__, id);
+               ret = -ERANGE;
+       }
+
+exit:
+       return ret;
+}
+
+/**
+ * _ti_bandgap_write_threshold() - helper to update TALERT t_cold or t_hot
+ * @bgp: struct ti_bandgap pointer
+ * @id: bandgap sensor id
+ * @val: value (mCelsius) of a new threshold
+ * @hot: desired threshold to be updated. true if threshold hot, false if
+ *       threshold cold
+ *
+ * It will update the required thresholds (hot and cold) for TALERT signal.
+ * This function can be used to update t_hot or t_cold, depending on @hot value.
+ * Validates the mCelsius range and update the requested threshold.
+ * Call this function only if bandgap features HAS(TALERT).
+ *
+ * Return: 0 if no error, else corresponding error value.
+ */
+static int _ti_bandgap_write_threshold(struct ti_bandgap *bgp, int id, int val,
+                                      bool hot)
+{
+       struct temp_sensor_data *ts_data;
+       struct temp_sensor_registers *tsr;
+       u32 adc_val;
+       int ret;
+
+       ret = ti_bandgap_validate(bgp, id);
+       if (ret)
+               goto exit;
+
+       if (!TI_BANDGAP_HAS(bgp, TALERT)) {
+               ret = -ENOTSUPP;
+               goto exit;
+       }
+
+       ts_data = bgp->conf->sensors[id].ts_data;
+       tsr = bgp->conf->sensors[id].registers;
+       if (hot) {
+               if (val < ts_data->min_temp + ts_data->hyst_val)
+                       ret = -EINVAL;
+       } else {
+               if (val > ts_data->max_temp + ts_data->hyst_val)
+                       ret = -EINVAL;
+       }
+
+       if (ret)
+               goto exit;
+
+       ret = ti_bandgap_mcelsius_to_adc(bgp, val, &adc_val);
+       if (ret < 0)
+               goto exit;
+
+       spin_lock(&bgp->lock);
+       ret = ti_bandgap_update_alert_threshold(bgp, id, adc_val, hot);
+       spin_unlock(&bgp->lock);
+
+exit:
+       return ret;
+}
+
+/**
+ * _ti_bandgap_read_threshold() - helper to read TALERT t_cold or t_hot
+ * @bgp: struct ti_bandgap pointer
+ * @id: bandgap sensor id
+ * @val: value (mCelsius) of a threshold
+ * @hot: desired threshold to be read. true if threshold hot, false if
+ *       threshold cold
+ *
+ * It will fetch the required thresholds (hot and cold) for TALERT signal.
+ * This function can be used to read t_hot or t_cold, depending on @hot value.
+ * Call this function only if bandgap features HAS(TALERT).
+ *
+ * Return: 0 if no error, -ENOTSUPP if it has no TALERT support, or the
+ * corresponding error value if some operation fails.
+ */
+static int _ti_bandgap_read_threshold(struct ti_bandgap *bgp, int id,
+                                     int *val, bool hot)
+{
+       struct temp_sensor_registers *tsr;
+       u32 temp, mask;
+       int ret = 0;
+
+       ret = ti_bandgap_validate(bgp, id);
+       if (ret)
+               goto exit;
+
+       if (!TI_BANDGAP_HAS(bgp, TALERT)) {
+               ret = -ENOTSUPP;
+               goto exit;
+       }
+
+       tsr = bgp->conf->sensors[id].registers;
+       if (hot)
+               mask = tsr->threshold_thot_mask;
+       else
+               mask = tsr->threshold_tcold_mask;
+
+       temp = ti_bandgap_readl(bgp, tsr->bgap_threshold);
+       temp = (temp & mask) >> __ffs(mask);
+       ret |= ti_bandgap_adc_to_mcelsius(bgp, temp, &temp);
+       if (ret) {
+               dev_err(bgp->dev, "failed to read thot\n");
+               ret = -EIO;
+               goto exit;
+       }
+
+       *val = temp;
+
+exit:
+       return ret;
+}
+
+/***   Exposed APIs   ***/
+
+/**
+ * ti_bandgap_read_thot() - reads sensor current thot
+ * @bgp: pointer to bandgap instance
+ * @id: sensor id
+ * @thot: resulting current thot value
+ *
+ * Return: 0 on success or the proper error code
+ */
+int ti_bandgap_read_thot(struct ti_bandgap *bgp, int id, int *thot)
+{
+       return _ti_bandgap_read_threshold(bgp, id, thot, true);
+}
+
+/**
+ * ti_bandgap_write_thot() - sets sensor current thot
+ * @bgp: pointer to bandgap instance
+ * @id: sensor id
+ * @val: desired thot value
+ *
+ * Return: 0 on success or the proper error code
+ */
+int ti_bandgap_write_thot(struct ti_bandgap *bgp, int id, int val)
+{
+       return _ti_bandgap_write_threshold(bgp, id, val, true);
+}
+
+/**
+ * ti_bandgap_read_tcold() - reads sensor current tcold
+ * @bgp: pointer to bandgap instance
+ * @id: sensor id
+ * @tcold: resulting current tcold value
+ *
+ * Return: 0 on success or the proper error code
+ */
+int ti_bandgap_read_tcold(struct ti_bandgap *bgp, int id, int *tcold)
+{
+       return _ti_bandgap_read_threshold(bgp, id, tcold, false);
+}
+
+/**
+ * ti_bandgap_write_tcold() - sets the sensor tcold
+ * @bgp: pointer to bandgap instance
+ * @id: sensor id
+ * @val: desired tcold value
+ *
+ * Return: 0 on success or the proper error code
+ */
+int ti_bandgap_write_tcold(struct ti_bandgap *bgp, int id, int val)
+{
+       return _ti_bandgap_write_threshold(bgp, id, val, false);
+}
+
+/**
+ * ti_bandgap_read_update_interval() - read the sensor update interval
+ * @bgp: pointer to bandgap instance
+ * @id: sensor id
+ * @interval: resulting update interval in miliseconds
+ *
+ * Return: 0 on success or the proper error code
+ */
+int ti_bandgap_read_update_interval(struct ti_bandgap *bgp, int id,
+                                   int *interval)
+{
+       struct temp_sensor_registers *tsr;
+       u32 time;
+       int ret;
+
+       ret = ti_bandgap_validate(bgp, id);
+       if (ret)
+               return ret;
+
+       if (!TI_BANDGAP_HAS(bgp, COUNTER))
+               return -ENOTSUPP;
+
+       tsr = bgp->conf->sensors[id].registers;
+       time = ti_bandgap_readl(bgp, tsr->bgap_counter);
+       time = (time & tsr->counter_mask) >> __ffs(tsr->counter_mask);
+       time = time * 1000 / bgp->clk_rate;
+
+       *interval = time;
+
+       return 0;
+}
+
+/**
+ * ti_bandgap_write_update_interval() - set the update interval
+ * @bgp: pointer to bandgap instance
+ * @id: sensor id
+ * @interval: desired update interval in miliseconds
+ *
+ * Return: 0 on success or the proper error code
+ */
+int ti_bandgap_write_update_interval(struct ti_bandgap *bgp,
+                                    int id, u32 interval)
+{
+       int ret = ti_bandgap_validate(bgp, id);
+       if (ret)
+               return ret;
+
+       if (!TI_BANDGAP_HAS(bgp, COUNTER))
+               return -ENOTSUPP;
+
+       interval = interval * bgp->clk_rate / 1000;
+       spin_lock(&bgp->lock);
+       RMW_BITS(bgp, id, bgap_counter, counter_mask, interval);
+       spin_unlock(&bgp->lock);
+
+       return 0;
+}
+
+/**
+ * ti_bandgap_read_temperature() - report current temperature
+ * @bgp: pointer to bandgap instance
+ * @id: sensor id
+ * @temperature: resulting temperature
+ *
+ * Return: 0 on success or the proper error code
+ */
+int ti_bandgap_read_temperature(struct ti_bandgap *bgp, int id,
+                               int *temperature)
+{
+       u32 temp;
+       int ret;
+
+       ret = ti_bandgap_validate(bgp, id);
+       if (ret)
+               return ret;
+
+       spin_lock(&bgp->lock);
+       temp = ti_bandgap_read_temp(bgp, id);
+       spin_unlock(&bgp->lock);
+
+       ret |= ti_bandgap_adc_to_mcelsius(bgp, temp, &temp);
+       if (ret)
+               return -EIO;
+
+       *temperature = temp;
+
+       return 0;
+}
+
+/**
+ * ti_bandgap_set_sensor_data() - helper function to store thermal
+ * framework related data.
+ * @bgp: pointer to bandgap instance
+ * @id: sensor id
+ * @data: thermal framework related data to be stored
+ *
+ * Return: 0 on success or the proper error code
+ */
+int ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data)
+{
+       int ret = ti_bandgap_validate(bgp, id);
+       if (ret)
+               return ret;
+
+       bgp->regval[id].data = data;
+
+       return 0;
+}
+
+/**
+ * ti_bandgap_get_sensor_data() - helper function to get thermal
+ * framework related data.
+ * @bgp: pointer to bandgap instance
+ * @id: sensor id
+ *
+ * Return: data stored by set function with sensor id on success or NULL
+ */
+void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id)
+{
+       int ret = ti_bandgap_validate(bgp, id);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return bgp->regval[id].data;
+}
+
+/***   Helper functions used during device initialization   ***/
+
+/**
+ * ti_bandgap_force_single_read() - executes 1 single ADC conversion
+ * @bgp: pointer to struct ti_bandgap
+ * @id: sensor id which it is desired to read 1 temperature
+ *
+ * Used to initialize the conversion state machine and set it to a valid
+ * state. Called during device initialization and context restore events.
+ *
+ * Return: 0
+ */
+static int
+ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id)
+{
+       u32 temp = 0, counter = 1000;
+
+       /* Select single conversion mode */
+       if (TI_BANDGAP_HAS(bgp, MODE_CONFIG))
+               RMW_BITS(bgp, id, bgap_mode_ctrl, mode_ctrl_mask, 0);
+
+       /* Start of Conversion = 1 */
+       RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 1);
+       /* Wait until DTEMP is updated */
+       temp = ti_bandgap_read_temp(bgp, id);
+
+       while ((temp == 0) && --counter)
+               temp = ti_bandgap_read_temp(bgp, id);
+       /* REVISIT: Check correct condition for end of conversion */
+
+       /* Start of Conversion = 0 */
+       RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 0);
+
+       return 0;
+}
+
+/**
+ * ti_bandgap_set_continous_mode() - One time enabling of continuous mode
+ * @bgp: pointer to struct ti_bandgap
+ *
+ * Call this function only if HAS(MODE_CONFIG) is set. As this driver may
+ * be used for junction temperature monitoring, it is desirable that the
+ * sensors are operational all the time, so that alerts are generated
+ * properly.
+ *
+ * Return: 0
+ */
+static int ti_bandgap_set_continuous_mode(struct ti_bandgap *bgp)
+{
+       int i;
+
+       for (i = 0; i < bgp->conf->sensor_count; i++) {
+               /* Perform a single read just before enabling continuous */
+               ti_bandgap_force_single_read(bgp, i);
+               RMW_BITS(bgp, i, bgap_mode_ctrl, mode_ctrl_mask, 1);
+       }
+
+       return 0;
+}
+
+/**
+ * ti_bandgap_tshut_init() - setup and initialize tshut handling
+ * @bgp: pointer to struct ti_bandgap
+ * @pdev: pointer to device struct platform_device
+ *
+ * Call this function only in case the bandgap features HAS(TSHUT).
+ * In this case, the driver needs to handle the TSHUT signal as an IRQ.
+ * The IRQ is wired as a GPIO, and for this purpose, it is required
+ * to specify which GPIO line is used. TSHUT IRQ is fired anytime
+ * one of the bandgap sensors violates the TSHUT high/hot threshold.
+ * And in that case, the system must go off.
+ *
+ * Return: 0 if no error, else error status
+ */
+static int ti_bandgap_tshut_init(struct ti_bandgap *bgp,
+                                struct platform_device *pdev)
+{
+       int gpio_nr = bgp->tshut_gpio;
+       int status;
+
+       /* Request for gpio_86 line */
+       status = gpio_request(gpio_nr, "tshut");
+       if (status < 0) {
+               dev_err(bgp->dev, "Could not request for TSHUT GPIO:%i\n", 86);
+               return status;
+       }
+       status = gpio_direction_input(gpio_nr);
+       if (status) {
+               dev_err(bgp->dev, "Cannot set input TSHUT GPIO %d\n", gpio_nr);
+               return status;
+       }
+
+       status = request_irq(gpio_to_irq(gpio_nr), ti_bandgap_tshut_irq_handler,
+                            IRQF_TRIGGER_RISING, "tshut", NULL);
+       if (status) {
+               gpio_free(gpio_nr);
+               dev_err(bgp->dev, "request irq failed for TSHUT");
+       }
+
+       return 0;
+}
+
+/**
+ * ti_bandgap_alert_init() - setup and initialize talert handling
+ * @bgp: pointer to struct ti_bandgap
+ * @pdev: pointer to device struct platform_device
+ *
+ * Call this function only in case the bandgap features HAS(TALERT).
+ * In this case, the driver needs to handle the TALERT signals as an IRQs.
+ * TALERT is a normal IRQ and it is fired any time thresholds (hot or cold)
+ * are violated. In these situation, the driver must reprogram the thresholds,
+ * accordingly to specified policy.
+ *
+ * Return: 0 if no error, else return corresponding error.
+ */
+static int ti_bandgap_talert_init(struct ti_bandgap *bgp,
+                                 struct platform_device *pdev)
+{
+       int ret;
+
+       bgp->irq = platform_get_irq(pdev, 0);
+       if (bgp->irq < 0) {
+               dev_err(&pdev->dev, "get_irq failed\n");
+               return bgp->irq;
+       }
+       ret = request_threaded_irq(bgp->irq, NULL,
+                                  ti_bandgap_talert_irq_handler,
+                                  IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                  "talert", bgp);
+       if (ret) {
+               dev_err(&pdev->dev, "Request threaded irq failed.\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id of_ti_bandgap_match[];
+/**
+ * ti_bandgap_build() - parse DT and setup a struct ti_bandgap
+ * @pdev: pointer to device struct platform_device
+ *
+ * Used to read the device tree properties accordingly to the bandgap
+ * matching version. Based on bandgap version and its capabilities it
+ * will build a struct ti_bandgap out of the required DT entries.
+ *
+ * Return: valid bandgap structure if successful, else returns ERR_PTR
+ * return value must be verified with IS_ERR.
+ */
+static struct ti_bandgap *ti_bandgap_build(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       const struct of_device_id *of_id;
+       struct ti_bandgap *bgp;
+       struct resource *res;
+       u32 prop;
+       int i;
+
+       /* just for the sake */
+       if (!node) {
+               dev_err(&pdev->dev, "no platform information available\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       bgp = devm_kzalloc(&pdev->dev, sizeof(*bgp), GFP_KERNEL);
+       if (!bgp) {
+               dev_err(&pdev->dev, "Unable to allocate mem for driver ref\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       of_id = of_match_device(of_ti_bandgap_match, &pdev->dev);
+       if (of_id)
+               bgp->conf = of_id->data;
+
+       /* register shadow for context save and restore */
+       bgp->regval = devm_kzalloc(&pdev->dev, sizeof(*bgp->regval) *
+                                  bgp->conf->sensor_count, GFP_KERNEL);
+       if (!bgp) {
+               dev_err(&pdev->dev, "Unable to allocate mem for driver ref\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       i = 0;
+       do {
+               void __iomem *chunk;
+
+               res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+               if (!res)
+                       break;
+               chunk = devm_request_and_ioremap(&pdev->dev, res);
+               if (i == 0)
+                       bgp->base = chunk;
+               if (!chunk) {
+                       dev_err(&pdev->dev,
+                               "failed to request the IO (%d:%pR).\n",
+                               i, res);
+                       return ERR_PTR(-EADDRNOTAVAIL);
+               }
+               i++;
+       } while (res);
+
+       if (TI_BANDGAP_HAS(bgp, TSHUT)) {
+               if (of_property_read_u32(node, "ti,tshut-gpio", &prop) < 0) {
+                       dev_err(&pdev->dev, "missing tshut gpio in device tree\n");
+                       return ERR_PTR(-EINVAL);
+               }
+               bgp->tshut_gpio = prop;
+               if (!gpio_is_valid(bgp->tshut_gpio)) {
+                       dev_err(&pdev->dev, "invalid gpio for tshut (%d)\n",
+                               bgp->tshut_gpio);
+                       return ERR_PTR(-EINVAL);
+               }
+       }
+
+       return bgp;
+}
+
+/***   Device driver call backs   ***/
+
+static
+int ti_bandgap_probe(struct platform_device *pdev)
+{
+       struct ti_bandgap *bgp;
+       int clk_rate, ret = 0, i;
+
+       bgp = ti_bandgap_build(pdev);
+       if (IS_ERR_OR_NULL(bgp)) {
+               dev_err(&pdev->dev, "failed to fetch platform data\n");
+               return PTR_ERR(bgp);
+       }
+       bgp->dev = &pdev->dev;
+
+       if (TI_BANDGAP_HAS(bgp, TSHUT)) {
+               ret = ti_bandgap_tshut_init(bgp, pdev);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "failed to initialize system tshut IRQ\n");
+                       return ret;
+               }
+       }
+
+       bgp->fclock = clk_get(NULL, bgp->conf->fclock_name);
+       ret = IS_ERR_OR_NULL(bgp->fclock);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to request fclock reference\n");
+               goto free_irqs;
+       }
+
+       bgp->div_clk = clk_get(NULL,  bgp->conf->div_ck_name);
+       ret = IS_ERR_OR_NULL(bgp->div_clk);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "failed to request div_ts_ck clock ref\n");
+               goto free_irqs;
+       }
+
+       for (i = 0; i < bgp->conf->sensor_count; i++) {
+               struct temp_sensor_registers *tsr;
+               u32 val;
+
+               tsr = bgp->conf->sensors[i].registers;
+               /*
+                * check if the efuse has a non-zero value if not
+                * it is an untrimmed sample and the temperatures
+                * may not be accurate
+                */
+               val = ti_bandgap_readl(bgp, tsr->bgap_efuse);
+               if (ret || !val)
+                       dev_info(&pdev->dev,
+                                "Non-trimmed BGAP, Temp not accurate\n");
+       }
+
+       clk_rate = clk_round_rate(bgp->div_clk,
+                                 bgp->conf->sensors[0].ts_data->max_freq);
+       if (clk_rate < bgp->conf->sensors[0].ts_data->min_freq ||
+           clk_rate == 0xffffffff) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "wrong clock rate (%d)\n", clk_rate);
+               goto put_clks;
+       }
+
+       ret = clk_set_rate(bgp->div_clk, clk_rate);
+       if (ret)
+               dev_err(&pdev->dev, "Cannot re-set clock rate. Continuing\n");
+
+       bgp->clk_rate = clk_rate;
+       if (TI_BANDGAP_HAS(bgp, CLK_CTRL))
+               clk_prepare_enable(bgp->fclock);
+
+
+       spin_lock_init(&bgp->lock);
+       bgp->dev = &pdev->dev;
+       platform_set_drvdata(pdev, bgp);
+
+       ti_bandgap_power(bgp, true);
+
+       /* Set default counter to 1 for now */
+       if (TI_BANDGAP_HAS(bgp, COUNTER))
+               for (i = 0; i < bgp->conf->sensor_count; i++)
+                       RMW_BITS(bgp, i, bgap_counter, counter_mask, 1);
+
+       /* Set default thresholds for alert and shutdown */
+       for (i = 0; i < bgp->conf->sensor_count; i++) {
+               struct temp_sensor_data *ts_data;
+
+               ts_data = bgp->conf->sensors[i].ts_data;
+
+               if (TI_BANDGAP_HAS(bgp, TALERT)) {
+                       /* Set initial Talert thresholds */
+                       RMW_BITS(bgp, i, bgap_threshold,
+                                threshold_tcold_mask, ts_data->t_cold);
+                       RMW_BITS(bgp, i, bgap_threshold,
+                                threshold_thot_mask, ts_data->t_hot);
+                       /* Enable the alert events */
+                       RMW_BITS(bgp, i, bgap_mask_ctrl, mask_hot_mask, 1);
+                       RMW_BITS(bgp, i, bgap_mask_ctrl, mask_cold_mask, 1);
+               }
+
+               if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG)) {
+                       /* Set initial Tshut thresholds */
+                       RMW_BITS(bgp, i, tshut_threshold,
+                                tshut_hot_mask, ts_data->tshut_hot);
+                       RMW_BITS(bgp, i, tshut_threshold,
+                                tshut_cold_mask, ts_data->tshut_cold);
+               }
+       }
+
+       if (TI_BANDGAP_HAS(bgp, MODE_CONFIG))
+               ti_bandgap_set_continuous_mode(bgp);
+
+       /* Set .250 seconds time as default counter */
+       if (TI_BANDGAP_HAS(bgp, COUNTER))
+               for (i = 0; i < bgp->conf->sensor_count; i++)
+                       RMW_BITS(bgp, i, bgap_counter, counter_mask,
+                                bgp->clk_rate / 4);
+
+       /* Every thing is good? Then expose the sensors */
+       for (i = 0; i < bgp->conf->sensor_count; i++) {
+               char *domain;
+
+               if (bgp->conf->sensors[i].register_cooling)
+                       bgp->conf->sensors[i].register_cooling(bgp, i);
+
+               domain = bgp->conf->sensors[i].domain;
+               if (bgp->conf->expose_sensor)
+                       bgp->conf->expose_sensor(bgp, i, domain);
+       }
+
+       /*
+        * Enable the Interrupts once everything is set. Otherwise irq handler
+        * might be called as soon as it is enabled where as rest of framework
+        * is still getting initialised.
+        */
+       if (TI_BANDGAP_HAS(bgp, TALERT)) {
+               ret = ti_bandgap_talert_init(bgp, pdev);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to initialize Talert IRQ\n");
+                       i = bgp->conf->sensor_count;
+                       goto disable_clk;
+               }
+       }
+
+       return 0;
+
+disable_clk:
+       if (TI_BANDGAP_HAS(bgp, CLK_CTRL))
+               clk_disable_unprepare(bgp->fclock);
+put_clks:
+       clk_put(bgp->fclock);
+       clk_put(bgp->div_clk);
+free_irqs:
+       if (TI_BANDGAP_HAS(bgp, TSHUT)) {
+               free_irq(gpio_to_irq(bgp->tshut_gpio), NULL);
+               gpio_free(bgp->tshut_gpio);
+       }
+
+       return ret;
+}
+
+static
+int ti_bandgap_remove(struct platform_device *pdev)
+{
+       struct ti_bandgap *bgp = platform_get_drvdata(pdev);
+       int i;
+
+       /* First thing is to remove sensor interfaces */
+       for (i = 0; i < bgp->conf->sensor_count; i++) {
+               if (bgp->conf->sensors[i].register_cooling)
+                       bgp->conf->sensors[i].unregister_cooling(bgp, i);
+
+               if (bgp->conf->remove_sensor)
+                       bgp->conf->remove_sensor(bgp, i);
+       }
+
+       ti_bandgap_power(bgp, false);
+
+       if (TI_BANDGAP_HAS(bgp, CLK_CTRL))
+               clk_disable_unprepare(bgp->fclock);
+       clk_put(bgp->fclock);
+       clk_put(bgp->div_clk);
+
+       if (TI_BANDGAP_HAS(bgp, TALERT))
+               free_irq(bgp->irq, bgp);
+
+       if (TI_BANDGAP_HAS(bgp, TSHUT)) {
+               free_irq(gpio_to_irq(bgp->tshut_gpio), NULL);
+               gpio_free(bgp->tshut_gpio);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int ti_bandgap_save_ctxt(struct ti_bandgap *bgp)
+{
+       int i;
+
+       for (i = 0; i < bgp->conf->sensor_count; i++) {
+               struct temp_sensor_registers *tsr;
+               struct temp_sensor_regval *rval;
+
+               rval = &bgp->regval[i];
+               tsr = bgp->conf->sensors[i].registers;
+
+               if (TI_BANDGAP_HAS(bgp, MODE_CONFIG))
+                       rval->bg_mode_ctrl = ti_bandgap_readl(bgp,
+                                                       tsr->bgap_mode_ctrl);
+               if (TI_BANDGAP_HAS(bgp, COUNTER))
+                       rval->bg_counter = ti_bandgap_readl(bgp,
+                                                       tsr->bgap_counter);
+               if (TI_BANDGAP_HAS(bgp, TALERT)) {
+                       rval->bg_threshold = ti_bandgap_readl(bgp,
+                                                       tsr->bgap_threshold);
+                       rval->bg_ctrl = ti_bandgap_readl(bgp,
+                                                  tsr->bgap_mask_ctrl);
+               }
+
+               if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG))
+                       rval->tshut_threshold = ti_bandgap_readl(bgp,
+                                                  tsr->tshut_threshold);
+       }
+
+       return 0;
+}
+
+static int ti_bandgap_restore_ctxt(struct ti_bandgap *bgp)
+{
+       int i;
+
+       for (i = 0; i < bgp->conf->sensor_count; i++) {
+               struct temp_sensor_registers *tsr;
+               struct temp_sensor_regval *rval;
+               u32 val = 0;
+
+               rval = &bgp->regval[i];
+               tsr = bgp->conf->sensors[i].registers;
+
+               if (TI_BANDGAP_HAS(bgp, COUNTER))
+                       val = ti_bandgap_readl(bgp, tsr->bgap_counter);
+
+               if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG))
+                       ti_bandgap_writel(bgp, rval->tshut_threshold,
+                                         tsr->tshut_threshold);
+               /* Force immediate temperature measurement and update
+                * of the DTEMP field
+                */
+               ti_bandgap_force_single_read(bgp, i);
+
+               if (TI_BANDGAP_HAS(bgp, COUNTER))
+                       ti_bandgap_writel(bgp, rval->bg_counter,
+                                         tsr->bgap_counter);
+               if (TI_BANDGAP_HAS(bgp, MODE_CONFIG))
+                       ti_bandgap_writel(bgp, rval->bg_mode_ctrl,
+                                         tsr->bgap_mode_ctrl);
+               if (TI_BANDGAP_HAS(bgp, TALERT)) {
+                       ti_bandgap_writel(bgp, rval->bg_threshold,
+                                         tsr->bgap_threshold);
+                       ti_bandgap_writel(bgp, rval->bg_ctrl,
+                                         tsr->bgap_mask_ctrl);
+               }
+       }
+
+       return 0;
+}
+
+static int ti_bandgap_suspend(struct device *dev)
+{
+       struct ti_bandgap *bgp = dev_get_drvdata(dev);
+       int err;
+
+       err = ti_bandgap_save_ctxt(bgp);
+       ti_bandgap_power(bgp, false);
+
+       if (TI_BANDGAP_HAS(bgp, CLK_CTRL))
+               clk_disable_unprepare(bgp->fclock);
+
+       return err;
+}
+
+static int ti_bandgap_resume(struct device *dev)
+{
+       struct ti_bandgap *bgp = dev_get_drvdata(dev);
+
+       if (TI_BANDGAP_HAS(bgp, CLK_CTRL))
+               clk_prepare_enable(bgp->fclock);
+
+       ti_bandgap_power(bgp, true);
+
+       return ti_bandgap_restore_ctxt(bgp);
+}
+static const struct dev_pm_ops ti_bandgap_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(ti_bandgap_suspend,
+                               ti_bandgap_resume)
+};
+
+#define DEV_PM_OPS     (&ti_bandgap_dev_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif
+
+static const struct of_device_id of_ti_bandgap_match[] = {
+#ifdef CONFIG_OMAP4_THERMAL
+       {
+               .compatible = "ti,omap4430-bandgap",
+               .data = (void *)&omap4430_data,
+       },
+       {
+               .compatible = "ti,omap4460-bandgap",
+               .data = (void *)&omap4460_data,
+       },
+       {
+               .compatible = "ti,omap4470-bandgap",
+               .data = (void *)&omap4470_data,
+       },
+#endif
+#ifdef CONFIG_OMAP5_THERMAL
+       {
+               .compatible = "ti,omap5430-bandgap",
+               .data = (void *)&omap5430_data,
+       },
+#endif
+       /* Sentinel */
+       { },
+};
+MODULE_DEVICE_TABLE(of, of_ti_bandgap_match);
+
+static struct platform_driver ti_bandgap_sensor_driver = {
+       .probe = ti_bandgap_probe,
+       .remove = ti_bandgap_remove,
+       .driver = {
+                       .name = "ti-soc-thermal",
+                       .pm = DEV_PM_OPS,
+                       .of_match_table = of_ti_bandgap_match,
+       },
+};
+
+module_platform_driver(ti_bandgap_sensor_driver);
+
+MODULE_DESCRIPTION("OMAP4+ bandgap temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ti-soc-thermal");
+MODULE_AUTHOR("Texas Instrument Inc.");
diff --git a/drivers/staging/ti-soc-thermal/ti-bandgap.h b/drivers/staging/ti-soc-thermal/ti-bandgap.h
new file mode 100644 (file)
index 0000000..04c6ec2
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * OMAP4 Bandgap temperature sensor driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Contact:
+ *   Eduardo Valentin <eduardo.valentin@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifndef __TI_BANDGAP_H
+#define __TI_BANDGAP_H
+
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/err.h>
+
+/**
+ * DOC: bandgap driver data structure
+ * ==================================
+ *
+ *   +----------+----------------+
+ *   | struct temp_sensor_regval |
+ *   +---------------------------+
+ *              * (Array of)
+ *              |
+ *              |
+ *   +-------------------+   +-----------------+
+ *   | struct ti_bandgap |-->| struct device * |
+ *   +----------+--------+   +-----------------+
+ *              |
+ *              |
+ *              V
+ *   +------------------------+
+ *   | struct ti_bandgap_data |
+ *   +------------------------+
+ *              |
+ *              |
+ *              * (Array of)
+ * +------------+------------------------------------------------------+
+ * | +----------+------------+   +-------------------------+           |
+ * | | struct ti_temp_sensor |-->| struct temp_sensor_data |           |
+ * | +-----------------------+   +------------+------------+           |
+ * |            |                                                      |
+ * |            +                                                      |
+ * |            V                                                      |
+ * | +----------+-------------------+                                  |
+ * | | struct temp_sensor_registers |                                  |
+ * | +------------------------------+                                  |
+ * |                                                                   |
+ * +-------------------------------------------------------------------+
+ *
+ * Above is a simple diagram describing how the data structure below
+ * are organized. For each bandgap device there should be a ti_bandgap_data
+ * containing the device instance configuration, as well as, an array of
+ * sensors, representing every sensor instance present in this bandgap.
+ */
+
+/**
+ * struct temp_sensor_registers - descriptor to access registers and bitfields
+ * @temp_sensor_ctrl: TEMP_SENSOR_CTRL register offset
+ * @bgap_tempsoff_mask: mask to temp_sensor_ctrl.tempsoff
+ * @bgap_soc_mask: mask to temp_sensor_ctrl.soc
+ * @bgap_eocz_mask: mask to temp_sensor_ctrl.eocz
+ * @bgap_dtemp_mask: mask to temp_sensor_ctrl.dtemp
+ * @bgap_mask_ctrl: BANDGAP_MASK_CTRL register offset
+ * @mask_hot_mask: mask to bandgap_mask_ctrl.mask_hot
+ * @mask_cold_mask: mask to bandgap_mask_ctrl.mask_cold
+ * @mask_sidlemode_mask: mask to bandgap_mask_ctrl.mask_sidlemode
+ * @mask_freeze_mask: mask to bandgap_mask_ctrl.mask_free
+ * @mask_clear_mask: mask to bandgap_mask_ctrl.mask_clear
+ * @mask_clear_accum_mask: mask to bandgap_mask_ctrl.mask_clear_accum
+ * @bgap_mode_ctrl: BANDGAP_MODE_CTRL register offset
+ * @mode_ctrl_mask: mask to bandgap_mode_ctrl.mode_ctrl
+ * @bgap_counter: BANDGAP_COUNTER register offset
+ * @counter_mask: mask to bandgap_counter.counter
+ * @bgap_threshold: BANDGAP_THRESHOLD register offset (TALERT thresholds)
+ * @threshold_thot_mask: mask to bandgap_threhold.thot
+ * @threshold_tcold_mask: mask to bandgap_threhold.tcold
+ * @tshut_threshold: TSHUT_THRESHOLD register offset (TSHUT thresholds)
+ * @tshut_efuse_mask: mask to tshut_threshold.tshut_efuse
+ * @tshut_efuse_shift: shift to tshut_threshold.tshut_efuse
+ * @tshut_hot_mask: mask to tshut_threhold.thot
+ * @tshut_cold_mask: mask to tshut_threhold.thot
+ * @bgap_status: BANDGAP_STATUS register offset
+ * @status_clean_stop_mask: mask to bandgap_status.clean_stop
+ * @status_bgap_alert_mask: mask to bandgap_status.bandgap_alert
+ * @status_hot_mask: mask to bandgap_status.hot
+ * @status_cold_mask: mask to bandgap_status.cold
+ * @bgap_cumul_dtemp: BANDGAP_CUMUL_DTEMP register offset
+ * @ctrl_dtemp_0: CTRL_DTEMP0 register offset
+ * @ctrl_dtemp_1: CTRL_DTEMP1 register offset
+ * @ctrl_dtemp_2: CTRL_DTEMP2 register offset
+ * @ctrl_dtemp_3: CTRL_DTEMP3 register offset
+ * @ctrl_dtemp_4: CTRL_DTEMP4 register offset
+ * @bgap_efuse: BANDGAP_EFUSE register offset
+ *
+ * The register offsets and bitfields might change across
+ * OMAP and variants versions. Hence this struct serves as a
+ * descriptor map on how to access the registers and the bitfields.
+ *
+ * This descriptor contains registers of all versions of bandgap chips.
+ * Not all versions will use all registers, depending on the available
+ * features. Please read TRMs for descriptive explanation on each bitfield.
+ */
+
+struct temp_sensor_registers {
+       u32     temp_sensor_ctrl;
+       u32     bgap_tempsoff_mask;
+       u32     bgap_soc_mask;
+       u32     bgap_eocz_mask; /* not used: but needs revisit */
+       u32     bgap_dtemp_mask;
+
+       u32     bgap_mask_ctrl;
+       u32     mask_hot_mask;
+       u32     mask_cold_mask;
+       u32     mask_sidlemode_mask; /* not used: but may be needed for pm */
+       u32     mask_freeze_mask;
+       u32     mask_clear_mask; /* not used: but needed for trending */
+       u32     mask_clear_accum_mask; /* not used: but needed for trending */
+
+       u32     bgap_mode_ctrl;
+       u32     mode_ctrl_mask;
+
+       u32     bgap_counter;
+       u32     counter_mask;
+
+       u32     bgap_threshold;
+       u32     threshold_thot_mask;
+       u32     threshold_tcold_mask;
+
+       u32     tshut_threshold;
+       u32     tshut_efuse_mask; /* not used */
+       u32     tshut_efuse_shift; /* not used */
+       u32     tshut_hot_mask;
+       u32     tshut_cold_mask;
+
+       u32     bgap_status;
+       u32     status_clean_stop_mask; /* not used: but needed for trending */
+       u32     status_bgap_alert_mask; /* not used */
+       u32     status_hot_mask;
+       u32     status_cold_mask;
+
+       u32     bgap_cumul_dtemp; /* not used: but needed for trending */
+       u32     ctrl_dtemp_0; /* not used: but needed for trending */
+       u32     ctrl_dtemp_1; /* not used: but needed for trending */
+       u32     ctrl_dtemp_2; /* not used: but needed for trending */
+       u32     ctrl_dtemp_3; /* not used: but needed for trending */
+       u32     ctrl_dtemp_4; /* not used: but needed for trending */
+       u32     bgap_efuse;
+};
+
+/**
+ * struct temp_sensor_data - The thresholds and limits for temperature sensors.
+ * @tshut_hot: temperature to trigger a thermal reset (initial value)
+ * @tshut_cold: temp to get the plat out of reset due to thermal (init val)
+ * @t_hot: temperature to trigger a thermal alert (high initial value)
+ * @t_cold: temperature to trigger a thermal alert (low initial value)
+ * @min_freq: sensor minimum clock rate
+ * @max_freq: sensor maximum clock rate
+ * @max_temp: sensor maximum temperature
+ * @min_temp: sensor minimum temperature
+ * @hyst_val: temperature hysteresis considered while converting ADC values
+ * @update_int1: update interval
+ * @update_int2: update interval
+ *
+ * This data structure will hold the required thresholds and temperature limits
+ * for a specific temperature sensor, like shutdown temperature, alert
+ * temperature, clock / rate used, ADC conversion limits and update intervals
+ */
+struct temp_sensor_data {
+       u32     tshut_hot;
+       u32     tshut_cold;
+       u32     t_hot;
+       u32     t_cold;
+       u32     min_freq;
+       u32     max_freq;
+       int     max_temp;
+       int     min_temp;
+       int     hyst_val;
+       u32     update_int1; /* not used */
+       u32     update_int2; /* not used */
+};
+
+struct ti_bandgap_data;
+
+/**
+ * struct temp_sensor_regval - temperature sensor register values and priv data
+ * @bg_mode_ctrl: temp sensor control register value
+ * @bg_ctrl: bandgap ctrl register value
+ * @bg_counter: bandgap counter value
+ * @bg_threshold: bandgap threshold register value
+ * @tshut_threshold: bandgap tshut register value
+ * @data: private data
+ *
+ * Data structure to save and restore bandgap register set context. Only
+ * required registers are shadowed, when needed.
+ */
+struct temp_sensor_regval {
+       u32                     bg_mode_ctrl;
+       u32                     bg_ctrl;
+       u32                     bg_counter;
+       u32                     bg_threshold;
+       u32                     tshut_threshold;
+       void                    *data;
+};
+
+/**
+ * struct ti_bandgap - bandgap device structure
+ * @dev: struct device pointer
+ * @base: io memory base address
+ * @conf: struct with bandgap configuration set (# sensors, conv_table, etc)
+ * @regval: temperature sensor register values
+ * @fclock: pointer to functional clock of temperature sensor
+ * @div_clk: pointer to divider clock of temperature sensor fclk
+ * @lock: spinlock for ti_bandgap structure
+ * @irq: MPU IRQ number for thermal alert
+ * @tshut_gpio: GPIO where Tshut signal is routed
+ * @clk_rate: Holds current clock rate
+ *
+ * The bandgap device structure representing the bandgap device instance.
+ * It holds most of the dynamic stuff. Configurations and sensor specific
+ * entries are inside the @conf structure.
+ */
+struct ti_bandgap {
+       struct device                   *dev;
+       void __iomem                    *base;
+       const struct ti_bandgap_data    *conf;
+       struct temp_sensor_regval       *regval;
+       struct clk                      *fclock;
+       struct clk                      *div_clk;
+       spinlock_t                      lock; /* shields this struct */
+       int                             irq;
+       int                             tshut_gpio;
+       u32                             clk_rate;
+};
+
+/**
+ * struct ti_temp_sensor - bandgap temperature sensor configuration data
+ * @ts_data: pointer to struct with thresholds, limits of temperature sensor
+ * @registers: pointer to the list of register offsets and bitfields
+ * @domain: the name of the domain where the sensor is located
+ * @slope: sensor gradient slope info for hotspot extrapolation equation
+ * @constant: sensor gradient const info for hotspot extrapolation equation
+ * @slope_pcb: sensor gradient slope info for hotspot extrapolation equation
+ *             with no external influence
+ * @constant_pcb: sensor gradient const info for hotspot extrapolation equation
+ *             with no external influence
+ * @register_cooling: function to describe how this sensor is going to be cooled
+ * @unregister_cooling: function to release cooling data
+ *
+ * Data structure to describe a temperature sensor handled by a bandgap device.
+ * It should provide configuration details on this sensor, such as how to
+ * access the registers affecting this sensor, shadow register buffer, how to
+ * assess the gradient from hotspot, how to cooldown the domain when sensor
+ * reports too hot temperature.
+ */
+struct ti_temp_sensor {
+       struct temp_sensor_data         *ts_data;
+       struct temp_sensor_registers    *registers;
+       char                            *domain;
+       /* for hotspot extrapolation */
+       const int                       slope;
+       const int                       constant;
+       const int                       slope_pcb;
+       const int                       constant_pcb;
+       int (*register_cooling)(struct ti_bandgap *bgp, int id);
+       int (*unregister_cooling)(struct ti_bandgap *bgp, int id);
+};
+
+/**
+ * DOC: ti bandgap feature types
+ *
+ * TI_BANDGAP_FEATURE_TSHUT - used when the thermal shutdown signal output
+ *      of a bandgap device instance is routed to the processor. This means
+ *      the system must react and perform the shutdown by itself (handle an
+ *      IRQ, for instance).
+ *
+ * TI_BANDGAP_FEATURE_TSHUT_CONFIG - used when the bandgap device has control
+ *      over the thermal shutdown configuration. This means that the thermal
+ *      shutdown thresholds are programmable, for instance.
+ *
+ * TI_BANDGAP_FEATURE_TALERT - used when the bandgap device instance outputs
+ *      a signal representing violation of programmable alert thresholds.
+ *
+ * TI_BANDGAP_FEATURE_MODE_CONFIG - used when it is possible to choose which
+ *      mode, continuous or one shot, the bandgap device instance will operate.
+ *
+ * TI_BANDGAP_FEATURE_COUNTER - used when the bandgap device instance allows
+ *      programming the update interval of its internal state machine.
+ *
+ * TI_BANDGAP_FEATURE_POWER_SWITCH - used when the bandgap device allows
+ *      itself to be switched on/off.
+ *
+ * TI_BANDGAP_FEATURE_CLK_CTRL - used when the clocks feeding the bandgap
+ *      device are gateable or not.
+ *
+ * TI_BANDGAP_FEATURE_FREEZE_BIT - used when the bandgap device features
+ *      a history buffer that its update can be freezed/unfreezed.
+ *
+ * TI_BANDGAP_HAS(b, f) - macro to check if a bandgap device is capable of a
+ *      specific feature (above) or not. Return non-zero, if yes.
+ */
+#define TI_BANDGAP_FEATURE_TSHUT               BIT(0)
+#define TI_BANDGAP_FEATURE_TSHUT_CONFIG                BIT(1)
+#define TI_BANDGAP_FEATURE_TALERT              BIT(2)
+#define TI_BANDGAP_FEATURE_MODE_CONFIG         BIT(3)
+#define TI_BANDGAP_FEATURE_COUNTER             BIT(4)
+#define TI_BANDGAP_FEATURE_POWER_SWITCH                BIT(5)
+#define TI_BANDGAP_FEATURE_CLK_CTRL            BIT(6)
+#define TI_BANDGAP_FEATURE_FREEZE_BIT          BIT(7)
+#define TI_BANDGAP_HAS(b, f)                   \
+                       ((b)->conf->features & TI_BANDGAP_FEATURE_ ## f)
+
+/**
+ * struct ti_bandgap_data - ti bandgap data configuration structure
+ * @features: a bitwise flag set to describe the device features
+ * @conv_table: Pointer to ADC to temperature conversion table
+ * @adc_start_val: ADC conversion table starting value
+ * @adc_end_val: ADC conversion table ending value
+ * @fclock_name: clock name of the functional clock
+ * @div_ck_name: clock name of the clock divisor
+ * @sensor_count: count of temperature sensor within this bandgap device
+ * @report_temperature: callback to report thermal alert to thermal API
+ * @expose_sensor: callback to export sensor to thermal API
+ * @remove_sensor: callback to destroy sensor from thermal API
+ * @sensors: array of sensors present in this bandgap instance
+ *
+ * This is a data structure which should hold most of the static configuration
+ * of a bandgap device instance. It should describe which features this instance
+ * is capable of, the clock names to feed this device, the amount of sensors and
+ * their configuration representation, and how to export and unexport them to
+ * a thermal API.
+ */
+struct ti_bandgap_data {
+       unsigned int                    features;
+       const int                       *conv_table;
+       u32                             adc_start_val;
+       u32                             adc_end_val;
+       char                            *fclock_name;
+       char                            *div_ck_name;
+       int                             sensor_count;
+       int (*report_temperature)(struct ti_bandgap *bgp, int id);
+       int (*expose_sensor)(struct ti_bandgap *bgp, int id, char *domain);
+       int (*remove_sensor)(struct ti_bandgap *bgp, int id);
+
+       /* this needs to be at the end */
+       struct ti_temp_sensor           sensors[];
+};
+
+int ti_bandgap_read_thot(struct ti_bandgap *bgp, int id, int *thot);
+int ti_bandgap_write_thot(struct ti_bandgap *bgp, int id, int val);
+int ti_bandgap_read_tcold(struct ti_bandgap *bgp, int id, int *tcold);
+int ti_bandgap_write_tcold(struct ti_bandgap *bgp, int id, int val);
+int ti_bandgap_read_update_interval(struct ti_bandgap *bgp, int id,
+                                   int *interval);
+int ti_bandgap_write_update_interval(struct ti_bandgap *bgp, int id,
+                                    u32 interval);
+int ti_bandgap_read_temperature(struct ti_bandgap *bgp, int id,
+                                 int *temperature);
+int ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data);
+void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id);
+
+#ifdef CONFIG_OMAP4_THERMAL
+extern const struct ti_bandgap_data omap4430_data;
+extern const struct ti_bandgap_data omap4460_data;
+extern const struct ti_bandgap_data omap4470_data;
+#else
+#define omap4430_data                                  NULL
+#define omap4460_data                                  NULL
+#define omap4470_data                                  NULL
+#endif
+
+#ifdef CONFIG_OMAP5_THERMAL
+extern const struct ti_bandgap_data omap5430_data;
+#else
+#define omap5430_data                                  NULL
+#endif
+
+#endif
diff --git a/drivers/staging/ti-soc-thermal/ti-thermal-common.c b/drivers/staging/ti-soc-thermal/ti-thermal-common.c
new file mode 100644 (file)
index 0000000..231c549
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * OMAP thermal driver interface
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Contact:
+ *   Eduardo Valentin <eduardo.valentin@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/thermal.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/cpu_cooling.h>
+
+#include "ti-thermal.h"
+#include "ti-bandgap.h"
+
+/* common data structures */
+struct ti_thermal_data {
+       struct thermal_zone_device *ti_thermal;
+       struct thermal_cooling_device *cool_dev;
+       struct ti_bandgap *bgp;
+       enum thermal_device_mode mode;
+       struct work_struct thermal_wq;
+       int sensor_id;
+};
+
+static void ti_thermal_work(struct work_struct *work)
+{
+       struct ti_thermal_data *data = container_of(work,
+                                       struct ti_thermal_data, thermal_wq);
+
+       thermal_zone_device_update(data->ti_thermal);
+
+       dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n",
+               data->ti_thermal->type);
+}
+
+/**
+ * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature
+ * @t: omap sensor temperature
+ * @s: omap sensor slope value
+ * @c: omap sensor const value
+ */
+static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
+{
+       int delta = t * s / 1000 + c;
+
+       if (delta < 0)
+               delta = 0;
+
+       return t + delta;
+}
+
+/* thermal zone ops */
+/* Get temperature callback function for thermal zone*/
+static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
+                                     unsigned long *temp)
+{
+       struct ti_thermal_data *data = thermal->devdata;
+       struct ti_bandgap *bgp;
+       const struct ti_temp_sensor *s;
+       int ret, tmp, pcb_temp, slope, constant;
+
+       if (!data)
+               return 0;
+
+       bgp = data->bgp;
+       s = &bgp->conf->sensors[data->sensor_id];
+
+       ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp);
+       if (ret)
+               return ret;
+
+       pcb_temp = 0;
+       /* TODO: Introduce pcb temperature lookup */
+       /* In case pcb zone is available, use the extrapolation rule with it */
+       if (pcb_temp) {
+               tmp -= pcb_temp;
+               slope = s->slope_pcb;
+               constant = s->constant_pcb;
+       } else {
+               slope = s->slope;
+               constant = s->constant;
+       }
+       *temp = ti_thermal_hotspot_temperature(tmp, slope, constant);
+
+       return ret;
+}
+
+/* Bind callback functions for thermal zone */
+static int ti_thermal_bind(struct thermal_zone_device *thermal,
+                          struct thermal_cooling_device *cdev)
+{
+       struct ti_thermal_data *data = thermal->devdata;
+       int id;
+
+       if (IS_ERR_OR_NULL(data))
+               return -ENODEV;
+
+       /* check if this is the cooling device we registered */
+       if (data->cool_dev != cdev)
+               return 0;
+
+       id = data->sensor_id;
+
+       /* TODO: bind with min and max states */
+       /* Simple thing, two trips, one passive another critical */
+       return thermal_zone_bind_cooling_device(thermal, 0, cdev,
+                                               THERMAL_NO_LIMIT,
+                                               THERMAL_NO_LIMIT);
+}
+
+/* Unbind callback functions for thermal zone */
+static int ti_thermal_unbind(struct thermal_zone_device *thermal,
+                            struct thermal_cooling_device *cdev)
+{
+       struct ti_thermal_data *data = thermal->devdata;
+
+       if (IS_ERR_OR_NULL(data))
+               return -ENODEV;
+
+       /* check if this is the cooling device we registered */
+       if (data->cool_dev != cdev)
+               return 0;
+
+       /* Simple thing, two trips, one passive another critical */
+       return thermal_zone_unbind_cooling_device(thermal, 0, cdev);
+}
+
+/* Get mode callback functions for thermal zone */
+static int ti_thermal_get_mode(struct thermal_zone_device *thermal,
+                              enum thermal_device_mode *mode)
+{
+       struct ti_thermal_data *data = thermal->devdata;
+
+       if (data)
+               *mode = data->mode;
+
+       return 0;
+}
+
+/* Set mode callback functions for thermal zone */
+static int ti_thermal_set_mode(struct thermal_zone_device *thermal,
+                              enum thermal_device_mode mode)
+{
+       struct ti_thermal_data *data = thermal->devdata;
+
+       if (!data->ti_thermal) {
+               dev_notice(&thermal->device, "thermal zone not registered\n");
+               return 0;
+       }
+
+       mutex_lock(&data->ti_thermal->lock);
+
+       if (mode == THERMAL_DEVICE_ENABLED)
+               data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
+       else
+               data->ti_thermal->polling_delay = 0;
+
+       mutex_unlock(&data->ti_thermal->lock);
+
+       data->mode = mode;
+       thermal_zone_device_update(data->ti_thermal);
+       dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n",
+               data->ti_thermal->polling_delay);
+
+       return 0;
+}
+
+/* Get trip type callback functions for thermal zone */
+static int ti_thermal_get_trip_type(struct thermal_zone_device *thermal,
+                                   int trip, enum thermal_trip_type *type)
+{
+       if (!ti_thermal_is_valid_trip(trip))
+               return -EINVAL;
+
+       if (trip + 1 == OMAP_TRIP_NUMBER)
+               *type = THERMAL_TRIP_CRITICAL;
+       else
+               *type = THERMAL_TRIP_PASSIVE;
+
+       return 0;
+}
+
+/* Get trip temperature callback functions for thermal zone */
+static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal,
+                                   int trip, unsigned long *temp)
+{
+       if (!ti_thermal_is_valid_trip(trip))
+               return -EINVAL;
+
+       *temp = ti_thermal_get_trip_value(trip);
+
+       return 0;
+}
+
+/* Get critical temperature callback functions for thermal zone */
+static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal,
+                                   unsigned long *temp)
+{
+       /* shutdown zone */
+       return ti_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp);
+}
+
+static struct thermal_zone_device_ops ti_thermal_ops = {
+       .get_temp = ti_thermal_get_temp,
+       /* TODO: add .get_trend */
+       .bind = ti_thermal_bind,
+       .unbind = ti_thermal_unbind,
+       .get_mode = ti_thermal_get_mode,
+       .set_mode = ti_thermal_set_mode,
+       .get_trip_type = ti_thermal_get_trip_type,
+       .get_trip_temp = ti_thermal_get_trip_temp,
+       .get_crit_temp = ti_thermal_get_crit_temp,
+};
+
+static struct ti_thermal_data
+*ti_thermal_build_data(struct ti_bandgap *bgp, int id)
+{
+       struct ti_thermal_data *data;
+
+       data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               dev_err(bgp->dev, "kzalloc fail\n");
+               return NULL;
+       }
+       data->sensor_id = id;
+       data->bgp = bgp;
+       data->mode = THERMAL_DEVICE_ENABLED;
+       INIT_WORK(&data->thermal_wq, ti_thermal_work);
+
+       return data;
+}
+
+int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
+                            char *domain)
+{
+       struct ti_thermal_data *data;
+
+       data = ti_bandgap_get_sensor_data(bgp, id);
+
+       if (IS_ERR_OR_NULL(data))
+               data = ti_thermal_build_data(bgp, id);
+
+       if (!data)
+               return -EINVAL;
+
+       /* TODO: remove TC1 TC2 */
+       /* Create thermal zone */
+       data->ti_thermal = thermal_zone_device_register(domain,
+                               OMAP_TRIP_NUMBER, 0, data, &ti_thermal_ops,
+                               NULL, FAST_TEMP_MONITORING_RATE,
+                               FAST_TEMP_MONITORING_RATE);
+       if (IS_ERR_OR_NULL(data->ti_thermal)) {
+               dev_err(bgp->dev, "thermal zone device is NULL\n");
+               return PTR_ERR(data->ti_thermal);
+       }
+       data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
+       ti_bandgap_set_sensor_data(bgp, id, data);
+
+       return 0;
+}
+
+int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
+{
+       struct ti_thermal_data *data;
+
+       data = ti_bandgap_get_sensor_data(bgp, id);
+
+       thermal_zone_device_unregister(data->ti_thermal);
+
+       return 0;
+}
+
+int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)
+{
+       struct ti_thermal_data *data;
+
+       data = ti_bandgap_get_sensor_data(bgp, id);
+
+       schedule_work(&data->thermal_wq);
+
+       return 0;
+}
+
+int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
+{
+       struct ti_thermal_data *data;
+
+       data = ti_bandgap_get_sensor_data(bgp, id);
+       if (IS_ERR_OR_NULL(data))
+               data = ti_thermal_build_data(bgp, id);
+
+       if (!data)
+               return -EINVAL;
+
+       /* Register cooling device */
+       data->cool_dev = cpufreq_cooling_register(cpu_present_mask);
+       if (IS_ERR_OR_NULL(data->cool_dev)) {
+               dev_err(bgp->dev,
+                       "Failed to register cpufreq cooling device\n");
+               return PTR_ERR(data->cool_dev);
+       }
+       ti_bandgap_set_sensor_data(bgp, id, data);
+
+       return 0;
+}
+
+int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
+{
+       struct ti_thermal_data *data;
+
+       data = ti_bandgap_get_sensor_data(bgp, id);
+       cpufreq_cooling_unregister(data->cool_dev);
+
+       return 0;
+}
similarity index 71%
rename from drivers/staging/omap-thermal/omap-thermal.h
rename to drivers/staging/ti-soc-thermal/ti-thermal.h
index 0dd2184b966364cef328e8453997cd669b54e4c6..ef6981c9436aaf35805fe66414cb31f09f9217d4 100644 (file)
  * 02110-1301 USA
  *
  */
-#ifndef __OMAP_THERMAL_H
-#define __OMAP_THERMAL_H
+#ifndef __TI_THERMAL_H
+#define __TI_THERMAL_H
 
-#include "omap-bandgap.h"
+#include "ti-bandgap.h"
 
 /* sensors gradient and offsets */
 #define OMAP_GRADIENT_SLOPE_4460                               348
 
 /* helper macros */
 /**
- * omap_thermal_get_trip_value - returns trip temperature based on index
+ * ti_thermal_get_trip_value - returns trip temperature based on index
  * @i: trip index
  */
-#define omap_thermal_get_trip_value(i)                                 \
+#define ti_thermal_get_trip_value(i)                                   \
        (OMAP_TRIP_HOT + ((i) * OMAP_TRIP_STEP))
 
 /**
- * omap_thermal_is_valid_trip - check for trip index
+ * ti_thermal_is_valid_trip - check for trip index
  * @i: trip index
  */
-#define omap_thermal_is_valid_trip(trip)                               \
+#define ti_thermal_is_valid_trip(trip)                         \
        ((trip) >= 0 && (trip) < OMAP_TRIP_NUMBER)
 
-#ifdef CONFIG_OMAP_THERMAL
-int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id,
-                              char *domain);
-int omap_thermal_remove_sensor(struct omap_bandgap *bg_ptr, int id);
-int omap_thermal_register_cpu_cooling(struct omap_bandgap *bg_ptr, int id);
-int omap_thermal_unregister_cpu_cooling(struct omap_bandgap *bg_ptr, int id);
+#ifdef CONFIG_TI_THERMAL
+int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, char *domain);
+int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id);
+int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id);
+int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id);
 #else
 static inline
-int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id,
-                              char *domain)
+int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, char *domain)
 {
        return 0;
 }
 
 static inline
-int omap_thermal_remove_sensor(struct omap_bandgap *bg_ptr, int id)
+int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
 {
        return 0;
 }
 
 static inline
-int omap_thermal_register_cpu_cooling(struct omap_bandgap *bg_ptr, int id)
+int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
 {
        return 0;
 }
 
 static inline
-int omap_thermal_unregister_cpu_cooling(struct omap_bandgap *bg_ptr, int id)
+int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
 {
        return 0;
 }
similarity index 50%
rename from drivers/staging/omap-thermal/omap_bandgap.txt
rename to drivers/staging/ti-soc-thermal/ti_soc_thermal.txt
index 6008a1452fdee4a42a8d14e0ab6a9a20ad917a06..a4a33d1a07464a75b0b00e31adcb1b5c159144ce 100644 (file)
@@ -10,21 +10,51 @@ to the silicon temperature.
 
 Required properties:
 - compatible : Should be:
-  - "ti,omap4460-control-bandgap" : for OMAP4460 bandgap
-  - "ti,omap5430-control-bandgap" : for OMAP5430 bandgap
+  - "ti,omap4430-bandgap" : for OMAP4430 bandgap
+  - "ti,omap4460-bandgap" : for OMAP4460 bandgap
+  - "ti,omap4470-bandgap" : for OMAP4470 bandgap
+  - "ti,omap5430-bandgap" : for OMAP5430 bandgap
 - interrupts : this entry should indicate which interrupt line
 the talert signal is routed to;
 Specific:
 - ti,tshut-gpio : this entry should be used to inform which GPIO
 line the tshut signal is routed to;
+- regs : this entry must also be specified and it is specific
+to each bandgap version, because the mapping may change from
+soc to soc, apart of depending on available features.
 
 Example:
+OMAP4430:
+bandgap {
+       reg = <0x4a002260 0x4 0x4a00232C 0x4>;
+       compatible = "ti,omap4430-bandgap";
+};
 
+OMAP4460:
 bandgap {
        reg = <0x4a002260 0x4
                0x4a00232C 0x4
                0x4a002378 0x18>;
-       compatible = "ti,omap4460-control-bandgap";
+       compatible = "ti,omap4460-bandgap";
        interrupts = <0 126 4>; /* talert */
        ti,tshut-gpio = <86>;
 };
+
+OMAP4470:
+bandgap {
+       reg = <0x4a002260 0x4
+               0x4a00232C 0x4
+               0x4a002378 0x18>;
+       compatible = "ti,omap4470-bandgap";
+       interrupts = <0 126 4>; /* talert */
+       ti,tshut-gpio = <86>;
+};
+
+OMAP5430:
+bandgap {
+       reg = <0x4a0021e0 0xc
+               0x4a00232c 0xc
+               0x4a002380 0x2c
+               0x4a0023C0 0x3c>;
+       compatible = "ti,omap5430-bandgap";
+};
index 0dd479f5638d8b19c98365ee3c4a3a90f6590918..5235090c39bce3dbed05d7e9137588039c33cb92 100644 (file)
@@ -5,7 +5,8 @@
 menuconfig TIDSPBRIDGE
        tristate "DSP Bridge driver"
        depends on ARCH_OMAP3
-       select OMAP_MBOX_FWK
+       select MAILBOX
+       select OMAP2PLUS_MBOX
        help
          DSP/BIOS Bridge is designed for platforms that contain a GPP and
          one or more attached DSPs.  The GPP is considered the master or
index b783bfa59b1cea42f943596f8ddce5c620a08835..f71a89de1882d0fcc6a5b963bfd1bdd200383d1d 100644 (file)
@@ -338,7 +338,7 @@ struct bridge_dev_context {
        u32 dsp_start_add;      /* API Boot vector */
        u32 internal_size;      /* Internal memory size */
 
-       struct omap_mbox *mbox;         /* Mail box handle */
+       struct mailbox *mbox;           /* Mail box handle */
 
        struct cfg_hostres *resources;  /* Host Resources */
 
index 16fa3462fbbe851cb971cf41468f0c9c80c1abae..1cae172df02ee2907edb967d87b97084c4868c5c 100644 (file)
@@ -155,7 +155,7 @@ func_cont:
         * we disable ALL DPCs. We will try to disable ONLY IO DPC later. */
        chnl_mgr_obj = pchnl->chnl_mgr_obj;
        spin_lock_bh(&chnl_mgr_obj->chnl_mgr_lock);
-       omap_mbox_disable_irq(dev_ctxt->mbox, IRQ_RX);
+       mailbox_disable_irq(dev_ctxt->mbox, IRQ_RX);
        if (pchnl->chnl_type == CHNL_PCPY) {
                /* This is a processor-copy channel. */
                if (CHNL_IS_OUTPUT(pchnl->chnl_mode)) {
@@ -210,7 +210,7 @@ func_cont:
                         IO_OUTPUT), &mb_val);
        sched_dpc = true;
 out:
-       omap_mbox_enable_irq(dev_ctxt->mbox, IRQ_RX);
+       mailbox_enable_irq(dev_ctxt->mbox, IRQ_RX);
        spin_unlock_bh(&chnl_mgr_obj->chnl_mgr_lock);
        if (mb_val != 0)
                sm_interrupt_dsp(dev_ctxt, mb_val);
@@ -570,7 +570,7 @@ int bridge_chnl_get_ioc(struct chnl_object *chnl_obj, u32 timeout,
        }
        /* See comment in AddIOReq */
        spin_lock_bh(&pchnl->chnl_mgr_obj->chnl_mgr_lock);
-       omap_mbox_disable_irq(dev_ctxt->mbox, IRQ_RX);
+       mailbox_disable_irq(dev_ctxt->mbox, IRQ_RX);
        if (dequeue_ioc) {
                /* Dequeue IOC and set chan_ioc; */
                chnl_packet_obj = list_first_entry(&pchnl->io_completions,
@@ -616,7 +616,7 @@ int bridge_chnl_get_ioc(struct chnl_object *chnl_obj, u32 timeout,
                /* else, if list is empty, ensure event is reset. */
                sync_reset_event(pchnl->sync_event);
        }
-       omap_mbox_enable_irq(dev_ctxt->mbox, IRQ_RX);
+       mailbox_enable_irq(dev_ctxt->mbox, IRQ_RX);
        spin_unlock_bh(&pchnl->chnl_mgr_obj->chnl_mgr_lock);
        if (dequeue_ioc
            && (pchnl->chnl_type == CHNL_PCPY && pchnl->chnl_id > 1)) {
index e322fb7aebe1da0de4ebf8ec3c51eddde30a6cb8..495ef98d548c5365b82f04c875d26fd947655de8 100644 (file)
@@ -908,10 +908,11 @@ func_end:
  *      Calls the Bridge's CHNL_ISR to determine if this interrupt is ours, then
  *      schedules a DPC to dispatch I/O.
  */
-int io_mbox_msg(struct notifier_block *self, unsigned long len, void *msg)
+int io_mbox_msg(struct notifier_block *self, unsigned long len, void *data)
 {
        struct io_mgr *pio_mgr;
        struct dev_object *dev_obj;
+       u32 msg = (u32)((struct mailbox_msg *)data)->pdata;
        unsigned long flags;
 
        dev_obj = dev_get_first();
@@ -920,7 +921,7 @@ int io_mbox_msg(struct notifier_block *self, unsigned long len, void *msg)
        if (!pio_mgr)
                return NOTIFY_BAD;
 
-       pio_mgr->intr_val = (u16)((u32)msg);
+       pio_mgr->intr_val = (u16)(msg);
        if (pio_mgr->intr_val & MBX_PM_CLASS)
                io_dispatch_pm(pio_mgr);
 
index f619fb3c56d298334a615eb1b34203ccd1e5f5d3..310820d783009be97aaf7fa8587b1162a7ebd054 100644 (file)
@@ -562,7 +562,7 @@ static int bridge_brd_start(struct bridge_dev_context *dev_ctxt,
                 * Enable Mailbox events and also drain any pending
                 * stale messages.
                 */
-               dev_context->mbox = omap_mbox_get("dsp", &dsp_mbox_notifier);
+               dev_context->mbox = mailbox_get("dsp", &dsp_mbox_notifier);
                if (IS_ERR(dev_context->mbox)) {
                        dev_context->mbox = NULL;
                        pr_err("%s: Failed to get dsp mailbox handle\n",
@@ -697,8 +697,8 @@ static int bridge_brd_stop(struct bridge_dev_context *dev_ctxt)
        }
        /* Disable the mailbox interrupts */
        if (dev_context->mbox) {
-               omap_mbox_disable_irq(dev_context->mbox, IRQ_RX);
-               omap_mbox_put(dev_context->mbox, &dsp_mbox_notifier);
+               mailbox_disable_irq(dev_context->mbox, IRQ_RX);
+               mailbox_put(dev_context->mbox, &dsp_mbox_notifier);
                dev_context->mbox = NULL;
        }
        /* Reset IVA2 clocks*/
index dafa6d9b294848fe6408c56a4ecdbbd6684d46a1..e50f404ccac182e5982190831def18ea9508ee2a 100644 (file)
@@ -108,7 +108,7 @@ int handle_hibernation_from_dsp(struct bridge_dev_context *dev_context)
        } else {
 
                /* Save mailbox settings */
-               omap_mbox_save_ctx(dev_context->mbox);
+               mailbox_save_ctx(dev_context->mbox);
 
                /* Turn off DSP Peripheral clocks and DSP Load monitor timer */
                status = dsp_clock_disable_all(dev_context->dsp_per_clks);
@@ -165,7 +165,7 @@ int sleep_dsp(struct bridge_dev_context *dev_context, u32 dw_cmd,
 
        switch (dev_context->brd_state) {
        case BRD_RUNNING:
-               omap_mbox_save_ctx(dev_context->mbox);
+               mailbox_save_ctx(dev_context->mbox);
                if (dsp_test_sleepstate == PWRDM_POWER_OFF) {
                        sm_interrupt_dsp(dev_context, MBX_PM_DSPHIBERNATE);
                        dev_dbg(bridge, "PM: %s - sent hibernate cmd to DSP\n",
@@ -177,7 +177,7 @@ int sleep_dsp(struct bridge_dev_context *dev_context, u32 dw_cmd,
                }
                break;
        case BRD_RETENTION:
-               omap_mbox_save_ctx(dev_context->mbox);
+               mailbox_save_ctx(dev_context->mbox);
                if (dsp_test_sleepstate == PWRDM_POWER_OFF) {
                        sm_interrupt_dsp(dev_context, MBX_PM_DSPHIBERNATE);
                        target_pwr_state = PWRDM_POWER_OFF;
index f53ed98d18c1986cc17a6753b1ed985173fc15eb..5bd57dd2e15f497fcfcafeeff253eafe613aa23f 100644 (file)
@@ -374,6 +374,7 @@ int sm_interrupt_dsp(struct bridge_dev_context *dev_context, u16 mb_val)
        struct omap_dsp_platform_data *pdata =
                omap_dspbridge_dev->dev.platform_data;
        struct cfg_hostres *resources = dev_context->resources;
+       struct mailbox_msg msg;
        int status = 0;
        u32 temp;
 
@@ -416,7 +417,7 @@ int sm_interrupt_dsp(struct bridge_dev_context *dev_context, u16 mb_val)
                                OMAP3430_IVA2_MOD, OMAP3430_CM_CLKEN_PLL);
 
                /* Restore mailbox settings */
-               omap_mbox_restore_ctx(dev_context->mbox);
+               mailbox_restore_ctx(dev_context->mbox);
 
                /* Access MMU SYS CONFIG register to generate a short wakeup */
                temp = readl(resources->dmmu_base + 0x10);
@@ -427,10 +428,12 @@ int sm_interrupt_dsp(struct bridge_dev_context *dev_context, u16 mb_val)
                dsp_clock_enable_all(dev_context->dsp_per_clks);
        }
 
-       status = omap_mbox_msg_send(dev_context->mbox, mb_val);
+       temp = mb_val;
+       MAILBOX_FILL_MSG(msg, 0, temp, 0);
+       status = mailbox_msg_send(dev_context->mbox, &msg);
 
        if (status) {
-               pr_err("omap_mbox_msg_send Fail and status = %d\n", status);
+               pr_err("mailbox_msg_send Fail and status = %d\n", status);
                status = -EPERM;
        }
 
index 7f3a1db316199ac4618d3e57a6162b27ed305282..27f6bd6114bcfa65622f7ef866e6a30b2725aa50 100644 (file)
@@ -41,7 +41,7 @@
 #include <linux/ioport.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
-#include <plat/mailbox.h>
+#include <linux/mailbox.h>
 #include <linux/pagemap.h>
 #include <asm/cacheflush.h>
 #include <linux/dma-mapping.h>
index c2c77d1ac499d3ccb4d4c141d66986e88e980bf6..a764f165b58930da5044c208693a86a817bab93f 100644 (file)
@@ -29,14 +29,14 @@ choice
 
 config THERMAL_DEFAULT_GOV_STEP_WISE
        bool "step_wise"
-       select STEP_WISE
+       select THERMAL_GOV_STEP_WISE
        help
          Use the step_wise governor as default. This throttles the
          devices one step at a time.
 
 config THERMAL_DEFAULT_GOV_FAIR_SHARE
        bool "fair_share"
-       select FAIR_SHARE
+       select THERMAL_GOV_FAIR_SHARE
        help
          Use the fair_share governor as default. This throttles the
          devices based on their 'contribution' to a zone. The
@@ -44,24 +44,24 @@ config THERMAL_DEFAULT_GOV_FAIR_SHARE
 
 config THERMAL_DEFAULT_GOV_USER_SPACE
        bool "user_space"
-       select USER_SPACE
+       select THERMAL_GOV_USER_SPACE
        help
          Select this if you want to let the user space manage the
          lpatform thermals.
 
 endchoice
 
-config FAIR_SHARE
+config THERMAL_GOV_FAIR_SHARE
        bool "Fair-share thermal governor"
        help
          Enable this to manage platform thermals using fair-share governor.
 
-config STEP_WISE
+config THERMAL_GOV_STEP_WISE
        bool "Step_wise thermal governor"
        help
          Enable this to manage platform thermals using a simple linear
 
-config USER_SPACE
+config THERMAL_GOV_USER_SPACE
        bool "User_space thermal governor"
        help
          Enable this to let the user space manage the platform thermals.
@@ -78,6 +78,14 @@ config CPU_THERMAL
          and not the ACPI interface.
          If you want this support, you should say Y here.
 
+config THERMAL_EMULATION
+       bool "Thermal emulation mode support"
+       help
+         Enable this option to make a emul_temp sysfs node in thermal zone
+         directory to support temperature emulation. With emulation sysfs node,
+         user can manually input temperature and test the different trip
+         threshold behaviour for simulation purpose.
+
 config SPEAR_THERMAL
        bool "SPEAr thermal sensor driver"
        depends on PLAT_SPEAR
@@ -93,6 +101,14 @@ config RCAR_THERMAL
          Enable this to plug the R-Car thermal sensor driver into the Linux
          thermal framework
 
+config KIRKWOOD_THERMAL
+       tristate "Temperature sensor on Marvell Kirkwood SoCs"
+       depends on ARCH_KIRKWOOD
+       depends on OF
+       help
+         Support for the Kirkwood thermal sensor driver into the Linux thermal
+         framework. Only kirkwood 88F6282 and 88F6283 have this sensor.
+
 config EXYNOS_THERMAL
        tristate "Temperature sensor on Samsung EXYNOS"
        depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
@@ -101,6 +117,23 @@ config EXYNOS_THERMAL
          If you say yes here you get support for TMU (Thermal Management
          Unit) on SAMSUNG EXYNOS series of SoC.
 
+config EXYNOS_THERMAL_EMUL
+       bool "EXYNOS TMU emulation mode support"
+       depends on EXYNOS_THERMAL
+       help
+         Exynos 4412 and 4414 and 5 series has emulation mode on TMU.
+         Enable this option will be make sysfs node in exynos thermal platform
+         device directory to support emulation mode. With emulation mode sysfs
+         node, you can manually input temperature to TMU for simulation purpose.
+
+config DOVE_THERMAL
+       tristate "Temperature sensor on Marvell Dove SoCs"
+       depends on ARCH_DOVE
+       depends on OF
+       help
+         Support for the Dove thermal sensor driver in the Linux thermal
+         framework.
+
 config DB8500_THERMAL
        bool "DB8500 thermal management"
        depends on ARCH_U8500
@@ -122,4 +155,14 @@ config DB8500_CPUFREQ_COOLING
          bound cpufreq cooling device turns active to set CPU frequency low to
          cool down the CPU.
 
+config INTEL_POWERCLAMP
+       tristate "Intel PowerClamp idle injection driver"
+       depends on THERMAL
+       depends on X86
+       depends on CPU_SUP_INTEL
+       help
+         Enable this to enable Intel PowerClamp idle injection driver. This
+         enforce idle time which results in more package C-state residency. The
+         user interface is exposed via generic thermal framework.
+
 endif
index d8da683245fce70065aa3b707dc5d2a8b14447b9..d3a2b38c31e86b694df71a4ccbb2b366025fb317 100644 (file)
@@ -5,9 +5,9 @@
 obj-$(CONFIG_THERMAL)          += thermal_sys.o
 
 # governors
-obj-$(CONFIG_FAIR_SHARE)       += fair_share.o
-obj-$(CONFIG_STEP_WISE)                += step_wise.o
-obj-$(CONFIG_USER_SPACE)       += user_space.o
+obj-$(CONFIG_THERMAL_GOV_FAIR_SHARE)   += fair_share.o
+obj-$(CONFIG_THERMAL_GOV_STEP_WISE)    += step_wise.o
+obj-$(CONFIG_THERMAL_GOV_USER_SPACE)   += user_space.o
 
 # cpufreq cooling
 obj-$(CONFIG_CPU_THERMAL)      += cpu_cooling.o
@@ -15,6 +15,10 @@ obj-$(CONFIG_CPU_THERMAL)    += cpu_cooling.o
 # platform thermal drivers
 obj-$(CONFIG_SPEAR_THERMAL)    += spear_thermal.o
 obj-$(CONFIG_RCAR_THERMAL)     += rcar_thermal.o
+obj-$(CONFIG_KIRKWOOD_THERMAL)  += kirkwood_thermal.o
 obj-$(CONFIG_EXYNOS_THERMAL)   += exynos_thermal.o
+obj-$(CONFIG_DOVE_THERMAL)     += dove_thermal.o
 obj-$(CONFIG_DB8500_THERMAL)   += db8500_thermal.o
 obj-$(CONFIG_DB8500_CPUFREQ_COOLING)   += db8500_cpufreq_cooling.o
+obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
+
index 836828e29a87982e2828949e4e2daea532bb7ed9..455c77a961dec5119048fe7b687bc5cb100ed3f5 100644 (file)
@@ -118,8 +118,8 @@ static int is_cpufreq_valid(int cpu)
 /**
  * get_cpu_frequency - get the absolute value of frequency from level.
  * @cpu: cpu for which frequency is fetched.
- * @level: level of frequency of the CPU
- *     e.g level=1 --> 1st MAX FREQ, LEVEL=2 ---> 2nd MAX FREQ, .... etc
+ * @level: level of frequency, equals cooling state of cpu cooling device
+ *     e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc
  */
 static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level)
 {
index 4cf8e72af90a351687da093d7b80bc6678745153..21419851fc0284a5447649580edb007ff079b471 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/cpufreq.h>
 #include <linux/err.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
@@ -73,15 +74,13 @@ static const struct of_device_id db8500_cpufreq_cooling_match[] = {
        { .compatible = "stericsson,db8500-cpufreq-cooling" },
        {},
 };
-#else
-#define db8500_cpufreq_cooling_match NULL
 #endif
 
 static struct platform_driver db8500_cpufreq_cooling_driver = {
        .driver = {
                .owner = THIS_MODULE,
                .name = "db8500-cpufreq-cooling",
-               .of_match_table = db8500_cpufreq_cooling_match,
+               .of_match_table = of_match_ptr(db8500_cpufreq_cooling_match),
        },
        .probe = db8500_cpufreq_cooling_probe,
        .suspend = db8500_cpufreq_cooling_suspend,
index ec71ade3e3174491df515a33bd0c3b25e7f0c818..61ce60a35921d8ca543bcc275a5b7d479b98fa4f 100644 (file)
@@ -508,15 +508,13 @@ static const struct of_device_id db8500_thermal_match[] = {
        { .compatible = "stericsson,db8500-thermal" },
        {},
 };
-#else
-#define db8500_thermal_match NULL
 #endif
 
 static struct platform_driver db8500_thermal_driver = {
        .driver = {
                .owner = THIS_MODULE,
                .name = "db8500-thermal",
-               .of_match_table = db8500_thermal_match,
+               .of_match_table = of_match_ptr(db8500_thermal_match),
        },
        .probe = db8500_thermal_probe,
        .suspend = db8500_thermal_suspend,
diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c
new file mode 100644 (file)
index 0000000..7b0bfa0
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Dove thermal sensor driver
+ *
+ * Copyright (C) 2013 Andrew Lunn <andrew@lunn.ch>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+
+#define DOVE_THERMAL_TEMP_OFFSET       1
+#define DOVE_THERMAL_TEMP_MASK         0x1FF
+
+/* Dove Thermal Manager Control and Status Register */
+#define PMU_TM_DISABLE_OFFS            0
+#define PMU_TM_DISABLE_MASK            (0x1 << PMU_TM_DISABLE_OFFS)
+
+/* Dove Theraml Diode Control 0 Register */
+#define PMU_TDC0_SW_RST_MASK           (0x1 << 1)
+#define PMU_TDC0_SEL_VCAL_OFFS         5
+#define PMU_TDC0_SEL_VCAL_MASK         (0x3 << PMU_TDC0_SEL_VCAL_OFFS)
+#define PMU_TDC0_REF_CAL_CNT_OFFS      11
+#define PMU_TDC0_REF_CAL_CNT_MASK      (0x1FF << PMU_TDC0_REF_CAL_CNT_OFFS)
+#define PMU_TDC0_AVG_NUM_OFFS          25
+#define PMU_TDC0_AVG_NUM_MASK          (0x7 << PMU_TDC0_AVG_NUM_OFFS)
+
+/* Dove Thermal Diode Control 1 Register */
+#define PMU_TEMP_DIOD_CTRL1_REG                0x04
+#define PMU_TDC1_TEMP_VALID_MASK       (0x1 << 10)
+
+/* Dove Thermal Sensor Dev Structure */
+struct dove_thermal_priv {
+       void __iomem *sensor;
+       void __iomem *control;
+};
+
+static int dove_init_sensor(const struct dove_thermal_priv *priv)
+{
+       u32 reg;
+       u32 i;
+
+       /* Configure the Diode Control Register #0 */
+       reg = readl_relaxed(priv->control);
+
+       /* Use average of 2 */
+       reg &= ~PMU_TDC0_AVG_NUM_MASK;
+       reg |= (0x1 << PMU_TDC0_AVG_NUM_OFFS);
+
+       /* Reference calibration value */
+       reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
+       reg |= (0x0F1 << PMU_TDC0_REF_CAL_CNT_OFFS);
+
+       /* Set the high level reference for calibration */
+       reg &= ~PMU_TDC0_SEL_VCAL_MASK;
+       reg |= (0x2 << PMU_TDC0_SEL_VCAL_OFFS);
+       writel(reg, priv->control);
+
+       /* Reset the sensor */
+       reg = readl_relaxed(priv->control);
+       writel((reg | PMU_TDC0_SW_RST_MASK), priv->control);
+       writel(reg, priv->control);
+
+       /* Enable the sensor */
+       reg = readl_relaxed(priv->sensor);
+       reg &= ~PMU_TM_DISABLE_MASK;
+       writel(reg, priv->sensor);
+
+       /* Poll the sensor for the first reading */
+       for (i = 0; i < 1000000; i++) {
+               reg = readl_relaxed(priv->sensor);
+               if (reg & DOVE_THERMAL_TEMP_MASK)
+                       break;
+       }
+
+       if (i == 1000000)
+               return -EIO;
+
+       return 0;
+}
+
+static int dove_get_temp(struct thermal_zone_device *thermal,
+                         unsigned long *temp)
+{
+       unsigned long reg;
+       struct dove_thermal_priv *priv = thermal->devdata;
+
+       /* Valid check */
+       reg = readl_relaxed(priv->control + PMU_TEMP_DIOD_CTRL1_REG);
+       if ((reg & PMU_TDC1_TEMP_VALID_MASK) == 0x0) {
+               dev_err(&thermal->device,
+                       "Temperature sensor reading not valid\n");
+               return -EIO;
+       }
+
+       /*
+        * Calculate temperature. See Section 8.10.1 of 88AP510,
+        * Documentation/arm/Marvell/README
+        */
+       reg = readl_relaxed(priv->sensor);
+       reg = (reg >> DOVE_THERMAL_TEMP_OFFSET) & DOVE_THERMAL_TEMP_MASK;
+       *temp = ((2281638UL - (7298*reg)) / 10);
+
+       return 0;
+}
+
+static struct thermal_zone_device_ops ops = {
+       .get_temp = dove_get_temp,
+};
+
+static const struct of_device_id dove_thermal_id_table[] = {
+       { .compatible = "marvell,dove-thermal" },
+       {}
+};
+
+static int dove_thermal_probe(struct platform_device *pdev)
+{
+       struct thermal_zone_device *thermal = NULL;
+       struct dove_thermal_priv *priv;
+       struct resource *res;
+       int ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "Failed to get platform resource\n");
+               return -ENODEV;
+       }
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->sensor = devm_request_and_ioremap(&pdev->dev, res);
+       if (!priv->sensor) {
+               dev_err(&pdev->dev, "Failed to request_ioremap memory\n");
+               return -EADDRNOTAVAIL;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!res) {
+               dev_err(&pdev->dev, "Failed to get platform resource\n");
+               return -ENODEV;
+       }
+       priv->control = devm_request_and_ioremap(&pdev->dev, res);
+       if (!priv->control) {
+               dev_err(&pdev->dev, "Failed to request_ioremap memory\n");
+               return -EADDRNOTAVAIL;
+       }
+
+       ret = dove_init_sensor(priv);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to initialize sensor\n");
+               return ret;
+       }
+
+       thermal = thermal_zone_device_register("dove_thermal", 0, 0,
+                                              priv, &ops, NULL, 0, 0);
+       if (IS_ERR(thermal)) {
+               dev_err(&pdev->dev,
+                       "Failed to register thermal zone device\n");
+               return PTR_ERR(thermal);
+       }
+
+       platform_set_drvdata(pdev, thermal);
+
+       return 0;
+}
+
+static int dove_thermal_exit(struct platform_device *pdev)
+{
+       struct thermal_zone_device *dove_thermal =
+               platform_get_drvdata(pdev);
+
+       thermal_zone_device_unregister(dove_thermal);
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+MODULE_DEVICE_TABLE(of, dove_thermal_id_table);
+
+static struct platform_driver dove_thermal_driver = {
+       .probe = dove_thermal_probe,
+       .remove = dove_thermal_exit,
+       .driver = {
+               .name = "dove_thermal",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(dove_thermal_id_table),
+       },
+};
+
+module_platform_driver(dove_thermal_driver);
+
+MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
+MODULE_DESCRIPTION("Dove thermal driver");
+MODULE_LICENSE("GPL");
index 224751e9f5ff3659448796da7d5323ec738f0770..52d909a924d999188e951f4c50970c5f7158f168 100644 (file)
@@ -82,7 +82,7 @@
 
 #define EXYNOS_TRIMINFO_RELOAD         0x1
 #define EXYNOS_TMU_CLEAR_RISE_INT      0x111
-#define EXYNOS_TMU_CLEAR_FALL_INT      (0x111 << 16)
+#define EXYNOS_TMU_CLEAR_FALL_INT      (0x111 << 12)
 #define EXYNOS_MUX_ADDR_VALUE          6
 #define EXYNOS_MUX_ADDR_SHIFT          20
 #define EXYNOS_TMU_TRIP_MODE_SHIFT     13
 #define SENSOR_NAME_LEN        16
 #define MAX_TRIP_COUNT 8
 #define MAX_COOLING_DEVICE 4
+#define MAX_THRESHOLD_LEVS 4
 
 #define ACTIVE_INTERVAL 500
 #define IDLE_INTERVAL 10000
 #define MCELSIUS       1000
 
+#ifdef CONFIG_EXYNOS_THERMAL_EMUL
+#define EXYNOS_EMUL_TIME       0x57F0
+#define EXYNOS_EMUL_TIME_SHIFT 16
+#define EXYNOS_EMUL_DATA_SHIFT 8
+#define EXYNOS_EMUL_DATA_MASK  0xFF
+#define EXYNOS_EMUL_ENABLE     0x1
+#endif /* CONFIG_EXYNOS_THERMAL_EMUL */
+
 /* CPU Zone information */
 #define PANIC_ZONE      4
 #define WARN_ZONE       3
@@ -125,6 +134,7 @@ struct exynos_tmu_data {
 struct thermal_trip_point_conf {
        int trip_val[MAX_TRIP_COUNT];
        int trip_count;
+       u8 trigger_falling;
 };
 
 struct thermal_cooling_conf {
@@ -174,7 +184,8 @@ static int exynos_set_mode(struct thermal_zone_device *thermal,
 
        mutex_lock(&th_zone->therm_dev->lock);
 
-       if (mode == THERMAL_DEVICE_ENABLED)
+       if (mode == THERMAL_DEVICE_ENABLED &&
+               !th_zone->sensor_conf->trip_data.trigger_falling)
                th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
        else
                th_zone->therm_dev->polling_delay = 0;
@@ -284,7 +295,7 @@ static int exynos_bind(struct thermal_zone_device *thermal,
                case MONITOR_ZONE:
                case WARN_ZONE:
                        if (thermal_zone_bind_cooling_device(thermal, i, cdev,
-                                                               level, level)) {
+                                                               level, 0)) {
                                pr_err("error binding cdev inst %d\n", i);
                                ret = -EINVAL;
                        }
@@ -362,10 +373,17 @@ static int exynos_get_temp(struct thermal_zone_device *thermal,
 static int exynos_get_trend(struct thermal_zone_device *thermal,
                        int trip, enum thermal_trend *trend)
 {
-       if (thermal->temperature >= trip)
-               *trend = THERMAL_TREND_RAISING;
+       int ret;
+       unsigned long trip_temp;
+
+       ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
+       if (ret < 0)
+               return ret;
+
+       if (thermal->temperature >= trip_temp)
+               *trend = THERMAL_TREND_RAISE_FULL;
        else
-               *trend = THERMAL_TREND_DROPPING;
+               *trend = THERMAL_TREND_DROP_FULL;
 
        return 0;
 }
@@ -413,7 +431,8 @@ static void exynos_report_trigger(void)
                        break;
        }
 
-       if (th_zone->mode == THERMAL_DEVICE_ENABLED) {
+       if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
+               !th_zone->sensor_conf->trip_data.trigger_falling) {
                if (i > 0)
                        th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
                else
@@ -452,7 +471,8 @@ static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
 
        th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
                        EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0,
-                       IDLE_INTERVAL);
+                       sensor_conf->trip_data.trigger_falling ?
+                       0 : IDLE_INTERVAL);
 
        if (IS_ERR(th_zone->therm_dev)) {
                pr_err("Failed to register thermal zone device\n");
@@ -559,8 +579,9 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
 {
        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
        struct exynos_tmu_platform_data *pdata = data->pdata;
-       unsigned int status, trim_info, rising_threshold;
-       int ret = 0, threshold_code;
+       unsigned int status, trim_info;
+       unsigned int rising_threshold = 0, falling_threshold = 0;
+       int ret = 0, threshold_code, i, trigger_levs = 0;
 
        mutex_lock(&data->lock);
        clk_enable(data->clk);
@@ -585,6 +606,11 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
                        (data->temp_error2 != 0))
                data->temp_error1 = pdata->efuse_value;
 
+       /* Count trigger levels to be enabled */
+       for (i = 0; i < MAX_THRESHOLD_LEVS; i++)
+               if (pdata->trigger_levels[i])
+                       trigger_levs++;
+
        if (data->soc == SOC_ARCH_EXYNOS4210) {
                /* Write temperature code for threshold */
                threshold_code = temp_to_code(data, pdata->threshold);
@@ -594,44 +620,38 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
                }
                writeb(threshold_code,
                        data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
-
-               writeb(pdata->trigger_levels[0],
-                       data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0);
-               writeb(pdata->trigger_levels[1],
-                       data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL1);
-               writeb(pdata->trigger_levels[2],
-                       data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL2);
-               writeb(pdata->trigger_levels[3],
-                       data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL3);
+               for (i = 0; i < trigger_levs; i++)
+                       writeb(pdata->trigger_levels[i],
+                       data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4);
 
                writel(EXYNOS4210_TMU_INTCLEAR_VAL,
                        data->base + EXYNOS_TMU_REG_INTCLEAR);
        } else if (data->soc == SOC_ARCH_EXYNOS) {
-               /* Write temperature code for threshold */
-               threshold_code = temp_to_code(data, pdata->trigger_levels[0]);
-               if (threshold_code < 0) {
-                       ret = threshold_code;
-                       goto out;
-               }
-               rising_threshold = threshold_code;
-               threshold_code = temp_to_code(data, pdata->trigger_levels[1]);
-               if (threshold_code < 0) {
-                       ret = threshold_code;
-                       goto out;
-               }
-               rising_threshold |= (threshold_code << 8);
-               threshold_code = temp_to_code(data, pdata->trigger_levels[2]);
-               if (threshold_code < 0) {
-                       ret = threshold_code;
-                       goto out;
+               /* Write temperature code for rising and falling threshold */
+               for (i = 0; i < trigger_levs; i++) {
+                       threshold_code = temp_to_code(data,
+                                               pdata->trigger_levels[i]);
+                       if (threshold_code < 0) {
+                               ret = threshold_code;
+                               goto out;
+                       }
+                       rising_threshold |= threshold_code << 8 * i;
+                       if (pdata->threshold_falling) {
+                               threshold_code = temp_to_code(data,
+                                               pdata->trigger_levels[i] -
+                                               pdata->threshold_falling);
+                               if (threshold_code > 0)
+                                       falling_threshold |=
+                                               threshold_code << 8 * i;
+                       }
                }
-               rising_threshold |= (threshold_code << 16);
 
                writel(rising_threshold,
                                data->base + EXYNOS_THD_TEMP_RISE);
-               writel(0, data->base + EXYNOS_THD_TEMP_FALL);
+               writel(falling_threshold,
+                               data->base + EXYNOS_THD_TEMP_FALL);
 
-               writel(EXYNOS_TMU_CLEAR_RISE_INT|EXYNOS_TMU_CLEAR_FALL_INT,
+               writel(EXYNOS_TMU_CLEAR_RISE_INT | EXYNOS_TMU_CLEAR_FALL_INT,
                                data->base + EXYNOS_TMU_REG_INTCLEAR);
        }
 out:
@@ -664,6 +684,8 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
                        pdata->trigger_level2_en << 8 |
                        pdata->trigger_level1_en << 4 |
                        pdata->trigger_level0_en;
+               if (pdata->threshold_falling)
+                       interrupt_en |= interrupt_en << 16;
        } else {
                con |= EXYNOS_TMU_CORE_OFF;
                interrupt_en = 0; /* Disable all interrupts */
@@ -697,20 +719,19 @@ static void exynos_tmu_work(struct work_struct *work)
        struct exynos_tmu_data *data = container_of(work,
                        struct exynos_tmu_data, irq_work);
 
+       exynos_report_trigger();
        mutex_lock(&data->lock);
        clk_enable(data->clk);
-
-
        if (data->soc == SOC_ARCH_EXYNOS)
-               writel(EXYNOS_TMU_CLEAR_RISE_INT,
+               writel(EXYNOS_TMU_CLEAR_RISE_INT |
+                               EXYNOS_TMU_CLEAR_FALL_INT,
                                data->base + EXYNOS_TMU_REG_INTCLEAR);
        else
                writel(EXYNOS4210_TMU_INTCLEAR_VAL,
                                data->base + EXYNOS_TMU_REG_INTCLEAR);
-
        clk_disable(data->clk);
        mutex_unlock(&data->lock);
-       exynos_report_trigger();
+
        enable_irq(data->irq);
 }
 
@@ -759,6 +780,7 @@ static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = {
 
 #if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412)
 static struct exynos_tmu_platform_data const exynos_default_tmu_data = {
+       .threshold_falling = 10,
        .trigger_levels[0] = 85,
        .trigger_levels[1] = 103,
        .trigger_levels[2] = 110,
@@ -800,8 +822,6 @@ static const struct of_device_id exynos_tmu_match[] = {
        {},
 };
 MODULE_DEVICE_TABLE(of, exynos_tmu_match);
-#else
-#define  exynos_tmu_match NULL
 #endif
 
 static struct platform_device_id exynos_tmu_driver_ids[] = {
@@ -832,7 +852,95 @@ static inline struct  exynos_tmu_platform_data *exynos_get_driver_data(
        return (struct exynos_tmu_platform_data *)
                        platform_get_device_id(pdev)->driver_data;
 }
-static int exynos_tmu_probe(struct platform_device *pdev)
+
+#ifdef CONFIG_EXYNOS_THERMAL_EMUL
+static ssize_t exynos_tmu_emulation_show(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct platform_device *pdev = container_of(dev,
+                                       struct platform_device, dev);
+       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+       unsigned int reg;
+       u8 temp_code;
+       int temp = 0;
+
+       if (data->soc == SOC_ARCH_EXYNOS4210)
+               goto out;
+
+       mutex_lock(&data->lock);
+       clk_enable(data->clk);
+       reg = readl(data->base + EXYNOS_EMUL_CON);
+       clk_disable(data->clk);
+       mutex_unlock(&data->lock);
+
+       if (reg & EXYNOS_EMUL_ENABLE) {
+               reg >>= EXYNOS_EMUL_DATA_SHIFT;
+               temp_code = reg & EXYNOS_EMUL_DATA_MASK;
+               temp = code_to_temp(data, temp_code);
+       }
+out:
+       return sprintf(buf, "%d\n", temp * MCELSIUS);
+}
+
+static ssize_t exynos_tmu_emulation_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       struct platform_device *pdev = container_of(dev,
+                                       struct platform_device, dev);
+       struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+       unsigned int reg;
+       int temp;
+
+       if (data->soc == SOC_ARCH_EXYNOS4210)
+               goto out;
+
+       if (!sscanf(buf, "%d\n", &temp) || temp < 0)
+               return -EINVAL;
+
+       mutex_lock(&data->lock);
+       clk_enable(data->clk);
+
+       reg = readl(data->base + EXYNOS_EMUL_CON);
+
+       if (temp) {
+               /* Both CELSIUS and MCELSIUS type are available for input */
+               if (temp > MCELSIUS)
+                       temp /= MCELSIUS;
+
+               reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
+                       (temp_to_code(data, (temp / MCELSIUS))
+                        << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE;
+       } else {
+               reg &= ~EXYNOS_EMUL_ENABLE;
+       }
+
+       writel(reg, data->base + EXYNOS_EMUL_CON);
+
+       clk_disable(data->clk);
+       mutex_unlock(&data->lock);
+
+out:
+       return count;
+}
+
+static DEVICE_ATTR(emulation, 0644, exynos_tmu_emulation_show,
+                                       exynos_tmu_emulation_store);
+static int create_emulation_sysfs(struct device *dev)
+{
+       return device_create_file(dev, &dev_attr_emulation);
+}
+static void remove_emulation_sysfs(struct device *dev)
+{
+       device_remove_file(dev, &dev_attr_emulation);
+}
+#else
+static inline int create_emulation_sysfs(struct device *dev) { return 0; }
+static inline void remove_emulation_sysfs(struct device *dev) {}
+#endif
+
+static int __devinit exynos_tmu_probe(struct platform_device *pdev)
 {
        struct exynos_tmu_data *data;
        struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data;
@@ -916,6 +1024,8 @@ static int exynos_tmu_probe(struct platform_device *pdev)
                exynos_sensor_conf.trip_data.trip_val[i] =
                        pdata->threshold + pdata->trigger_levels[i];
 
+       exynos_sensor_conf.trip_data.trigger_falling = pdata->threshold_falling;
+
        exynos_sensor_conf.cooling_data.freq_clip_count =
                                                pdata->freq_tab_count;
        for (i = 0; i < pdata->freq_tab_count; i++) {
@@ -930,6 +1040,11 @@ static int exynos_tmu_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "Failed to register thermal interface\n");
                goto err_clk;
        }
+
+       ret = create_emulation_sysfs(&pdev->dev);
+       if (ret)
+               dev_err(&pdev->dev, "Failed to create emulation mode sysfs node\n");
+
        return 0;
 err_clk:
        platform_set_drvdata(pdev, NULL);
@@ -941,6 +1056,8 @@ static int exynos_tmu_remove(struct platform_device *pdev)
 {
        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
 
+       remove_emulation_sysfs(&pdev->dev);
+
        exynos_tmu_control(pdev, false);
 
        exynos_unregister_thermal();
@@ -982,7 +1099,7 @@ static struct platform_driver exynos_tmu_driver = {
                .name   = "exynos-tmu",
                .owner  = THIS_MODULE,
                .pm     = EXYNOS_TMU_PM,
-               .of_match_table = exynos_tmu_match,
+               .of_match_table = of_match_ptr(exynos_tmu_match),
        },
        .probe = exynos_tmu_probe,
        .remove = exynos_tmu_remove,
diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c
new file mode 100644 (file)
index 0000000..b40b37c
--- /dev/null
@@ -0,0 +1,795 @@
+/*
+ * intel_powerclamp.c - package c-state idle injection
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * Authors:
+ *     Arjan van de Ven <arjan@linux.intel.com>
+ *     Jacob Pan <jacob.jun.pan@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ *     TODO:
+ *           1. better handle wakeup from external interrupts, currently a fixed
+ *              compensation is added to clamping duration when excessive amount
+ *              of wakeups are observed during idle time. the reason is that in
+ *              case of external interrupts without need for ack, clamping down
+ *              cpu in non-irq context does not reduce irq. for majority of the
+ *              cases, clamping down cpu does help reduce irq as well, we should
+ *              be able to differenciate the two cases and give a quantitative
+ *              solution for the irqs that we can control. perhaps based on
+ *              get_cpu_iowait_time_us()
+ *
+ *          2. synchronization with other hw blocks
+ *
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/cpu.h>
+#include <linux/thermal.h>
+#include <linux/slab.h>
+#include <linux/tick.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/sched/rt.h>
+
+#include <asm/nmi.h>
+#include <asm/msr.h>
+#include <asm/mwait.h>
+#include <asm/cpu_device_id.h>
+#include <asm/idle.h>
+#include <asm/hardirq.h>
+
+#define MAX_TARGET_RATIO (50U)
+/* For each undisturbed clamping period (no extra wake ups during idle time),
+ * we increment the confidence counter for the given target ratio.
+ * CONFIDENCE_OK defines the level where runtime calibration results are
+ * valid.
+ */
+#define CONFIDENCE_OK (3)
+/* Default idle injection duration, driver adjust sleep time to meet target
+ * idle ratio. Similar to frequency modulation.
+ */
+#define DEFAULT_DURATION_JIFFIES (6)
+
+static unsigned int target_mwait;
+static struct dentry *debug_dir;
+
+/* user selected target */
+static unsigned int set_target_ratio;
+static unsigned int current_ratio;
+static bool should_skip;
+static bool reduce_irq;
+static atomic_t idle_wakeup_counter;
+static unsigned int control_cpu; /* The cpu assigned to collect stat and update
+                                 * control parameters. default to BSP but BSP
+                                 * can be offlined.
+                                 */
+static bool clamping;
+
+
+static struct task_struct * __percpu *powerclamp_thread;
+static struct thermal_cooling_device *cooling_dev;
+static unsigned long *cpu_clamping_mask;  /* bit map for tracking per cpu
+                                          * clamping thread
+                                          */
+
+static unsigned int duration;
+static unsigned int pkg_cstate_ratio_cur;
+static unsigned int window_size;
+
+static int duration_set(const char *arg, const struct kernel_param *kp)
+{
+       int ret = 0;
+       unsigned long new_duration;
+
+       ret = kstrtoul(arg, 10, &new_duration);
+       if (ret)
+               goto exit;
+       if (new_duration > 25 || new_duration < 6) {
+               pr_err("Out of recommended range %lu, between 6-25ms\n",
+                       new_duration);
+               ret = -EINVAL;
+       }
+
+       duration = clamp(new_duration, 6ul, 25ul);
+       smp_mb();
+
+exit:
+
+       return ret;
+}
+
+static struct kernel_param_ops duration_ops = {
+       .set = duration_set,
+       .get = param_get_int,
+};
+
+
+module_param_cb(duration, &duration_ops, &duration, 0644);
+MODULE_PARM_DESC(duration, "forced idle time for each attempt in msec.");
+
+struct powerclamp_calibration_data {
+       unsigned long confidence;  /* used for calibration, basically a counter
+                                   * gets incremented each time a clamping
+                                   * period is completed without extra wakeups
+                                   * once that counter is reached given level,
+                                   * compensation is deemed usable.
+                                   */
+       unsigned long steady_comp; /* steady state compensation used when
+                                   * no extra wakeups occurred.
+                                   */
+       unsigned long dynamic_comp; /* compensate excessive wakeup from idle
+                                    * mostly from external interrupts.
+                                    */
+};
+
+static struct powerclamp_calibration_data cal_data[MAX_TARGET_RATIO];
+
+static int window_size_set(const char *arg, const struct kernel_param *kp)
+{
+       int ret = 0;
+       unsigned long new_window_size;
+
+       ret = kstrtoul(arg, 10, &new_window_size);
+       if (ret)
+               goto exit_win;
+       if (new_window_size > 10 || new_window_size < 2) {
+               pr_err("Out of recommended window size %lu, between 2-10\n",
+                       new_window_size);
+               ret = -EINVAL;
+       }
+
+       window_size = clamp(new_window_size, 2ul, 10ul);
+       smp_mb();
+
+exit_win:
+
+       return ret;
+}
+
+static struct kernel_param_ops window_size_ops = {
+       .set = window_size_set,
+       .get = param_get_int,
+};
+
+module_param_cb(window_size, &window_size_ops, &window_size, 0644);
+MODULE_PARM_DESC(window_size, "sliding window in number of clamping cycles\n"
+       "\tpowerclamp controls idle ratio within this window. larger\n"
+       "\twindow size results in slower response time but more smooth\n"
+       "\tclamping results. default to 2.");
+
+static void find_target_mwait(void)
+{
+       unsigned int eax, ebx, ecx, edx;
+       unsigned int highest_cstate = 0;
+       unsigned int highest_subcstate = 0;
+       int i;
+
+       if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
+               return;
+
+       cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx);
+
+       if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) ||
+           !(ecx & CPUID5_ECX_INTERRUPT_BREAK))
+               return;
+
+       edx >>= MWAIT_SUBSTATE_SIZE;
+       for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) {
+               if (edx & MWAIT_SUBSTATE_MASK) {
+                       highest_cstate = i;
+                       highest_subcstate = edx & MWAIT_SUBSTATE_MASK;
+               }
+       }
+       target_mwait = (highest_cstate << MWAIT_SUBSTATE_SIZE) |
+               (highest_subcstate - 1);
+
+}
+
+static u64 pkg_state_counter(void)
+{
+       u64 val;
+       u64 count = 0;
+
+       static bool skip_c2;
+       static bool skip_c3;
+       static bool skip_c6;
+       static bool skip_c7;
+
+       if (!skip_c2) {
+               if (!rdmsrl_safe(MSR_PKG_C2_RESIDENCY, &val))
+                       count += val;
+               else
+                       skip_c2 = true;
+       }
+
+       if (!skip_c3) {
+               if (!rdmsrl_safe(MSR_PKG_C3_RESIDENCY, &val))
+                       count += val;
+               else
+                       skip_c3 = true;
+       }
+
+       if (!skip_c6) {
+               if (!rdmsrl_safe(MSR_PKG_C6_RESIDENCY, &val))
+                       count += val;
+               else
+                       skip_c6 = true;
+       }
+
+       if (!skip_c7) {
+               if (!rdmsrl_safe(MSR_PKG_C7_RESIDENCY, &val))
+                       count += val;
+               else
+                       skip_c7 = true;
+       }
+
+       return count;
+}
+
+static void noop_timer(unsigned long foo)
+{
+       /* empty... just the fact that we get the interrupt wakes us up */
+}
+
+static unsigned int get_compensation(int ratio)
+{
+       unsigned int comp = 0;
+
+       /* we only use compensation if all adjacent ones are good */
+       if (ratio == 1 &&
+               cal_data[ratio].confidence >= CONFIDENCE_OK &&
+               cal_data[ratio + 1].confidence >= CONFIDENCE_OK &&
+               cal_data[ratio + 2].confidence >= CONFIDENCE_OK) {
+               comp = (cal_data[ratio].steady_comp +
+                       cal_data[ratio + 1].steady_comp +
+                       cal_data[ratio + 2].steady_comp) / 3;
+       } else if (ratio == MAX_TARGET_RATIO - 1 &&
+               cal_data[ratio].confidence >= CONFIDENCE_OK &&
+               cal_data[ratio - 1].confidence >= CONFIDENCE_OK &&
+               cal_data[ratio - 2].confidence >= CONFIDENCE_OK) {
+               comp = (cal_data[ratio].steady_comp +
+                       cal_data[ratio - 1].steady_comp +
+                       cal_data[ratio - 2].steady_comp) / 3;
+       } else if (cal_data[ratio].confidence >= CONFIDENCE_OK &&
+               cal_data[ratio - 1].confidence >= CONFIDENCE_OK &&
+               cal_data[ratio + 1].confidence >= CONFIDENCE_OK) {
+               comp = (cal_data[ratio].steady_comp +
+                       cal_data[ratio - 1].steady_comp +
+                       cal_data[ratio + 1].steady_comp) / 3;
+       }
+
+       /* REVISIT: simple penalty of double idle injection */
+       if (reduce_irq)
+               comp = ratio;
+       /* do not exceed limit */
+       if (comp + ratio >= MAX_TARGET_RATIO)
+               comp = MAX_TARGET_RATIO - ratio - 1;
+
+       return comp;
+}
+
+static void adjust_compensation(int target_ratio, unsigned int win)
+{
+       int delta;
+       struct powerclamp_calibration_data *d = &cal_data[target_ratio];
+
+       /*
+        * adjust compensations if confidence level has not been reached or
+        * there are too many wakeups during the last idle injection period, we
+        * cannot trust the data for compensation.
+        */
+       if (d->confidence >= CONFIDENCE_OK ||
+               atomic_read(&idle_wakeup_counter) >
+               win * num_online_cpus())
+               return;
+
+       delta = set_target_ratio - current_ratio;
+       /* filter out bad data */
+       if (delta >= 0 && delta <= (1+target_ratio/10)) {
+               if (d->steady_comp)
+                       d->steady_comp =
+                               roundup(delta+d->steady_comp, 2)/2;
+               else
+                       d->steady_comp = delta;
+               d->confidence++;
+       }
+}
+
+static bool powerclamp_adjust_controls(unsigned int target_ratio,
+                               unsigned int guard, unsigned int win)
+{
+       static u64 msr_last, tsc_last;
+       u64 msr_now, tsc_now;
+       u64 val64;
+
+       /* check result for the last window */
+       msr_now = pkg_state_counter();
+       rdtscll(tsc_now);
+
+       /* calculate pkg cstate vs tsc ratio */
+       if (!msr_last || !tsc_last)
+               current_ratio = 1;
+       else if (tsc_now-tsc_last) {
+               val64 = 100*(msr_now-msr_last);
+               do_div(val64, (tsc_now-tsc_last));
+               current_ratio = val64;
+       }
+
+       /* update record */
+       msr_last = msr_now;
+       tsc_last = tsc_now;
+
+       adjust_compensation(target_ratio, win);
+       /*
+        * too many external interrupts, set flag such
+        * that we can take measure later.
+        */
+       reduce_irq = atomic_read(&idle_wakeup_counter) >=
+               2 * win * num_online_cpus();
+
+       atomic_set(&idle_wakeup_counter, 0);
+       /* if we are above target+guard, skip */
+       return set_target_ratio + guard <= current_ratio;
+}
+
+static int clamp_thread(void *arg)
+{
+       int cpunr = (unsigned long)arg;
+       DEFINE_TIMER(wakeup_timer, noop_timer, 0, 0);
+       static const struct sched_param param = {
+               .sched_priority = MAX_USER_RT_PRIO/2,
+       };
+       unsigned int count = 0;
+       unsigned int target_ratio;
+
+       set_bit(cpunr, cpu_clamping_mask);
+       set_freezable();
+       init_timer_on_stack(&wakeup_timer);
+       sched_setscheduler(current, SCHED_FIFO, &param);
+
+       while (true == clamping && !kthread_should_stop() &&
+               cpu_online(cpunr)) {
+               int sleeptime;
+               unsigned long target_jiffies;
+               unsigned int guard;
+               unsigned int compensation = 0;
+               int interval; /* jiffies to sleep for each attempt */
+               unsigned int duration_jiffies = msecs_to_jiffies(duration);
+               unsigned int window_size_now;
+
+               try_to_freeze();
+               /*
+                * make sure user selected ratio does not take effect until
+                * the next round. adjust target_ratio if user has changed
+                * target such that we can converge quickly.
+                */
+               target_ratio = set_target_ratio;
+               guard = 1 + target_ratio/20;
+               window_size_now = window_size;
+               count++;
+
+               /*
+                * systems may have different ability to enter package level
+                * c-states, thus we need to compensate the injected idle ratio
+                * to achieve the actual target reported by the HW.
+                */
+               compensation = get_compensation(target_ratio);
+               interval = duration_jiffies*100/(target_ratio+compensation);
+
+               /* align idle time */
+               target_jiffies = roundup(jiffies, interval);
+               sleeptime = target_jiffies - jiffies;
+               if (sleeptime <= 0)
+                       sleeptime = 1;
+               schedule_timeout_interruptible(sleeptime);
+               /*
+                * only elected controlling cpu can collect stats and update
+                * control parameters.
+                */
+               if (cpunr == control_cpu && !(count%window_size_now)) {
+                       should_skip =
+                               powerclamp_adjust_controls(target_ratio,
+                                                       guard, window_size_now);
+                       smp_mb();
+               }
+
+               if (should_skip)
+                       continue;
+
+               target_jiffies = jiffies + duration_jiffies;
+               mod_timer(&wakeup_timer, target_jiffies);
+               if (unlikely(local_softirq_pending()))
+                       continue;
+               /*
+                * stop tick sched during idle time, interrupts are still
+                * allowed. thus jiffies are updated properly.
+                */
+               preempt_disable();
+               tick_nohz_idle_enter();
+               /* mwait until target jiffies is reached */
+               while (time_before(jiffies, target_jiffies)) {
+                       unsigned long ecx = 1;
+                       unsigned long eax = target_mwait;
+
+                       /*
+                        * REVISIT: may call enter_idle() to notify drivers who
+                        * can save power during cpu idle. same for exit_idle()
+                        */
+                       local_touch_nmi();
+                       stop_critical_timings();
+                       __monitor((void *)&current_thread_info()->flags, 0, 0);
+                       cpu_relax(); /* allow HT sibling to run */
+                       __mwait(eax, ecx);
+                       start_critical_timings();
+                       atomic_inc(&idle_wakeup_counter);
+               }
+               tick_nohz_idle_exit();
+               preempt_enable_no_resched();
+       }
+       del_timer_sync(&wakeup_timer);
+       clear_bit(cpunr, cpu_clamping_mask);
+
+       return 0;
+}
+
+/*
+ * 1 HZ polling while clamping is active, useful for userspace
+ * to monitor actual idle ratio.
+ */
+static void poll_pkg_cstate(struct work_struct *dummy);
+static DECLARE_DELAYED_WORK(poll_pkg_cstate_work, poll_pkg_cstate);
+static void poll_pkg_cstate(struct work_struct *dummy)
+{
+       static u64 msr_last;
+       static u64 tsc_last;
+       static unsigned long jiffies_last;
+
+       u64 msr_now;
+       unsigned long jiffies_now;
+       u64 tsc_now;
+       u64 val64;
+
+       msr_now = pkg_state_counter();
+       rdtscll(tsc_now);
+       jiffies_now = jiffies;
+
+       /* calculate pkg cstate vs tsc ratio */
+       if (!msr_last || !tsc_last)
+               pkg_cstate_ratio_cur = 1;
+       else {
+               if (tsc_now - tsc_last) {
+                       val64 = 100 * (msr_now - msr_last);
+                       do_div(val64, (tsc_now - tsc_last));
+                       pkg_cstate_ratio_cur = val64;
+               }
+       }
+
+       /* update record */
+       msr_last = msr_now;
+       jiffies_last = jiffies_now;
+       tsc_last = tsc_now;
+
+       if (true == clamping)
+               schedule_delayed_work(&poll_pkg_cstate_work, HZ);
+}
+
+static int start_power_clamp(void)
+{
+       unsigned long cpu;
+       struct task_struct *thread;
+
+       /* check if pkg cstate counter is completely 0, abort in this case */
+       if (!pkg_state_counter()) {
+               pr_err("pkg cstate counter not functional, abort\n");
+               return -EINVAL;
+       }
+
+       set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO - 1);
+       /* prevent cpu hotplug */
+       get_online_cpus();
+
+       /* prefer BSP */
+       control_cpu = 0;
+       if (!cpu_online(control_cpu))
+               control_cpu = smp_processor_id();
+
+       clamping = true;
+       schedule_delayed_work(&poll_pkg_cstate_work, 0);
+
+       /* start one thread per online cpu */
+       for_each_online_cpu(cpu) {
+               struct task_struct **p =
+                       per_cpu_ptr(powerclamp_thread, cpu);
+
+               thread = kthread_create_on_node(clamp_thread,
+                                               (void *) cpu,
+                                               cpu_to_node(cpu),
+                                               "kidle_inject/%ld", cpu);
+               /* bind to cpu here */
+               if (likely(!IS_ERR(thread))) {
+                       kthread_bind(thread, cpu);
+                       wake_up_process(thread);
+                       *p = thread;
+               }
+
+       }
+       put_online_cpus();
+
+       return 0;
+}
+
+static void end_power_clamp(void)
+{
+       int i;
+       struct task_struct *thread;
+
+       clamping = false;
+       /*
+        * make clamping visible to other cpus and give per cpu clamping threads
+        * sometime to exit, or gets killed later.
+        */
+       smp_mb();
+       msleep(20);
+       if (bitmap_weight(cpu_clamping_mask, num_possible_cpus())) {
+               for_each_set_bit(i, cpu_clamping_mask, num_possible_cpus()) {
+                       pr_debug("clamping thread for cpu %d alive, kill\n", i);
+                       thread = *per_cpu_ptr(powerclamp_thread, i);
+                       kthread_stop(thread);
+               }
+       }
+}
+
+static int powerclamp_cpu_callback(struct notifier_block *nfb,
+                               unsigned long action, void *hcpu)
+{
+       unsigned long cpu = (unsigned long)hcpu;
+       struct task_struct *thread;
+       struct task_struct **percpu_thread =
+               per_cpu_ptr(powerclamp_thread, cpu);
+
+       if (false == clamping)
+               goto exit_ok;
+
+       switch (action) {
+       case CPU_ONLINE:
+               thread = kthread_create_on_node(clamp_thread,
+                                               (void *) cpu,
+                                               cpu_to_node(cpu),
+                                               "kidle_inject/%lu", cpu);
+               if (likely(!IS_ERR(thread))) {
+                       kthread_bind(thread, cpu);
+                       wake_up_process(thread);
+                       *percpu_thread = thread;
+               }
+               /* prefer BSP as controlling CPU */
+               if (cpu == 0) {
+                       control_cpu = 0;
+                       smp_mb();
+               }
+               break;
+       case CPU_DEAD:
+               if (test_bit(cpu, cpu_clamping_mask)) {
+                       pr_err("cpu %lu dead but powerclamping thread is not\n",
+                               cpu);
+                       kthread_stop(*percpu_thread);
+               }
+               if (cpu == control_cpu) {
+                       control_cpu = smp_processor_id();
+                       smp_mb();
+               }
+       }
+
+exit_ok:
+       return NOTIFY_OK;
+}
+
+static struct notifier_block powerclamp_cpu_notifier = {
+       .notifier_call = powerclamp_cpu_callback,
+};
+
+static int powerclamp_get_max_state(struct thermal_cooling_device *cdev,
+                                unsigned long *state)
+{
+       *state = MAX_TARGET_RATIO;
+
+       return 0;
+}
+
+static int powerclamp_get_cur_state(struct thermal_cooling_device *cdev,
+                                unsigned long *state)
+{
+       if (true == clamping)
+               *state = pkg_cstate_ratio_cur;
+       else
+               /* to save power, do not poll idle ratio while not clamping */
+               *state = -1; /* indicates invalid state */
+
+       return 0;
+}
+
+static int powerclamp_set_cur_state(struct thermal_cooling_device *cdev,
+                                unsigned long new_target_ratio)
+{
+       int ret = 0;
+
+       new_target_ratio = clamp(new_target_ratio, 0UL,
+                               (unsigned long) (MAX_TARGET_RATIO-1));
+       if (set_target_ratio == 0 && new_target_ratio > 0) {
+               pr_info("Start idle injection to reduce power\n");
+               set_target_ratio = new_target_ratio;
+               ret = start_power_clamp();
+               goto exit_set;
+       } else  if (set_target_ratio > 0 && new_target_ratio == 0) {
+               pr_info("Stop forced idle injection\n");
+               set_target_ratio = 0;
+               end_power_clamp();
+       } else  /* adjust currently running */ {
+               set_target_ratio = new_target_ratio;
+               /* make new set_target_ratio visible to other cpus */
+               smp_mb();
+       }
+
+exit_set:
+       return ret;
+}
+
+/* bind to generic thermal layer as cooling device*/
+static struct thermal_cooling_device_ops powerclamp_cooling_ops = {
+       .get_max_state = powerclamp_get_max_state,
+       .get_cur_state = powerclamp_get_cur_state,
+       .set_cur_state = powerclamp_set_cur_state,
+};
+
+/* runs on Nehalem and later */
+static const struct x86_cpu_id intel_powerclamp_ids[] = {
+       { X86_VENDOR_INTEL, 6, 0x1a},
+       { X86_VENDOR_INTEL, 6, 0x1c},
+       { X86_VENDOR_INTEL, 6, 0x1e},
+       { X86_VENDOR_INTEL, 6, 0x1f},
+       { X86_VENDOR_INTEL, 6, 0x25},
+       { X86_VENDOR_INTEL, 6, 0x26},
+       { X86_VENDOR_INTEL, 6, 0x2a},
+       { X86_VENDOR_INTEL, 6, 0x2c},
+       { X86_VENDOR_INTEL, 6, 0x2d},
+       { X86_VENDOR_INTEL, 6, 0x2e},
+       { X86_VENDOR_INTEL, 6, 0x2f},
+       { X86_VENDOR_INTEL, 6, 0x3a},
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);
+
+static int powerclamp_probe(void)
+{
+       if (!x86_match_cpu(intel_powerclamp_ids)) {
+               pr_err("Intel powerclamp does not run on family %d model %d\n",
+                               boot_cpu_data.x86, boot_cpu_data.x86_model);
+               return -ENODEV;
+       }
+       if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC) ||
+               !boot_cpu_has(X86_FEATURE_CONSTANT_TSC) ||
+               !boot_cpu_has(X86_FEATURE_MWAIT) ||
+               !boot_cpu_has(X86_FEATURE_ARAT))
+               return -ENODEV;
+
+       /* find the deepest mwait value */
+       find_target_mwait();
+
+       return 0;
+}
+
+static int powerclamp_debug_show(struct seq_file *m, void *unused)
+{
+       int i = 0;
+
+       seq_printf(m, "controlling cpu: %d\n", control_cpu);
+       seq_printf(m, "pct confidence steady dynamic (compensation)\n");
+       for (i = 0; i < MAX_TARGET_RATIO; i++) {
+               seq_printf(m, "%d\t%lu\t%lu\t%lu\n",
+                       i,
+                       cal_data[i].confidence,
+                       cal_data[i].steady_comp,
+                       cal_data[i].dynamic_comp);
+       }
+
+       return 0;
+}
+
+static int powerclamp_debug_open(struct inode *inode,
+                       struct file *file)
+{
+       return single_open(file, powerclamp_debug_show, inode->i_private);
+}
+
+static const struct file_operations powerclamp_debug_fops = {
+       .open           = powerclamp_debug_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .owner          = THIS_MODULE,
+};
+
+static inline void powerclamp_create_debug_files(void)
+{
+       debug_dir = debugfs_create_dir("intel_powerclamp", NULL);
+       if (!debug_dir)
+               return;
+
+       if (!debugfs_create_file("powerclamp_calib", S_IRUGO, debug_dir,
+                                       cal_data, &powerclamp_debug_fops))
+               goto file_error;
+
+       return;
+
+file_error:
+       debugfs_remove_recursive(debug_dir);
+}
+
+static int powerclamp_init(void)
+{
+       int retval;
+       int bitmap_size;
+
+       bitmap_size = BITS_TO_LONGS(num_possible_cpus()) * sizeof(long);
+       cpu_clamping_mask = kzalloc(bitmap_size, GFP_KERNEL);
+       if (!cpu_clamping_mask)
+               return -ENOMEM;
+
+       /* probe cpu features and ids here */
+       retval = powerclamp_probe();
+       if (retval)
+               return retval;
+       /* set default limit, maybe adjusted during runtime based on feedback */
+       window_size = 2;
+       register_hotcpu_notifier(&powerclamp_cpu_notifier);
+       powerclamp_thread = alloc_percpu(struct task_struct *);
+       cooling_dev = thermal_cooling_device_register("intel_powerclamp", NULL,
+                                               &powerclamp_cooling_ops);
+       if (IS_ERR(cooling_dev))
+               return -ENODEV;
+
+       if (!duration)
+               duration = jiffies_to_msecs(DEFAULT_DURATION_JIFFIES);
+       powerclamp_create_debug_files();
+
+       return 0;
+}
+module_init(powerclamp_init);
+
+static void powerclamp_exit(void)
+{
+       unregister_hotcpu_notifier(&powerclamp_cpu_notifier);
+       end_power_clamp();
+       free_percpu(powerclamp_thread);
+       thermal_cooling_device_unregister(cooling_dev);
+       kfree(cpu_clamping_mask);
+
+       cancel_delayed_work_sync(&poll_pkg_cstate_work);
+       debugfs_remove_recursive(debug_dir);
+}
+module_exit(powerclamp_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
+MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
+MODULE_DESCRIPTION("Package Level C-state Idle Injection for Intel CPUs");
diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c
new file mode 100644 (file)
index 0000000..65cb4f0
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Kirkwood thermal sensor driver
+ *
+ * Copyright (C) 2012 Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+
+#define KIRKWOOD_THERMAL_VALID_OFFSET  9
+#define KIRKWOOD_THERMAL_VALID_MASK    0x1
+#define KIRKWOOD_THERMAL_TEMP_OFFSET   10
+#define KIRKWOOD_THERMAL_TEMP_MASK     0x1FF
+
+/* Kirkwood Thermal Sensor Dev Structure */
+struct kirkwood_thermal_priv {
+       void __iomem *sensor;
+};
+
+static int kirkwood_get_temp(struct thermal_zone_device *thermal,
+                         unsigned long *temp)
+{
+       unsigned long reg;
+       struct kirkwood_thermal_priv *priv = thermal->devdata;
+
+       reg = readl_relaxed(priv->sensor);
+
+       /* Valid check */
+       if (!(reg >> KIRKWOOD_THERMAL_VALID_OFFSET) &
+           KIRKWOOD_THERMAL_VALID_MASK) {
+               dev_err(&thermal->device,
+                       "Temperature sensor reading not valid\n");
+               return -EIO;
+       }
+
+       /*
+        * Calculate temperature. See Section 8.10.1 of the 88AP510,
+        * datasheet, which has the same sensor.
+        * Documentation/arm/Marvell/README
+        */
+       reg = (reg >> KIRKWOOD_THERMAL_TEMP_OFFSET) &
+               KIRKWOOD_THERMAL_TEMP_MASK;
+       *temp = ((2281638UL - (7298*reg)) / 10);
+
+       return 0;
+}
+
+static struct thermal_zone_device_ops ops = {
+       .get_temp = kirkwood_get_temp,
+};
+
+static const struct of_device_id kirkwood_thermal_id_table[] = {
+       { .compatible = "marvell,kirkwood-thermal" },
+       {}
+};
+
+static int kirkwood_thermal_probe(struct platform_device *pdev)
+{
+       struct thermal_zone_device *thermal = NULL;
+       struct kirkwood_thermal_priv *priv;
+       struct resource *res;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "Failed to get platform resource\n");
+               return -ENODEV;
+       }
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->sensor = devm_request_and_ioremap(&pdev->dev, res);
+       if (!priv->sensor) {
+               dev_err(&pdev->dev, "Failed to request_ioremap memory\n");
+               return -EADDRNOTAVAIL;
+       }
+
+       thermal = thermal_zone_device_register("kirkwood_thermal", 0, 0,
+                                              priv, &ops, NULL, 0, 0);
+       if (IS_ERR(thermal)) {
+               dev_err(&pdev->dev,
+                       "Failed to register thermal zone device\n");
+               return PTR_ERR(thermal);
+       }
+
+       platform_set_drvdata(pdev, thermal);
+
+       return 0;
+}
+
+static int kirkwood_thermal_exit(struct platform_device *pdev)
+{
+       struct thermal_zone_device *kirkwood_thermal =
+               platform_get_drvdata(pdev);
+
+       thermal_zone_device_unregister(kirkwood_thermal);
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+MODULE_DEVICE_TABLE(of, kirkwood_thermal_id_table);
+
+static struct platform_driver kirkwood_thermal_driver = {
+       .probe = kirkwood_thermal_probe,
+       .remove = kirkwood_thermal_exit,
+       .driver = {
+               .name = "kirkwood_thermal",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(kirkwood_thermal_id_table),
+       },
+};
+
+module_platform_driver(kirkwood_thermal_driver);
+
+MODULE_AUTHOR("Nobuhiro Iwamatsu <iwamatsu@nigauri.org>");
+MODULE_DESCRIPTION("kirkwood thermal driver");
+MODULE_LICENSE("GPL");
index 90db951725da46a7663264d94e2fa928a058e2dc..28f0919940137dcaf57fce04df69ddcdecfc173f 100644 (file)
  */
 #include <linux/delay.h>
 #include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/thermal.h>
 
-#define THSCR  0x2c
-#define THSSR  0x30
+#define IDLE_INTERVAL  5000
+
+#define COMMON_STR     0x00
+#define COMMON_ENR     0x04
+#define COMMON_INTMSK  0x0c
+
+#define REG_POSNEG     0x20
+#define REG_FILONOFF   0x28
+#define REG_THSCR      0x2c
+#define REG_THSSR      0x30
+#define REG_INTCTRL    0x34
 
 /* THSCR */
-#define CPTAP  0xf
+#define CPCTL  (1 << 12)
 
 /* THSSR */
 #define CTEMP  0x3f
 
-
-struct rcar_thermal_priv {
+struct rcar_thermal_common {
        void __iomem *base;
        struct device *dev;
+       struct list_head head;
        spinlock_t lock;
-       u32 comp;
 };
 
+struct rcar_thermal_priv {
+       void __iomem *base;
+       struct rcar_thermal_common *common;
+       struct thermal_zone_device *zone;
+       struct delayed_work work;
+       struct mutex lock;
+       struct list_head list;
+       int id;
+       int ctemp;
+};
+
+#define rcar_thermal_for_each_priv(pos, common)        \
+       list_for_each_entry(pos, &common->head, list)
+
 #define MCELSIUS(temp)                 ((temp) * 1000)
-#define rcar_zone_to_priv(zone)                (zone->devdata)
+#define rcar_zone_to_priv(zone)                ((zone)->devdata)
+#define rcar_priv_to_dev(priv)         ((priv)->common->dev)
+#define rcar_has_irq_support(priv)     ((priv)->common->base)
+#define rcar_id_to_shift(priv)         ((priv)->id * 8)
+
+#ifdef DEBUG
+# define rcar_force_update_temp(priv)  1
+#else
+# define rcar_force_update_temp(priv)  0
+#endif
 
 /*
  *             basic functions
  */
-static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg)
+#define rcar_thermal_common_read(c, r) \
+       _rcar_thermal_common_read(c, COMMON_ ##r)
+static u32 _rcar_thermal_common_read(struct rcar_thermal_common *common,
+                                    u32 reg)
 {
-       unsigned long flags;
-       u32 ret;
-
-       spin_lock_irqsave(&priv->lock, flags);
+       return ioread32(common->base + reg);
+}
 
-       ret = ioread32(priv->base + reg);
+#define rcar_thermal_common_write(c, r, d) \
+       _rcar_thermal_common_write(c, COMMON_ ##r, d)
+static void _rcar_thermal_common_write(struct rcar_thermal_common *common,
+                                      u32 reg, u32 data)
+{
+       iowrite32(data, common->base + reg);
+}
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+#define rcar_thermal_common_bset(c, r, m, d) \
+       _rcar_thermal_common_bset(c, COMMON_ ##r, m, d)
+static void _rcar_thermal_common_bset(struct rcar_thermal_common *common,
+                                     u32 reg, u32 mask, u32 data)
+{
+       u32 val;
 
-       return ret;
+       val = ioread32(common->base + reg);
+       val &= ~mask;
+       val |= (data & mask);
+       iowrite32(val, common->base + reg);
 }
 
-#if 0 /* no user at this point */
-static void rcar_thermal_write(struct rcar_thermal_priv *priv,
-                              u32 reg, u32 data)
+#define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r)
+static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&priv->lock, flags);
+       return ioread32(priv->base + reg);
+}
 
+#define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d)
+static void _rcar_thermal_write(struct rcar_thermal_priv *priv,
+                               u32 reg, u32 data)
+{
        iowrite32(data, priv->base + reg);
-
-       spin_unlock_irqrestore(&priv->lock, flags);
 }
-#endif
 
-static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg,
-                             u32 mask, u32 data)
+#define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d)
+static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg,
+                              u32 mask, u32 data)
 {
-       unsigned long flags;
        u32 val;
 
-       spin_lock_irqsave(&priv->lock, flags);
-
        val = ioread32(priv->base + reg);
        val &= ~mask;
        val |= (data & mask);
        iowrite32(val, priv->base + reg);
-
-       spin_unlock_irqrestore(&priv->lock, flags);
 }
 
 /*
  *             zone device functions
  */
-static int rcar_thermal_get_temp(struct thermal_zone_device *zone,
-                          unsigned long *temp)
+static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
 {
-       struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone);
-       int val, min, max, tmp;
-
-       tmp = -200; /* default */
-       while (1) {
-               if (priv->comp < 1 || priv->comp > 12) {
-                       dev_err(priv->dev,
-                               "THSSR invalid data (%d)\n", priv->comp);
-                       priv->comp = 4; /* for next thermal */
-                       return -EINVAL;
-               }
+       struct device *dev = rcar_priv_to_dev(priv);
+       int i;
+       int ctemp, old, new;
 
-               /*
-                * THS comparator offset and the reference temperature
-                *
-                * Comparator   | reference     | Temperature field
-                * offset       | temperature   | measurement
-                *              | (degrees C)   | (degrees C)
-                * -------------+---------------+-------------------
-                *  1           |  -45          |  -45 to  -30
-                *  2           |  -30          |  -30 to  -15
-                *  3           |  -15          |  -15 to    0
-                *  4           |    0          |    0 to  +15
-                *  5           |  +15          |  +15 to  +30
-                *  6           |  +30          |  +30 to  +45
-                *  7           |  +45          |  +45 to  +60
-                *  8           |  +60          |  +60 to  +75
-                *  9           |  +75          |  +75 to  +90
-                * 10           |  +90          |  +90 to +105
-                * 11           | +105          | +105 to +120
-                * 12           | +120          | +120 to +135
-                */
+       mutex_lock(&priv->lock);
 
-               /* calculate thermal limitation */
-               min = (priv->comp * 15) - 60;
-               max = min + 15;
+       /*
+        * TSC decides a value of CPTAP automatically,
+        * and this is the conditions which validate interrupt.
+        */
+       rcar_thermal_bset(priv, THSCR, CPCTL, CPCTL);
 
+       ctemp = 0;
+       old = ~0;
+       for (i = 0; i < 128; i++) {
                /*
                 * we need to wait 300us after changing comparator offset
                 * to get stable temperature.
                 * see "Usage Notes" on datasheet
                 */
-               rcar_thermal_bset(priv, THSCR, CPTAP, priv->comp);
                udelay(300);
 
-               /* calculate current temperature */
-               val = rcar_thermal_read(priv, THSSR) & CTEMP;
-               val = (val * 5) - 65;
+               new = rcar_thermal_read(priv, THSSR) & CTEMP;
+               if (new == old) {
+                       ctemp = new;
+                       break;
+               }
+               old = new;
+       }
 
-               dev_dbg(priv->dev, "comp/min/max/val = %d/%d/%d/%d\n",
-                       priv->comp, min, max, val);
+       if (!ctemp) {
+               dev_err(dev, "thermal sensor was broken\n");
+               return -EINVAL;
+       }
 
-               /*
-                * If val is same as min/max, then,
-                * it should try again on next comparator.
-                * But the val might be correct temperature.
-                * Keep it on "tmp" and compare with next val.
-                */
-               if (tmp == val)
-                       break;
+       /*
+        * enable IRQ
+        */
+       if (rcar_has_irq_support(priv)) {
+               rcar_thermal_write(priv, FILONOFF, 0);
 
-               if (val <= min) {
-                       tmp = min;
-                       priv->comp--; /* try again */
-               } else if (val >= max) {
-                       tmp = max;
-                       priv->comp++; /* try again */
-               } else {
-                       tmp = val;
-                       break;
-               }
+               /* enable Rising/Falling edge interrupt */
+               rcar_thermal_write(priv, POSNEG,  0x1);
+               rcar_thermal_write(priv, INTCTRL, (((ctemp - 0) << 8) |
+                                                  ((ctemp - 1) << 0)));
+       }
+
+       dev_dbg(dev, "thermal%d  %d -> %d\n", priv->id, priv->ctemp, ctemp);
+
+       priv->ctemp = ctemp;
+
+       mutex_unlock(&priv->lock);
+
+       return 0;
+}
+
+static int rcar_thermal_get_temp(struct thermal_zone_device *zone,
+                                unsigned long *temp)
+{
+       struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone);
+
+       if (!rcar_has_irq_support(priv) || rcar_force_update_temp(priv))
+               rcar_thermal_update_temp(priv);
+
+       mutex_lock(&priv->lock);
+       *temp =  MCELSIUS((priv->ctemp * 5) - 65);
+       mutex_unlock(&priv->lock);
+
+       return 0;
+}
+
+static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone,
+                                     int trip, enum thermal_trip_type *type)
+{
+       struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone);
+       struct device *dev = rcar_priv_to_dev(priv);
+
+       /* see rcar_thermal_get_temp() */
+       switch (trip) {
+       case 0: /* +90 <= temp */
+               *type = THERMAL_TRIP_CRITICAL;
+               break;
+       default:
+               dev_err(dev, "rcar driver trip error\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone,
+                                     int trip, unsigned long *temp)
+{
+       struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone);
+       struct device *dev = rcar_priv_to_dev(priv);
+
+       /* see rcar_thermal_get_temp() */
+       switch (trip) {
+       case 0: /* +90 <= temp */
+               *temp = MCELSIUS(90);
+               break;
+       default:
+               dev_err(dev, "rcar driver trip error\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rcar_thermal_notify(struct thermal_zone_device *zone,
+                              int trip, enum thermal_trip_type type)
+{
+       struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone);
+       struct device *dev = rcar_priv_to_dev(priv);
+
+       switch (type) {
+       case THERMAL_TRIP_CRITICAL:
+               /* FIXME */
+               dev_warn(dev, "Thermal reached to critical temperature\n");
+               break;
+       default:
+               break;
        }
 
-       *temp = MCELSIUS(tmp);
        return 0;
 }
 
 static struct thermal_zone_device_ops rcar_thermal_zone_ops = {
-       .get_temp = rcar_thermal_get_temp,
+       .get_temp       = rcar_thermal_get_temp,
+       .get_trip_type  = rcar_thermal_get_trip_type,
+       .get_trip_temp  = rcar_thermal_get_trip_temp,
+       .notify         = rcar_thermal_notify,
 };
 
 /*
- *             platform functions
+ *             interrupt
  */
-static int rcar_thermal_probe(struct platform_device *pdev)
+#define rcar_thermal_irq_enable(p)     _rcar_thermal_irq_ctrl(p, 1)
+#define rcar_thermal_irq_disable(p)    _rcar_thermal_irq_ctrl(p, 0)
+static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable)
+{
+       struct rcar_thermal_common *common = priv->common;
+       unsigned long flags;
+       u32 mask = 0x3 << rcar_id_to_shift(priv); /* enable Rising/Falling */
+
+       spin_lock_irqsave(&common->lock, flags);
+
+       rcar_thermal_common_bset(common, INTMSK, mask, enable ? 0 : mask);
+
+       spin_unlock_irqrestore(&common->lock, flags);
+}
+
+static void rcar_thermal_work(struct work_struct *work)
 {
-       struct thermal_zone_device *zone;
        struct rcar_thermal_priv *priv;
-       struct resource *res;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(&pdev->dev, "Could not get platform resource\n");
-               return -ENODEV;
+       priv = container_of(work, struct rcar_thermal_priv, work.work);
+
+       rcar_thermal_update_temp(priv);
+       rcar_thermal_irq_enable(priv);
+       thermal_zone_device_update(priv->zone);
+}
+
+static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status)
+{
+       struct device *dev = rcar_priv_to_dev(priv);
+
+       status = (status >> rcar_id_to_shift(priv)) & 0x3;
+
+       if (status & 0x3) {
+               dev_dbg(dev, "thermal%d %s%s\n",
+                       priv->id,
+                       (status & 0x2) ? "Rising " : "",
+                       (status & 0x1) ? "Falling" : "");
        }
 
-       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv) {
-               dev_err(&pdev->dev, "Could not allocate priv\n");
-               return -ENOMEM;
+       return status;
+}
+
+static irqreturn_t rcar_thermal_irq(int irq, void *data)
+{
+       struct rcar_thermal_common *common = data;
+       struct rcar_thermal_priv *priv;
+       unsigned long flags;
+       u32 status, mask;
+
+       spin_lock_irqsave(&common->lock, flags);
+
+       mask    = rcar_thermal_common_read(common, INTMSK);
+       status  = rcar_thermal_common_read(common, STR);
+       rcar_thermal_common_write(common, STR, 0x000F0F0F & mask);
+
+       spin_unlock_irqrestore(&common->lock, flags);
+
+       status = status & ~mask;
+
+       /*
+        * check the status
+        */
+       rcar_thermal_for_each_priv(priv, common) {
+               if (rcar_thermal_had_changed(priv, status)) {
+                       rcar_thermal_irq_disable(priv);
+                       schedule_delayed_work(&priv->work,
+                                             msecs_to_jiffies(300));
+               }
        }
 
-       priv->comp = 4; /* basic setup */
-       priv->dev = &pdev->dev;
-       spin_lock_init(&priv->lock);
-       priv->base = devm_ioremap_nocache(&pdev->dev,
-                                         res->start, resource_size(res));
-       if (!priv->base) {
-               dev_err(&pdev->dev, "Unable to ioremap thermal register\n");
+       return IRQ_HANDLED;
+}
+
+/*
+ *             platform functions
+ */
+static int rcar_thermal_probe(struct platform_device *pdev)
+{
+       struct rcar_thermal_common *common;
+       struct rcar_thermal_priv *priv;
+       struct device *dev = &pdev->dev;
+       struct resource *res, *irq;
+       int mres = 0;
+       int i;
+       int idle = IDLE_INTERVAL;
+
+       common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
+       if (!common) {
+               dev_err(dev, "Could not allocate common\n");
                return -ENOMEM;
        }
 
-       zone = thermal_zone_device_register("rcar_thermal", 0, 0, priv,
-                                   &rcar_thermal_zone_ops, NULL, 0, 0);
-       if (IS_ERR(zone)) {
-               dev_err(&pdev->dev, "thermal zone device is NULL\n");
-               return PTR_ERR(zone);
+       INIT_LIST_HEAD(&common->head);
+       spin_lock_init(&common->lock);
+       common->dev = dev;
+
+       irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (irq) {
+               int ret;
+
+               /*
+                * platform has IRQ support.
+                * Then, drier use common register
+                */
+               res = platform_get_resource(pdev, IORESOURCE_MEM, mres++);
+               if (!res) {
+                       dev_err(dev, "Could not get platform resource\n");
+                       return -ENODEV;
+               }
+
+               ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0,
+                                      dev_name(dev), common);
+               if (ret) {
+                       dev_err(dev, "irq request failed\n ");
+                       return ret;
+               }
+
+               /*
+                * rcar_has_irq_support() will be enabled
+                */
+               common->base = devm_request_and_ioremap(dev, res);
+               if (!common->base) {
+                       dev_err(dev, "Unable to ioremap thermal register\n");
+                       return -ENOMEM;
+               }
+
+               /* enable temperature comparation */
+               rcar_thermal_common_write(common, ENR, 0x00030303);
+
+               idle = 0; /* polling delaye is not needed */
        }
 
-       platform_set_drvdata(pdev, zone);
+       for (i = 0;; i++) {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, mres++);
+               if (!res)
+                       break;
+
+               priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+               if (!priv) {
+                       dev_err(dev, "Could not allocate priv\n");
+                       return -ENOMEM;
+               }
+
+               priv->base = devm_request_and_ioremap(dev, res);
+               if (!priv->base) {
+                       dev_err(dev, "Unable to ioremap priv register\n");
+                       return -ENOMEM;
+               }
 
-       dev_info(&pdev->dev, "proved\n");
+               priv->common = common;
+               priv->id = i;
+               mutex_init(&priv->lock);
+               INIT_LIST_HEAD(&priv->list);
+               INIT_DELAYED_WORK(&priv->work, rcar_thermal_work);
+               rcar_thermal_update_temp(priv);
+
+               priv->zone = thermal_zone_device_register("rcar_thermal",
+                                               1, 0, priv,
+                                               &rcar_thermal_zone_ops, NULL, 0,
+                                               idle);
+               if (IS_ERR(priv->zone)) {
+                       dev_err(dev, "can't register thermal zone\n");
+                       goto error_unregister;
+               }
+
+               list_move_tail(&priv->list, &common->head);
+
+               if (rcar_has_irq_support(priv))
+                       rcar_thermal_irq_enable(priv);
+       }
+
+       platform_set_drvdata(pdev, common);
+
+       dev_info(dev, "%d sensor proved\n", i);
 
        return 0;
+
+error_unregister:
+       rcar_thermal_for_each_priv(priv, common)
+               thermal_zone_device_unregister(priv->zone);
+
+       return -ENODEV;
 }
 
 static int rcar_thermal_remove(struct platform_device *pdev)
 {
-       struct thermal_zone_device *zone = platform_get_drvdata(pdev);
+       struct rcar_thermal_common *common = platform_get_drvdata(pdev);
+       struct rcar_thermal_priv *priv;
+
+       rcar_thermal_for_each_priv(priv, common)
+               thermal_zone_device_unregister(priv->zone);
 
-       thermal_zone_device_unregister(zone);
        platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
 
+static const struct of_device_id rcar_thermal_dt_ids[] = {
+       { .compatible = "renesas,rcar-thermal", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids);
+
 static struct platform_driver rcar_thermal_driver = {
        .driver = {
                .name   = "rcar_thermal",
+               .of_match_table = rcar_thermal_dt_ids,
        },
        .probe          = rcar_thermal_probe,
        .remove         = rcar_thermal_remove,
index 6b2d8b21aaee03b5a723249cea6773f96c5adf19..3c5ee5607977c562dca9209d44c1b2627a2b760f 100644 (file)
@@ -131,7 +131,7 @@ static int spear_thermal_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       stdev->clk = clk_get(&pdev->dev, NULL);
+       stdev->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(stdev->clk)) {
                dev_err(&pdev->dev, "Can't get clock\n");
                return PTR_ERR(stdev->clk);
@@ -140,7 +140,7 @@ static int spear_thermal_probe(struct platform_device *pdev)
        ret = clk_enable(stdev->clk);
        if (ret) {
                dev_err(&pdev->dev, "Can't enable clock\n");
-               goto put_clk;
+               return ret;
        }
 
        stdev->flags = val;
@@ -163,8 +163,6 @@ static int spear_thermal_probe(struct platform_device *pdev)
 
 disable_clk:
        clk_disable(stdev->clk);
-put_clk:
-       clk_put(stdev->clk);
 
        return ret;
 }
@@ -183,7 +181,6 @@ static int spear_thermal_exit(struct platform_device *pdev)
        writel_relaxed(actual_mask & ~stdev->flags, stdev->thermal_base);
 
        clk_disable(stdev->clk);
-       clk_put(stdev->clk);
 
        return 0;
 }
index 0cd5e9fbab1c96ea7393db980aeafc26c87bb577..407cde3211c1bccfcf1e43515fb0a47a5a99e67c 100644 (file)
  *       state for this trip point
  *    b. if the trend is THERMAL_TREND_DROPPING, use lower cooling
  *       state for this trip point
+ *    c. if the trend is THERMAL_TREND_RAISE_FULL, use upper limit
+ *       for this trip point
+ *    d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit
+ *       for this trip point
+ * If the temperature is lower than a trip point,
+ *    a. if the trend is THERMAL_TREND_RAISING, do nothing
+ *    b. if the trend is THERMAL_TREND_DROPPING, use lower cooling
+ *       state for this trip point, if the cooling state already
+ *       equals lower limit, deactivate the thermal instance
+ *    c. if the trend is THERMAL_TREND_RAISE_FULL, do nothing
+ *    d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit,
+ *       if the cooling state already equals lower limit,
+ *       deactive the thermal instance
  */
 static unsigned long get_target_state(struct thermal_instance *instance,
-                                       enum thermal_trend trend)
+                               enum thermal_trend trend, bool throttle)
 {
        struct thermal_cooling_device *cdev = instance->cdev;
        unsigned long cur_state;
 
        cdev->ops->get_cur_state(cdev, &cur_state);
 
-       if (trend == THERMAL_TREND_RAISING) {
-               cur_state = cur_state < instance->upper ?
-                           (cur_state + 1) : instance->upper;
-       } else if (trend == THERMAL_TREND_DROPPING) {
-               cur_state = cur_state > instance->lower ?
-                           (cur_state - 1) : instance->lower;
+       switch (trend) {
+       case THERMAL_TREND_RAISING:
+               if (throttle)
+                       cur_state = cur_state < instance->upper ?
+                                   (cur_state + 1) : instance->upper;
+               break;
+       case THERMAL_TREND_RAISE_FULL:
+               if (throttle)
+                       cur_state = instance->upper;
+               break;
+       case THERMAL_TREND_DROPPING:
+               if (cur_state == instance->lower) {
+                       if (!throttle)
+                               cur_state = -1;
+               } else
+                       cur_state -= 1;
+               break;
+       case THERMAL_TREND_DROP_FULL:
+               if (cur_state == instance->lower) {
+                       if (!throttle)
+                               cur_state = -1;
+               } else
+                       cur_state = instance->lower;
+               break;
+       default:
+               break;
        }
 
        return cur_state;
@@ -66,57 +99,14 @@ static void update_passive_instance(struct thermal_zone_device *tz,
                tz->passive += value;
 }
 
-static void update_instance_for_throttle(struct thermal_zone_device *tz,
-                               int trip, enum thermal_trip_type trip_type,
-                               enum thermal_trend trend)
-{
-       struct thermal_instance *instance;
-
-       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
-               if (instance->trip != trip)
-                       continue;
-
-               instance->target = get_target_state(instance, trend);
-
-               /* Activate a passive thermal instance */
-               if (instance->target == THERMAL_NO_TARGET)
-                       update_passive_instance(tz, trip_type, 1);
-
-               instance->cdev->updated = false; /* cdev needs update */
-       }
-}
-
-static void update_instance_for_dethrottle(struct thermal_zone_device *tz,
-                               int trip, enum thermal_trip_type trip_type)
-{
-       struct thermal_instance *instance;
-       struct thermal_cooling_device *cdev;
-       unsigned long cur_state;
-
-       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
-               if (instance->trip != trip ||
-                       instance->target == THERMAL_NO_TARGET)
-                       continue;
-
-               cdev = instance->cdev;
-               cdev->ops->get_cur_state(cdev, &cur_state);
-
-               instance->target = cur_state > instance->lower ?
-                           (cur_state - 1) : THERMAL_NO_TARGET;
-
-               /* Deactivate a passive thermal instance */
-               if (instance->target == THERMAL_NO_TARGET)
-                       update_passive_instance(tz, trip_type, -1);
-
-               cdev->updated = false; /* cdev needs update */
-       }
-}
-
 static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
 {
        long trip_temp;
        enum thermal_trip_type trip_type;
        enum thermal_trend trend;
+       struct thermal_instance *instance;
+       bool throttle = false;
+       int old_target;
 
        if (trip == THERMAL_TRIPS_NONE) {
                trip_temp = tz->forced_passive;
@@ -128,12 +118,30 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
 
        trend = get_tz_trend(tz, trip);
 
+       if (tz->temperature >= trip_temp)
+               throttle = true;
+
        mutex_lock(&tz->lock);
 
-       if (tz->temperature >= trip_temp)
-               update_instance_for_throttle(tz, trip, trip_type, trend);
-       else
-               update_instance_for_dethrottle(tz, trip, trip_type);
+       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+               if (instance->trip != trip)
+                       continue;
+
+               old_target = instance->target;
+               instance->target = get_target_state(instance, trend, throttle);
+
+               /* Activate a passive thermal instance */
+               if (old_target == THERMAL_NO_TARGET &&
+                       instance->target != THERMAL_NO_TARGET)
+                       update_passive_instance(tz, trip_type, 1);
+               /* Deactivate a passive thermal instance */
+               else if (old_target != THERMAL_NO_TARGET &&
+                       instance->target == THERMAL_NO_TARGET)
+                       update_passive_instance(tz, trip_type, -1);
+
+
+               instance->cdev->updated = false; /* cdev needs update */
+       }
 
        mutex_unlock(&tz->lock);
 }
index 8c8ce806180fc1d56988273175ac899741449cad..1a19a2f4ba2744b7a7d06abe1f949224f83ad3c5 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/kdev_t.h>
 #include <linux/idr.h>
 #include <linux/thermal.h>
-#include <linux/spinlock.h>
 #include <linux/reboot.h>
 #include <net/netlink.h>
 #include <net/genetlink.h>
@@ -355,8 +354,9 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
                tz->ops->notify(tz, trip, trip_type);
 
        if (trip_type == THERMAL_TRIP_CRITICAL) {
-               pr_emerg("Critical temperature reached(%d C),shutting down\n",
-                        tz->temperature / 1000);
+               dev_emerg(&tz->device,
+                         "critical temperature reached(%d C),shutting down\n",
+                         tz->temperature / 1000);
                orderly_poweroff(true);
        }
 }
@@ -378,23 +378,57 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
        monitor_thermal_zone(tz);
 }
 
+static int thermal_zone_get_temp(struct thermal_zone_device *tz,
+                               unsigned long *temp)
+{
+       int ret = 0;
+#ifdef CONFIG_THERMAL_EMULATION
+       int count;
+       unsigned long crit_temp = -1UL;
+       enum thermal_trip_type type;
+#endif
+
+       mutex_lock(&tz->lock);
+
+       ret = tz->ops->get_temp(tz, temp);
+#ifdef CONFIG_THERMAL_EMULATION
+       if (!tz->emul_temperature)
+               goto skip_emul;
+
+       for (count = 0; count < tz->trips; count++) {
+               ret = tz->ops->get_trip_type(tz, count, &type);
+               if (!ret && type == THERMAL_TRIP_CRITICAL) {
+                       ret = tz->ops->get_trip_temp(tz, count, &crit_temp);
+                       break;
+               }
+       }
+
+       if (ret)
+               goto skip_emul;
+
+       if (*temp < crit_temp)
+               *temp = tz->emul_temperature;
+skip_emul:
+#endif
+       mutex_unlock(&tz->lock);
+       return ret;
+}
+
 static void update_temperature(struct thermal_zone_device *tz)
 {
        long temp;
        int ret;
 
-       mutex_lock(&tz->lock);
-
-       ret = tz->ops->get_temp(tz, &temp);
+       ret = thermal_zone_get_temp(tz, &temp);
        if (ret) {
-               pr_warn("failed to read out thermal zone %d\n", tz->id);
-               goto exit;
+               dev_warn(&tz->device, "failed to read out thermal zone %d\n",
+                        tz->id);
+               return;
        }
 
+       mutex_lock(&tz->lock);
        tz->last_temperature = tz->temperature;
        tz->temperature = temp;
-
-exit:
        mutex_unlock(&tz->lock);
 }
 
@@ -437,10 +471,7 @@ temp_show(struct device *dev, struct device_attribute *attr, char *buf)
        long temperature;
        int ret;
 
-       if (!tz->ops->get_temp)
-               return -EPERM;
-
-       ret = tz->ops->get_temp(tz, &temperature);
+       ret = thermal_zone_get_temp(tz, &temperature);
 
        if (ret)
                return ret;
@@ -700,6 +731,31 @@ policy_show(struct device *dev, struct device_attribute *devattr, char *buf)
        return sprintf(buf, "%s\n", tz->governor->name);
 }
 
+#ifdef CONFIG_THERMAL_EMULATION
+static ssize_t
+emul_temp_store(struct device *dev, struct device_attribute *attr,
+                    const char *buf, size_t count)
+{
+       struct thermal_zone_device *tz = to_thermal_zone(dev);
+       int ret = 0;
+       unsigned long temperature;
+
+       if (kstrtoul(buf, 10, &temperature))
+               return -EINVAL;
+
+       if (!tz->ops->set_emul_temp) {
+               mutex_lock(&tz->lock);
+               tz->emul_temperature = temperature;
+               mutex_unlock(&tz->lock);
+       } else {
+               ret = tz->ops->set_emul_temp(tz, temperature);
+       }
+
+       return ret ? ret : count;
+}
+static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
+#endif/*CONFIG_THERMAL_EMULATION*/
+
 static DEVICE_ATTR(type, 0444, type_show, NULL);
 static DEVICE_ATTR(temp, 0444, temp_show, NULL);
 static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
@@ -842,7 +898,7 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
                                       temp_input);
        struct thermal_zone_device *tz = temp->tz;
 
-       ret = tz->ops->get_temp(tz, &temperature);
+       ret = thermal_zone_get_temp(tz, &temperature);
 
        if (ret)
                return ret;
@@ -1529,6 +1585,9 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
        if (!ops || !ops->get_temp)
                return ERR_PTR(-EINVAL);
 
+       if (trips > 0 && !ops->get_trip_type)
+               return ERR_PTR(-EINVAL);
+
        tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
        if (!tz)
                return ERR_PTR(-ENOMEM);
@@ -1592,6 +1651,11 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
                        goto unregister;
        }
 
+#ifdef CONFIG_THERMAL_EMULATION
+       result = device_create_file(&tz->device, &dev_attr_emul_temp);
+       if (result)
+               goto unregister;
+#endif
        /* Create policy attribute */
        result = device_create_file(&tz->device, &dev_attr_policy);
        if (result)
@@ -1711,7 +1775,8 @@ static struct genl_multicast_group thermal_event_mcgrp = {
        .name = THERMAL_GENL_MCAST_GROUP_NAME,
 };
 
-int thermal_generate_netlink_event(u32 orig, enum events event)
+int thermal_generate_netlink_event(struct thermal_zone_device *tz,
+                                       enum events event)
 {
        struct sk_buff *skb;
        struct nlattr *attr;
@@ -1721,6 +1786,9 @@ int thermal_generate_netlink_event(u32 orig, enum events event)
        int result;
        static unsigned int thermal_event_seqnum;
 
+       if (!tz)
+               return -EINVAL;
+
        /* allocate memory */
        size = nla_total_size(sizeof(struct thermal_genl_event)) +
               nla_total_size(0);
@@ -1755,7 +1823,7 @@ int thermal_generate_netlink_event(u32 orig, enum events event)
 
        memset(thermal_event, 0, sizeof(struct thermal_genl_event));
 
-       thermal_event->orig = orig;
+       thermal_event->orig = tz->id;
        thermal_event->event = event;
 
        /* send multicast genetlink message */
@@ -1767,7 +1835,7 @@ int thermal_generate_netlink_event(u32 orig, enum events event)
 
        result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC);
        if (result)
-               pr_info("failed to send netlink event:%d\n", result);
+               dev_err(&tz->device, "Failed to send netlink event:%d", result);
 
        return result;
 }
@@ -1807,6 +1875,7 @@ static int __init thermal_init(void)
                idr_destroy(&thermal_cdev_idr);
                mutex_destroy(&thermal_idr_lock);
                mutex_destroy(&thermal_list_lock);
+               return result;
        }
        result = genetlink_init();
        return result;
index 57d6b29c039cacd8dbf32d5044101211fdfe1c0c..5722eaf302944e5b3e56f20b88703d1a71973242 100644 (file)
@@ -201,26 +201,6 @@ static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
        return pdata->get_context_loss_count(up->dev);
 }
 
-static void serial_omap_set_forceidle(struct uart_omap_port *up)
-{
-       struct omap_uart_port_info *pdata = up->dev->platform_data;
-
-       if (!pdata || !pdata->set_forceidle)
-               return;
-
-       pdata->set_forceidle(up->dev);
-}
-
-static void serial_omap_set_noidle(struct uart_omap_port *up)
-{
-       struct omap_uart_port_info *pdata = up->dev->platform_data;
-
-       if (!pdata || !pdata->set_noidle)
-               return;
-
-       pdata->set_noidle(up->dev);
-}
-
 static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
 {
        struct omap_uart_port_info *pdata = up->dev->platform_data;
@@ -279,8 +259,6 @@ static void serial_omap_stop_tx(struct uart_port *port)
                serial_out(up, UART_IER, up->ier);
        }
 
-       serial_omap_set_forceidle(up);
-
        pm_runtime_mark_last_busy(up->dev);
        pm_runtime_put_autosuspend(up->dev);
 }
@@ -348,7 +326,6 @@ static void serial_omap_start_tx(struct uart_port *port)
 
        pm_runtime_get_sync(up->dev);
        serial_omap_enable_ier_thri(up);
-       serial_omap_set_noidle(up);
        pm_runtime_mark_last_busy(up->dev);
        pm_runtime_put_autosuspend(up->dev);
 }
index f00c74978b7ad09ee36e9a3ff048a7465ab68a29..ffa6b004a84bec3b4d58b9e5b6bd515d499e603d 100644 (file)
@@ -420,18 +420,27 @@ static int dwc3_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+       if (node) {
+               dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
+               dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1);
+       } else {
+               dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+               dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
+       }
+
        if (IS_ERR_OR_NULL(dwc->usb2_phy)) {
                dev_err(dev, "no usb2 phy configured\n");
                return -EPROBE_DEFER;
        }
 
-       dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
        if (IS_ERR_OR_NULL(dwc->usb3_phy)) {
                dev_err(dev, "no usb3 phy configured\n");
                return -EPROBE_DEFER;
        }
 
+       usb_phy_set_suspend(dwc->usb2_phy, 0);
+       usb_phy_set_suspend(dwc->usb3_phy, 0);
+
        spin_lock_init(&dwc->lock);
        platform_set_drvdata(pdev, dwc);
 
@@ -450,8 +459,7 @@ static int dwc3_probe(struct platform_device *pdev)
        else
                dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
 
-       if (of_get_property(node, "tx-fifo-resize", NULL))
-               dwc->needs_fifo_resize = true;
+       dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
 
        pm_runtime_enable(dev);
        pm_runtime_get_sync(dev);
@@ -550,9 +558,9 @@ err0:
 static int dwc3_remove(struct platform_device *pdev)
 {
        struct dwc3     *dwc = platform_get_drvdata(pdev);
-       struct resource *res;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       usb_phy_set_suspend(dwc->usb2_phy, 1);
+       usb_phy_set_suspend(dwc->usb3_phy, 1);
 
        pm_runtime_put(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
@@ -581,11 +589,22 @@ static int dwc3_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id of_dwc3_match[] = {
+       {
+               .compatible = "synopsys,dwc3"
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, of_dwc3_match);
+#endif
+
 static struct platform_driver dwc3_driver = {
        .probe          = dwc3_probe,
        .remove         = dwc3_remove,
        .driver         = {
                .name   = "dwc3",
+               .of_match_table = of_match_ptr(of_dwc3_match),
        },
 };
 
index aae5328ac771b69ff0c516d75e0751379a6c9bd6..e25c98a7c2200977f9848eede8d064cec4461b9b 100644 (file)
@@ -23,8 +23,6 @@
 #include <linux/usb/nop-usb-xceiv.h>
 #include <linux/of.h>
 
-#include "core.h"
-
 struct dwc3_exynos {
        struct platform_device  *dwc3;
        struct platform_device  *usb2_phy;
index f31867fd2574340637f37abca69b3fc9b018b0fa..352f397d4aae3e7c17a22d5b47e0887ee3594303 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/dwc3-omap.h>
+#include <linux/usb/dwc3-omap.h>
+#include <linux/pm_runtime.h>
 #include <linux/dma-mapping.h>
 #include <linux/ioport.h>
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/of_platform.h>
 
 #include <linux/usb/otg.h>
-#include <linux/usb/nop-usb-xceiv.h>
-
-#include "core.h"
 
 /*
  * All these registers belong to OMAP's Wrapper around the
 
 /* SYSCONFIG REGISTER */
 #define USBOTGSS_SYSCONFIG_DMADISABLE          (1 << 16)
-#define USBOTGSS_SYSCONFIG_STANDBYMODE(x)      ((x) << 4)
-
-#define USBOTGSS_STANDBYMODE_FORCE_STANDBY     0
-#define USBOTGSS_STANDBYMODE_NO_STANDBY                1
-#define USBOTGSS_STANDBYMODE_SMART_STANDBY     2
-#define USBOTGSS_STANDBYMODE_SMART_WAKEUP      3
-
-#define USBOTGSS_STANDBYMODE_MASK              (0x03 << 4)
-
-#define USBOTGSS_SYSCONFIG_IDLEMODE(x)         ((x) << 2)
-
-#define USBOTGSS_IDLEMODE_FORCE_IDLE           0
-#define USBOTGSS_IDLEMODE_NO_IDLE              1
-#define USBOTGSS_IDLEMODE_SMART_IDLE           2
-#define USBOTGSS_IDLEMODE_SMART_WAKEUP         3
-
-#define USBOTGSS_IDLEMODE_MASK                 (0x03 << 2)
 
 /* IRQ_EOI REGISTER */
 #define USBOTGSS_IRQ_EOI_LINE_NUMBER           (1 << 0)
@@ -133,9 +116,6 @@ struct dwc3_omap {
        /* device lock */
        spinlock_t              lock;
 
-       struct platform_device  *dwc3;
-       struct platform_device  *usb2_phy;
-       struct platform_device  *usb3_phy;
        struct device           *dev;
 
        int                     irq;
@@ -147,6 +127,8 @@ struct dwc3_omap {
        u32                     dma_status:1;
 };
 
+struct dwc3_omap               *_omap;
+
 static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
 {
        return readl(base + offset);
@@ -157,59 +139,59 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
        writel(value, base + offset);
 }
 
-static int dwc3_omap_register_phys(struct dwc3_omap *omap)
+int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
 {
-       struct nop_usb_xceiv_platform_data pdata;
-       struct platform_device  *pdev;
-       int                     ret;
-
-       memset(&pdata, 0x00, sizeof(pdata));
-
-       pdev = platform_device_alloc("nop_usb_xceiv", 0);
-       if (!pdev)
-               return -ENOMEM;
-
-       omap->usb2_phy = pdev;
-       pdata.type = USB_PHY_TYPE_USB2;
-
-       ret = platform_device_add_data(omap->usb2_phy, &pdata, sizeof(pdata));
-       if (ret)
-               goto err1;
+       u32                     val;
+       struct dwc3_omap        *omap = _omap;
 
-       pdev = platform_device_alloc("nop_usb_xceiv", 1);
-       if (!pdev) {
-               ret = -ENOMEM;
-               goto err1;
+       if (!omap)
+               return -EPROBE_DEFER;
+
+       switch (status) {
+       case OMAP_DWC3_ID_GROUND:
+               dev_dbg(omap->dev, "ID GND\n");
+
+               val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
+               val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG
+                               | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
+                               | USBOTGSS_UTMI_OTG_STATUS_SESSEND);
+               val |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID
+                               | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
+               dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
+               break;
+
+       case OMAP_DWC3_VBUS_VALID:
+               dev_dbg(omap->dev, "VBUS Connect\n");
+
+               val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
+               val &= ~USBOTGSS_UTMI_OTG_STATUS_SESSEND;
+               val |= USBOTGSS_UTMI_OTG_STATUS_IDDIG
+                               | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
+                               | USBOTGSS_UTMI_OTG_STATUS_SESSVALID
+                               | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
+               dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
+               break;
+
+       case OMAP_DWC3_ID_FLOAT:
+       case OMAP_DWC3_VBUS_OFF:
+               dev_dbg(omap->dev, "VBUS Disconnect\n");
+
+               val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
+               val &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSVALID
+                               | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
+                               | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT);
+               val |= USBOTGSS_UTMI_OTG_STATUS_SESSEND
+                               | USBOTGSS_UTMI_OTG_STATUS_IDDIG;
+               dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
+               break;
+
+       default:
+               dev_dbg(omap->dev, "ID float\n");
        }
 
-       omap->usb3_phy = pdev;
-       pdata.type = USB_PHY_TYPE_USB3;
-
-       ret = platform_device_add_data(omap->usb3_phy, &pdata, sizeof(pdata));
-       if (ret)
-               goto err2;
-
-       ret = platform_device_add(omap->usb2_phy);
-       if (ret)
-               goto err2;
-
-       ret = platform_device_add(omap->usb3_phy);
-       if (ret)
-               goto err3;
-
-       return 0;
-
-err3:
-       platform_device_del(omap->usb2_phy);
-
-err2:
-       platform_device_put(omap->usb3_phy);
-
-err1:
-       platform_device_put(omap->usb2_phy);
-
-       return ret;
+       return IRQ_HANDLED;
 }
+EXPORT_SYMBOL_GPL(dwc3_omap_mailbox);
 
 static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
 {
@@ -262,26 +244,46 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
        return IRQ_HANDLED;
 }
 
+static int dwc3_omap_remove_core(struct device *dev, void *c)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+
+       platform_device_unregister(pdev);
+
+       return 0;
+}
+
+static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32);
+
+static int dwc3_omap_set_dmamask(struct device *dev, void *c)
+{
+       dev->dma_mask = &dwc3_omap_dma_mask;
+       return 0;
+}
+
 static int dwc3_omap_probe(struct platform_device *pdev)
 {
-       struct dwc3_omap_data   *pdata = pdev->dev.platform_data;
        struct device_node      *node = pdev->dev.of_node;
 
-       struct platform_device  *dwc3;
        struct dwc3_omap        *omap;
        struct resource         *res;
        struct device           *dev = &pdev->dev;
 
-       int                     size;
        int                     ret = -ENOMEM;
        int                     irq;
 
-       const u32               *utmi_mode;
+       int                     utmi_mode;
+
        u32                     reg;
 
        void __iomem            *base;
        void                    *context;
 
+       if (!node) {
+               dev_err(dev, "device node not found\n");
+               return -EINVAL;
+       }
+
        omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
        if (!omap) {
                dev_err(dev, "not enough memory\n");
@@ -290,13 +292,13 @@ static int dwc3_omap_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, omap);
 
-       irq = platform_get_irq(pdev, 1);
+       irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(dev, "missing IRQ resource\n");
                return -EINVAL;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(dev, "missing memory base resource\n");
                return -EINVAL;
@@ -308,58 +310,46 @@ static int dwc3_omap_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       ret = dwc3_omap_register_phys(omap);
-       if (ret) {
-               dev_err(dev, "couldn't register PHYs\n");
-               return ret;
-       }
-
-       dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
-       if (!dwc3) {
-               dev_err(dev, "couldn't allocate dwc3 device\n");
-               return -ENOMEM;
-       }
-
        context = devm_kzalloc(dev, resource_size(res), GFP_KERNEL);
        if (!context) {
                dev_err(dev, "couldn't allocate dwc3 context memory\n");
-               goto err2;
+               return -ENOMEM;
        }
 
        spin_lock_init(&omap->lock);
-       dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
 
-       dwc3->dev.parent = dev;
-       dwc3->dev.dma_mask = dev->dma_mask;
-       dwc3->dev.dma_parms = dev->dma_parms;
        omap->resource_size = resource_size(res);
        omap->context   = context;
        omap->dev       = dev;
        omap->irq       = irq;
        omap->base      = base;
-       omap->dwc3      = dwc3;
+
+       /*
+        * REVISIT if we ever have two instances of the wrapper, we will be
+        * in big trouble
+        */
+       _omap   = omap;
+
+       pm_runtime_enable(dev);
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0) {
+               dev_err(dev, "get_sync failed with err %d\n", ret);
+               return ret;
+       }
 
        reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
 
-       utmi_mode = of_get_property(node, "utmi-mode", &size);
-       if (utmi_mode && size == sizeof(*utmi_mode)) {
-               reg |= *utmi_mode;
-       } else {
-               if (!pdata) {
-                       dev_dbg(dev, "missing platform data\n");
-               } else {
-                       switch (pdata->utmi_mode) {
-                       case DWC3_OMAP_UTMI_MODE_SW:
-                               reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
-                               break;
-                       case DWC3_OMAP_UTMI_MODE_HW:
-                               reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
-                               break;
-                       default:
-                               dev_dbg(dev, "UNKNOWN utmi mode %d\n",
-                                               pdata->utmi_mode);
-                       }
-               }
+       of_property_read_u32(node, "utmi-mode", &utmi_mode);
+
+       switch (utmi_mode) {
+       case DWC3_OMAP_UTMI_MODE_SW:
+               reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+               break;
+       case DWC3_OMAP_UTMI_MODE_HW:
+               reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+               break;
+       default:
+               dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode);
        }
 
        dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
@@ -368,21 +358,12 @@ static int dwc3_omap_probe(struct platform_device *pdev)
        reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
        omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
 
-       /* Set No-Idle and No-Standby */
-       reg &= ~(USBOTGSS_STANDBYMODE_MASK
-                       | USBOTGSS_IDLEMODE_MASK);
-
-       reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY)
-               | USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE));
-
-       dwc3_omap_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
-
        ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
                        "dwc3-omap", omap);
        if (ret) {
                dev_err(dev, "failed to request IRQ #%d --> %d\n",
                                omap->irq, ret);
-               goto err2;
+               return ret;
        }
 
        /* enable all IRQs */
@@ -401,50 +382,40 @@ static int dwc3_omap_probe(struct platform_device *pdev)
 
        dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
 
-       ret = platform_device_add_resources(dwc3, pdev->resource,
-                       pdev->num_resources);
+       ret = of_platform_populate(node, NULL, NULL, dev);
        if (ret) {
-               dev_err(dev, "couldn't add resources to dwc3 device\n");
-               goto err2;
+               dev_err(&pdev->dev, "failed to create dwc3 core\n");
+               return ret;
        }
 
-       ret = platform_device_add(dwc3);
-       if (ret) {
-               dev_err(dev, "failed to register dwc3 device\n");
-               goto err2;
-       }
+       device_for_each_child(&pdev->dev, NULL, dwc3_omap_set_dmamask);
 
        return 0;
-
-err2:
-       platform_device_put(dwc3);
-       return ret;
 }
 
 static int dwc3_omap_remove(struct platform_device *pdev)
 {
-       struct dwc3_omap        *omap = platform_get_drvdata(pdev);
+       pm_runtime_put_sync(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+       device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core);
 
-       platform_device_unregister(omap->dwc3);
-       platform_device_unregister(omap->usb2_phy);
-       platform_device_unregister(omap->usb3_phy);
        return 0;
 }
 
-static const struct of_device_id of_dwc3_matach[] = {
+static const struct of_device_id of_dwc3_match[] = {
        {
-               "ti,dwc3",
+               .compatible =   "ti,dwc3"
        },
        { },
 };
-MODULE_DEVICE_TABLE(of, of_dwc3_matach);
+MODULE_DEVICE_TABLE(of, of_dwc3_match);
 
 static struct platform_driver dwc3_omap_driver = {
        .probe          = dwc3_omap_probe,
        .remove         = dwc3_omap_remove,
        .driver         = {
                .name   = "omap-dwc3",
-               .of_match_table = of_dwc3_matach,
+               .of_match_table = of_dwc3_match,
        },
 };
 
index 7d70f44567d2a80fb9b5cf459f36ac0f61dafdf3..e8d77689a322d941042643a980e423545f7bc365 100644 (file)
@@ -45,8 +45,6 @@
 #include <linux/usb/otg.h>
 #include <linux/usb/nop-usb-xceiv.h>
 
-#include "core.h"
-
 /* FIXME define these in <linux/pci_ids.h> */
 #define PCI_VENDOR_ID_SYNOPSYS         0x16c3
 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3        0xabcd
index 09835b667ae04b4184f0cf7f855572bbc1732553..cb47bf2f929a842c73809e59b4ccff43c20539a2 100644 (file)
@@ -2123,7 +2123,6 @@ static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed)
 
 static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 {
-       struct dwc3_gadget_ep_cmd_params params;
        struct dwc3_ep          *dep;
        int                     ret;
        u32                     reg;
@@ -2131,8 +2130,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 
        dev_vdbg(dwc->dev, "%s\n", __func__);
 
-       memset(&params, 0x00, sizeof(params));
-
        reg = dwc3_readl(dwc->regs, DWC3_DSTS);
        speed = reg & DWC3_DSTS_CONNECTSPD;
        dwc->speed = speed;
index 3a21c5d683c04f0380aa1606045b35375d26143c..2d2975df226574d18b341e1199b22ecc7b0f3db9 100644 (file)
@@ -155,8 +155,9 @@ config USB_EHCI_MXC
          Variation of ARC USB block used in some Freescale chips.
 
 config USB_EHCI_HCD_OMAP
-       bool "EHCI support for OMAP3 and later chips"
+       tristate "EHCI support for OMAP3 and later chips"
        depends on USB_EHCI_HCD && ARCH_OMAP
+       select NOP_USB_XCEIV
        default y
        ---help---
          Enables support for the on-chip EHCI controller on
index 001fbff2fdefedbb346f71db705f8e687201e454..56de4106c8b3607bdd62a9ae01a46daafb1e52e4 100644 (file)
@@ -27,6 +27,7 @@ obj-$(CONFIG_USB_EHCI_HCD)    += ehci-hcd.o
 obj-$(CONFIG_USB_EHCI_PCI)     += ehci-pci.o
 obj-$(CONFIG_USB_EHCI_HCD_PLATFORM)    += ehci-platform.o
 obj-$(CONFIG_USB_EHCI_MXC)     += ehci-mxc.o
+obj-$(CONFIG_USB_EHCI_HCD_OMAP)        += ehci-omap.o
 
 obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
 obj-$(CONFIG_USB_ISP116X_HCD)  += isp116x-hcd.o
index 416a6dce5e11c8adeeed2eef487b93c93dd5d728..442b7915ab81c6ddd7ecb52f3f4eb089e1ec840d 100644 (file)
@@ -1251,11 +1251,6 @@ MODULE_LICENSE ("GPL");
 #define PLATFORM_DRIVER                ehci_hcd_sh_driver
 #endif
 
-#ifdef CONFIG_USB_EHCI_HCD_OMAP
-#include "ehci-omap.c"
-#define        PLATFORM_DRIVER         ehci_hcd_omap_driver
-#endif
-
 #ifdef CONFIG_PPC_PS3
 #include "ehci-ps3.c"
 #define        PS3_SYSTEM_BUS_DRIVER   ps3_ehci_driver
@@ -1345,6 +1340,7 @@ MODULE_LICENSE ("GPL");
        !IS_ENABLED(CONFIG_USB_EHCI_HCD_PLATFORM) && \
        !IS_ENABLED(CONFIG_USB_CHIPIDEA_HOST) && \
        !IS_ENABLED(CONFIG_USB_EHCI_MXC) && \
+       !IS_ENABLED(CONFIG_USB_EHCI_HCD_OMAP) && \
        !defined(PLATFORM_DRIVER) && \
        !defined(PS3_SYSTEM_BUS_DRIVER) && \
        !defined(OF_PLATFORM_DRIVER) && \
index 99899e808c6afc83af6e428fa96e2e781983effd..56c8e462ed5e2ebd6a7b91020662f7b990531dd4 100644 (file)
@@ -4,10 +4,11 @@
  * Bus Glue for the EHCI controllers in OMAP3/4
  * Tested on several OMAP3 boards, and OMAP4 Pandaboard
  *
- * Copyright (C) 2007-2011 Texas Instruments, Inc.
+ * Copyright (C) 2007-2013 Texas Instruments, Inc.
  *     Author: Vikram Pandita <vikram.pandita@ti.com>
  *     Author: Anand Gadiyar <gadiyar@ti.com>
  *     Author: Keshava Munegowda <keshava_mgowda@ti.com>
+ *     Author: Roger Quadros <rogerq@ti.com>
  *
  * Copyright (C) 2009 Nokia Corporation
  *     Contact: Felipe Balbi <felipe.balbi@nokia.com>
  *     - convert to use hwmod and runtime PM
  */
 
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/usb/ulpi.h>
-#include <linux/regulator/consumer.h>
 #include <linux/pm_runtime.h>
 #include <linux/gpio.h>
 #include <linux/clk.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/of.h>
+#include <linux/dma-mapping.h>
+
+#include "ehci.h"
 
 #include <linux/platform_data/usb-omap.h>
 
 #define        EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT             8
 #define        EHCI_INSNREG05_ULPI_WRDATA_SHIFT                0
 
-/*-------------------------------------------------------------------------*/
+#define DRIVER_DESC "OMAP-EHCI Host Controller driver"
+
+static const char hcd_name[] = "ehci-omap";
 
-static const struct hc_driver ehci_omap_hc_driver;
+/*-------------------------------------------------------------------------*/
 
+struct omap_hcd {
+       struct usb_phy *phy[OMAP3_HS_USB_PORTS]; /* one PHY for each port */
+       int nports;
+};
 
 static inline void ehci_write(void __iomem *base, u32 reg, u32 val)
 {
@@ -72,100 +87,31 @@ static inline u32 ehci_read(void __iomem *base, u32 reg)
        return __raw_readl(base + reg);
 }
 
-
-static void omap_ehci_soft_phy_reset(struct usb_hcd *hcd, u8 port)
-{
-       unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-       unsigned reg = 0;
-
-       reg = ULPI_FUNC_CTRL_RESET
-               /* FUNCTION_CTRL_SET register */
-               | (ULPI_SET(ULPI_FUNC_CTRL) << EHCI_INSNREG05_ULPI_REGADD_SHIFT)
-               /* Write */
-               | (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT)
-               /* PORTn */
-               | ((port + 1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT)
-               /* start ULPI access*/
-               | (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT);
-
-       ehci_write(hcd->regs, EHCI_INSNREG05_ULPI, reg);
-
-       /* Wait for ULPI access completion */
-       while ((ehci_read(hcd->regs, EHCI_INSNREG05_ULPI)
-                       & (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) {
-               cpu_relax();
-
-               if (time_after(jiffies, timeout)) {
-                       dev_dbg(hcd->self.controller,
-                                       "phy reset operation timed out\n");
-                       break;
-               }
-       }
-}
-
 static int omap_ehci_init(struct usb_hcd *hcd)
 {
-       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
-       int                     rc;
-       struct ehci_hcd_omap_platform_data      *pdata;
-
-       pdata = hcd->self.controller->platform_data;
-
-       /* Hold PHYs in reset while initializing EHCI controller */
-       if (pdata->phy_reset) {
-               if (gpio_is_valid(pdata->reset_gpio_port[0]))
-                       gpio_set_value_cansleep(pdata->reset_gpio_port[0], 0);
-
-               if (gpio_is_valid(pdata->reset_gpio_port[1]))
-                       gpio_set_value_cansleep(pdata->reset_gpio_port[1], 0);
-
-               /* Hold the PHY in RESET for enough time till DIR is high */
-               udelay(10);
-       }
-
-       /* Soft reset the PHY using PHY reset command over ULPI */
-       if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY)
-               omap_ehci_soft_phy_reset(hcd, 0);
-       if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY)
-               omap_ehci_soft_phy_reset(hcd, 1);
+       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       int rc;
 
        /* we know this is the memory we want, no need to ioremap again */
        ehci->caps = hcd->regs;
 
        rc = ehci_setup(hcd);
 
-       if (pdata->phy_reset) {
-               /* Hold the PHY in RESET for enough time till
-                * PHY is settled and ready
-                */
-               udelay(10);
-
-               if (gpio_is_valid(pdata->reset_gpio_port[0]))
-                       gpio_set_value_cansleep(pdata->reset_gpio_port[0], 1);
-
-               if (gpio_is_valid(pdata->reset_gpio_port[1]))
-                       gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1);
-       }
-
        return rc;
 }
 
-static void disable_put_regulator(
-               struct ehci_hcd_omap_platform_data *pdata)
-{
-       int i;
-
-       for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
-               if (pdata->regulator[i]) {
-                       regulator_disable(pdata->regulator[i]);
-                       regulator_put(pdata->regulator[i]);
-               }
-       }
-}
-
 /* configure so an HC device and id are always provided */
 /* always called with process context; sleeping is OK */
 
+static struct hc_driver __read_mostly ehci_omap_hc_driver;
+
+static const struct ehci_driver_overrides ehci_omap_overrides __initdata = {
+       .reset = omap_ehci_init,
+       .extra_priv_size = sizeof(struct omap_hcd),
+};
+
+static u64 omap_ehci_dma_mask = DMA_BIT_MASK(32);
+
 /**
  * ehci_hcd_omap_probe - initialize TI-based HCDs
  *
@@ -175,15 +121,15 @@ static void disable_put_regulator(
  */
 static int ehci_hcd_omap_probe(struct platform_device *pdev)
 {
-       struct device                           *dev = &pdev->dev;
-       struct ehci_hcd_omap_platform_data      *pdata = dev->platform_data;
-       struct resource                         *res;
-       struct usb_hcd                          *hcd;
-       void __iomem                            *regs;
-       int                                     ret = -ENODEV;
-       int                                     irq;
-       int                                     i;
-       char                                    supply[7];
+       struct device *dev = &pdev->dev;
+       struct usbhs_omap_platform_data *pdata = dev->platform_data;
+       struct resource *res;
+       struct usb_hcd  *hcd;
+       void __iomem *regs;
+       int ret = -ENODEV;
+       int irq;
+       int i;
+       struct omap_hcd *omap;
 
        if (usb_disabled())
                return -ENODEV;
@@ -193,52 +139,75 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       irq = platform_get_irq_byname(pdev, "ehci-irq");
-       if (irq < 0) {
-               dev_err(dev, "EHCI irq failed\n");
+       /* For DT boot, get platform data from parent. i.e. usbhshost */
+       if (dev->of_node) {
+               pdata = dev->parent->platform_data;
+               dev->platform_data = pdata;
+       }
+
+       if (!pdata) {
+               dev_err(dev, "Missing platform data\n");
                return -ENODEV;
        }
 
-       res =  platform_get_resource_byname(pdev,
-                               IORESOURCE_MEM, "ehci");
-       if (!res) {
-               dev_err(dev, "UHH EHCI get resource failed\n");
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(dev, "EHCI irq failed\n");
                return -ENODEV;
        }
 
-       regs = ioremap(res->start, resource_size(res));
+       res =  platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       regs = devm_request_and_ioremap(dev, res);
        if (!regs) {
-               dev_err(dev, "UHH EHCI ioremap failed\n");
-               return -ENOMEM;
+               dev_err(dev, "Resource request/ioremap failed\n");
+               return -EADDRNOTAVAIL;
        }
 
+       /*
+        * Right now device-tree probed devices don't get dma_mask set.
+        * Since shared usb code relies on it, set it here for now.
+        * Once we have dma capability bindings this can go away.
+        */
+       if (!pdev->dev.dma_mask)
+               pdev->dev.dma_mask = &omap_ehci_dma_mask;
+
        hcd = usb_create_hcd(&ehci_omap_hc_driver, dev,
                        dev_name(dev));
        if (!hcd) {
-               dev_err(dev, "failed to create hcd with err %d\n", ret);
-               ret = -ENOMEM;
-               goto err_io;
+               dev_err(dev, "Failed to create HCD\n");
+               return -ENOMEM;
        }
 
        hcd->rsrc_start = res->start;
        hcd->rsrc_len = resource_size(res);
        hcd->regs = regs;
 
-       /* get ehci regulator and enable */
-       for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
-               if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) {
-                       pdata->regulator[i] = NULL;
-                       continue;
-               }
-               snprintf(supply, sizeof(supply), "hsusb%d", i);
-               pdata->regulator[i] = regulator_get(dev, supply);
-               if (IS_ERR(pdata->regulator[i])) {
-                       pdata->regulator[i] = NULL;
-                       dev_dbg(dev,
-                       "failed to get ehci port%d regulator\n", i);
-               } else {
-                       regulator_enable(pdata->regulator[i]);
+       omap = (struct omap_hcd *)hcd_to_ehci(hcd)->priv;
+       omap->nports = pdata->nports;
+
+       platform_set_drvdata(pdev, hcd);
+
+       /* get the PHY devices if needed */
+       for (i = 0 ; i < omap->nports ; i++) {
+               struct usb_phy *phy;
+
+               /* get the PHY device */
+               if (dev->of_node)
+                       phy = devm_usb_get_phy_by_phandle(dev, "phys", i);
+               else
+                       phy = devm_usb_get_phy_dev(dev, i);
+               if (IS_ERR(phy) || !phy) {
+                       /* Don't bail out if PHY is not absolutely necessary */
+                       if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY)
+                               continue;
+
+                       ret = IS_ERR(phy) ? PTR_ERR(phy) : -ENODEV;
+                       dev_err(dev, "Can't get PHY device for port %d: %d\n",
+                                       i, ret);
+                       goto err_phy;
                }
+
+               omap->phy[i] = phy;
        }
 
        pm_runtime_enable(dev);
@@ -262,16 +231,29 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
                goto err_pm_runtime;
        }
 
+       /* Bring PHYs out of reset */
+       for (i = 0; i < omap->nports; i++) {
+               if (!omap->phy[i])
+                       continue;
+
+               usb_phy_init(omap->phy[i]);
+               /* bring PHY out of suspend */
+               usb_phy_set_suspend(omap->phy[i], 0);
+       }
 
        return 0;
 
 err_pm_runtime:
-       disable_put_regulator(pdata);
        pm_runtime_put_sync(dev);
+
+err_phy:
+       for (i = 0; i < omap->nports; i++) {
+               if (omap->phy[i])
+                       usb_phy_shutdown(omap->phy[i]);
+       }
+
        usb_put_hcd(hcd);
 
-err_io:
-       iounmap(regs);
        return ret;
 }
 
@@ -286,14 +268,19 @@ err_io:
  */
 static int ehci_hcd_omap_remove(struct platform_device *pdev)
 {
-       struct device *dev                              = &pdev->dev;
-       struct usb_hcd *hcd                             = dev_get_drvdata(dev);
+       struct device *dev = &pdev->dev;
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct omap_hcd *omap = (struct omap_hcd *)hcd_to_ehci(hcd)->priv;
+       int i;
 
        usb_remove_hcd(hcd);
-       disable_put_regulator(dev->platform_data);
-       iounmap(hcd->regs);
-       usb_put_hcd(hcd);
 
+       for (i = 0; i < omap->nports; i++) {
+               if (omap->phy[i])
+                       usb_phy_shutdown(omap->phy[i]);
+       }
+
+       usb_put_hcd(hcd);
        pm_runtime_put_sync(dev);
        pm_runtime_disable(dev);
 
@@ -308,6 +295,13 @@ static void ehci_hcd_omap_shutdown(struct platform_device *pdev)
                hcd->driver->shutdown(hcd);
 }
 
+static const struct of_device_id omap_ehci_dt_ids[] = {
+       { .compatible = "ti,ehci-omap" },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, omap_ehci_dt_ids);
+
 static struct platform_driver ehci_hcd_omap_driver = {
        .probe                  = ehci_hcd_omap_probe,
        .remove                 = ehci_hcd_omap_remove,
@@ -315,56 +309,34 @@ static struct platform_driver ehci_hcd_omap_driver = {
        /*.suspend              = ehci_hcd_omap_suspend, */
        /*.resume               = ehci_hcd_omap_resume, */
        .driver = {
-               .name           = "ehci-omap",
+               .name           = hcd_name,
+               .of_match_table = of_match_ptr(omap_ehci_dt_ids),
        }
 };
 
 /*-------------------------------------------------------------------------*/
 
-static const struct hc_driver ehci_omap_hc_driver = {
-       .description            = hcd_name,
-       .product_desc           = "OMAP-EHCI Host Controller",
-       .hcd_priv_size          = sizeof(struct ehci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
-
-       /*
-        * basic lifecycle operations
-        */
-       .reset                  = omap_ehci_init,
-       .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,
+static int __init ehci_omap_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
 
-       /*
-        * scheduling support
-        */
-       .get_frame_number       = ehci_get_frame,
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 
-       /*
-        * root hub support
-        */
-       .hub_status_data        = ehci_hub_status_data,
-       .hub_control            = ehci_hub_control,
-       .bus_suspend            = ehci_bus_suspend,
-       .bus_resume             = ehci_bus_resume,
+       ehci_init_driver(&ehci_omap_hc_driver, &ehci_omap_overrides);
+       return platform_driver_register(&ehci_hcd_omap_driver);
+}
+module_init(ehci_omap_init);
 
-       .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
-};
+static void __exit ehci_omap_cleanup(void)
+{
+       platform_driver_unregister(&ehci_hcd_omap_driver);
+}
+module_exit(ehci_omap_cleanup);
 
 MODULE_ALIAS("platform:ehci-omap");
 MODULE_AUTHOR("Texas Instruments, Inc.");
 MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
 
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
index eb35d96302373c7e9e2fa3345f7d81008aa802f9..ddfc31427bc09e1fc95c63ea40f006ad00631e66 100644 (file)
@@ -31,6 +31,8 @@
 
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/dma-mapping.h>
 
 /*-------------------------------------------------------------------------*/
 
@@ -112,6 +114,8 @@ static const struct hc_driver ohci_omap3_hc_driver = {
 
 /*-------------------------------------------------------------------------*/
 
+static u64 omap_ohci_dma_mask = DMA_BIT_MASK(32);
+
 /*
  * configure so an HC device and id are always provided
  * always called with process context; sleeping is OK
@@ -141,14 +145,13 @@ static int ohci_hcd_omap3_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       irq = platform_get_irq_byname(pdev, "ohci-irq");
+       irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(dev, "OHCI irq failed\n");
                return -ENODEV;
        }
 
-       res = platform_get_resource_byname(pdev,
-                               IORESOURCE_MEM, "ohci");
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(dev, "UHH OHCI get resource failed\n");
                return -ENOMEM;
@@ -160,6 +163,13 @@ static int ohci_hcd_omap3_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       /*
+        * Right now device-tree probed devices don't get dma_mask set.
+        * Since shared usb code relies on it, set it here for now.
+        * Once we have dma capability bindings this can go away.
+        */
+       if (!pdev->dev.dma_mask)
+               pdev->dev.dma_mask = &omap_ohci_dma_mask;
 
        hcd = usb_create_hcd(&ohci_omap3_hc_driver, dev,
                        dev_name(dev));
@@ -229,12 +239,20 @@ static void ohci_hcd_omap3_shutdown(struct platform_device *pdev)
                hcd->driver->shutdown(hcd);
 }
 
+static const struct of_device_id omap_ohci_dt_ids[] = {
+       { .compatible = "ti,ohci-omap3" },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, omap_ohci_dt_ids);
+
 static struct platform_driver ohci_hcd_omap3_driver = {
        .probe          = ohci_hcd_omap3_probe,
        .remove         = ohci_hcd_omap3_remove,
        .shutdown       = ohci_hcd_omap3_shutdown,
        .driver         = {
                .name   = "ohci-omap3",
+               .of_match_table = of_match_ptr(omap_ohci_dt_ids),
        },
 };
 
index 23a0b7f0892d3d51d784fe708a05159317883e1b..9f42916b8b690639c97ac0351ebd9b1e1dbc7833 100644 (file)
@@ -9,8 +9,7 @@ config USB_MUSB_HDRC
        depends on USB && USB_GADGET
        select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN)
        select NOP_USB_XCEIV if (SOC_TI81XX || SOC_AM33XX)
-       select TWL4030_USB if MACH_OMAP_3430SDP
-       select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
+       select OMAP_CONTROL_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
        select USB_OTG_UTILS
        help
          Say Y here if your system has a dual role high speed USB
@@ -49,6 +48,8 @@ config USB_MUSB_TUSB6010
 config USB_MUSB_OMAP2PLUS
        tristate "OMAP2430 and onwards"
        depends on ARCH_OMAP2PLUS
+       select TWL4030_USB if MACH_OMAP_3430SDP
+       select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
 
 config USB_MUSB_AM35X
        tristate "AM35x"
@@ -73,6 +74,7 @@ choice
        default USB_UX500_DMA if USB_MUSB_UX500
        default USB_INVENTRA_DMA if USB_MUSB_OMAP2PLUS || USB_MUSB_BLACKFIN
        default USB_TI_CPPI_DMA if USB_MUSB_DAVINCI
+       default USB_TI_CPPI41_DMA if USB_MUSB_DSPS
        default USB_TUSB_OMAP_DMA if USB_MUSB_TUSB6010
        default MUSB_PIO_ONLY if USB_MUSB_TUSB6010 || USB_MUSB_DA8XX || USB_MUSB_AM35X \
                                || USB_MUSB_DSPS
@@ -99,6 +101,15 @@ config USB_TI_CPPI_DMA
        help
          Enable DMA transfers when TI CPPI DMA is available.
 
+config USB_TI_CPPI41_DMA
+       bool 'TI CPPI41 (DSPS)'
+       depends on USB_MUSB_DSPS
+       help
+        Configure this option to include the CPPI 4.1 support,
+        The CPPI 4.1 DMA engine integrated with musb controller
+        accelarate the usb packet transmission and receception
+        to/from musb endpoints.
+
 config USB_TUSB_OMAP_DMA
        bool 'TUSB 6010'
        depends on USB_MUSB_TUSB6010
index 3b858715b5eaa9808a7b1ac179e7396981943619..e3fd8dc835ce686303c170dd9cbe87da96481eed 100644 (file)
@@ -27,5 +27,6 @@ obj-$(CONFIG_USB_MUSB_UX500)                  += ux500.o
 
 musb_hdrc-$(CONFIG_USB_INVENTRA_DMA)           += musbhsdma.o
 musb_hdrc-$(CONFIG_USB_TI_CPPI_DMA)            += cppi_dma.o
+musb_hdrc-$(CONFIG_USB_TI_CPPI41_DMA)          += cppi41.o cppi41_dma.o
 musb_hdrc-$(CONFIG_USB_TUSB_OMAP_DMA)          += tusb6010_omap.o
 musb_hdrc-$(CONFIG_USB_UX500_DMA)              += ux500_dma.o
diff --git a/drivers/usb/musb/cppi41.c b/drivers/usb/musb/cppi41.c
new file mode 100644 (file)
index 0000000..ac8b22b
--- /dev/null
@@ -0,0 +1,1046 @@
+/*
+ * CPPI 4.1 support
+ *
+ * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
+ *
+ * Based on the PAL CPPI 4.1 implementation
+ * Copyright (C) 1998-2006 Texas Instruments Incorporated
+ *
+ * This file contains the main implementation for CPPI 4.1 common peripherals,
+ * including the DMA Controllers and the Queue Managers.
+ *
+ * This program is free software; you can distribute 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 it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+
+#include "cppi41.h"
+
+#undef CPPI41_DEBUG
+
+#ifdef CPPI41_DEBUG
+#define DBG(format, args...) printk(format, ##args)
+#else
+#define DBG(format, args...)
+#endif
+
+static struct {
+       void *virt_addr;
+       dma_addr_t phys_addr;
+       u32     size;
+} linking_ram[CPPI41_NUM_QUEUE_MGR];
+
+static u32 *allocated_queues[CPPI41_NUM_QUEUE_MGR];
+
+/* First 32 packet descriptors are reserved for unallocated memory regions. */
+static u32 next_desc_index[CPPI41_NUM_QUEUE_MGR] = { 0 };
+static u8  next_mem_rgn[CPPI41_NUM_QUEUE_MGR];
+
+static struct {
+       size_t rgn_size;
+       void *virt_addr;
+       dma_addr_t phys_addr;
+       struct cppi41_queue_obj queue_obj;
+       u8 mem_rgn;
+       u16 q_mgr;
+       u16 q_num;
+       u32 num_desc;
+} dma_teardown[CPPI41_NUM_DMA_BLOCK];
+
+struct cppi41_sched_tbl_t *dma_sched_tbl;
+u32 num_sched_tbl_entry;
+
+struct cppi41_queue_mgr cppi41_queue_mgr[CPPI41_NUM_QUEUE_MGR];
+EXPORT_SYMBOL_GPL(cppi41_queue_mgr);
+
+struct cppi41_dma_block cppi41_dma_block[CPPI41_NUM_DMA_BLOCK];
+EXPORT_SYMBOL_GPL(cppi41_dma_block);
+/******************** CPPI 4.1 Functions (External Interface) *****************/
+
+int cppi41_queue_mgr_init(u8 q_mgr, dma_addr_t rgn0_base, u16 rgn0_size)
+{
+       void __iomem *q_mgr_regs;
+       void *ptr;
+
+       if (q_mgr >= cppi41_num_queue_mgr)
+               return -EINVAL;
+
+       q_mgr_regs = cppi41_queue_mgr[q_mgr].q_mgr_rgn_base;
+       ptr = dma_alloc_coherent(NULL, rgn0_size * 4,
+                                &linking_ram[q_mgr].phys_addr,
+                                GFP_KERNEL | GFP_DMA);
+       if (ptr == NULL)
+               return -ENOMEM;
+
+       linking_ram[q_mgr].virt_addr = ptr;
+       linking_ram[q_mgr].size = rgn0_size * 4;
+
+       cppi_writel(linking_ram[q_mgr].phys_addr,
+                       q_mgr_regs + QMGR_LINKING_RAM_RGN0_BASE_REG);
+       DBG("Linking RAM region 0 base @ %p, value: %x\n",
+           q_mgr_regs + QMGR_LINKING_RAM_RGN0_BASE_REG,
+           cppi_readl(q_mgr_regs + QMGR_LINKING_RAM_RGN0_BASE_REG));
+
+       cppi_writel(rgn0_size, q_mgr_regs + QMGR_LINKING_RAM_RGN0_SIZE_REG);
+       DBG("Linking RAM region 0 size @ %p, value: %x\n",
+           q_mgr_regs + QMGR_LINKING_RAM_RGN0_SIZE_REG,
+           cppi_readl(q_mgr_regs + QMGR_LINKING_RAM_RGN0_SIZE_REG));
+
+       ptr = kzalloc(BITS_TO_LONGS(cppi41_queue_mgr[q_mgr].num_queue) *
+                       sizeof(long), GFP_KERNEL);
+       if (ptr == NULL) {
+               pr_err("%s: fail to allocate queue bitmap.\n", __func__);
+               dma_free_coherent(NULL, rgn0_size * 4,
+                                 linking_ram[q_mgr].virt_addr,
+                                 linking_ram[q_mgr].phys_addr);
+               return -ENOMEM;
+       }
+       allocated_queues[q_mgr] = ptr;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppi41_queue_mgr_init);
+
+int cppi41_queue_mgr_uninit(u8 q_mgr)
+{
+       void __iomem *q_mgr_regs;
+
+       if (q_mgr >= cppi41_num_queue_mgr)
+               return -EINVAL;
+
+       q_mgr_regs = cppi41_queue_mgr[q_mgr].q_mgr_rgn_base;
+
+       /* free the Queue Mgr linking ram space */
+       cppi_writel(0,  q_mgr_regs + QMGR_LINKING_RAM_RGN0_BASE_REG);
+       cppi_writel(0, q_mgr_regs + QMGR_LINKING_RAM_RGN0_SIZE_REG);
+       dma_free_coherent(NULL, linking_ram[q_mgr].size,
+                       linking_ram[q_mgr].virt_addr,
+                       linking_ram[q_mgr].phys_addr);
+
+       /* free the allocated queues */
+       kfree(allocated_queues[q_mgr]);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppi41_queue_mgr_uninit);
+
+static void cppi41_disable_schedular(u8 dma_num)
+{
+       struct cppi41_dma_block *dma_block;
+       void __iomem *sched_base;
+
+       dma_block = (struct cppi41_dma_block *)&cppi41_dma_block[dma_num];
+       sched_base = dma_block->sched_ctrl_base;
+
+       cppi_writel(0, sched_base + DMA_SCHED_CTRL_REG);
+       DBG("DMA scheduler control @ %p, value: %x\n",
+               sched_base + DMA_SCHED_CTRL_REG,
+               cppi_readl(sched_base + DMA_SCHED_CTRL_REG));
+}
+
+static void cppi41_enable_schedular(u8 dma_num, int num_ch)
+{
+       struct cppi41_dma_block *dma_block;
+       void __iomem *sched_base;
+
+       if (num_ch == 0) {
+               cppi41_disable_schedular(dma_num);
+               return ;
+       }
+
+       dma_block = (struct cppi41_dma_block *)&cppi41_dma_block[dma_num];
+       sched_base = dma_block->sched_ctrl_base;
+
+       cppi_writel((num_ch - 1) << DMA_SCHED_LAST_ENTRY_SHIFT |
+                    DMA_SCHED_ENABLE_MASK,
+                    sched_base + DMA_SCHED_CTRL_REG);
+
+       DBG("DMA scheduler control @ %p, value: %x\n",
+               sched_base + DMA_SCHED_CTRL_REG,
+               cppi_readl(sched_base + DMA_SCHED_CTRL_REG));
+}
+
+static int cppi41_dma_sched_tbl_init(u8 dma_num, u8 q_mgr)
+{
+       struct cppi41_dma_block *dma_block;
+       u32 val = 0, chnum;
+       int i, j, k = 0, offs, num_ch = 0;
+       void __iomem *sched_base;
+
+       if (!dma_sched_tbl)
+               return -EINVAL;
+
+       dma_block = (struct cppi41_dma_block *)&cppi41_dma_block[dma_num];
+       sched_base = dma_block->sched_table_base;
+
+       cppi41_disable_schedular(dma_num);
+
+       for (i = 0, j = 0; i < num_sched_tbl_entry; i++) {
+               if (dma_sched_tbl[i].enb) {
+                       chnum = dma_sched_tbl[i].dma_ch;
+                       if (!dma_sched_tbl[i].is_tx)
+                               chnum |= 0x80;
+
+                       val |= chnum << j;
+                       j += 8;
+                       num_ch++;
+
+                       if (j % 32 == 0) {
+                               offs = DMA_SCHED_TABLE_WORD_REG(k);
+                               cppi_writel(val, sched_base + offs);
+                               DBG("dma_sched_tbl @ %p, value written: %x\n",
+                                       sched_base + offs, val);
+                               val = j = 0;
+                               k++;
+                       }
+               }
+       }
+
+       cppi41_enable_schedular(dma_num, num_ch);
+
+       return num_ch;
+}
+
+static int cppi41_cfg_sched_tbl(u8 dmanum, u8 qmgr, u8 dmach, u8 is_tx, u8 enb)
+{
+       int index = dmach * 2;
+
+       if (is_tx)
+               index++;
+
+       if (!dma_sched_tbl || !(dma_sched_tbl[index].enb ^ enb))
+               return -EINVAL;
+
+       dma_sched_tbl[index].enb = enb ? 1 : 0;
+
+       return cppi41_dma_sched_tbl_init(dmanum, qmgr);
+}
+
+int cppi41_schedtbl_add_dma_ch(u8 dmanum, u8 qmgr, u8 dma_ch, u8 is_tx)
+{
+       if (!dma_sched_tbl)
+               return -EINVAL;
+
+       return cppi41_cfg_sched_tbl(dmanum, qmgr, dma_ch, is_tx, 1);
+}
+EXPORT_SYMBOL_GPL(cppi41_schedtbl_add_dma_ch);
+
+int cppi41_schedtbl_remove_dma_ch(u8 dmanum, u8 qmgr, u8 dma_ch, u8 is_tx)
+{
+       if (!dma_sched_tbl)
+               return -EINVAL;
+
+       return cppi41_cfg_sched_tbl(dmanum, qmgr, dma_ch, is_tx, 0);
+}
+EXPORT_SYMBOL_GPL(cppi41_schedtbl_remove_dma_ch);
+
+int cppi41_dma_block_init(u8 dma_num, u8 q_mgr, u8 num_order,
+                        struct cppi41_sched_tbl_t *sched_tbl, u8 tbl_size)
+{
+       const struct cppi41_dma_block *dma_block;
+       unsigned num_desc;
+       void *ptr;
+       int error;
+       u16 q_num;
+
+       if (dma_num >= cppi41_num_dma_block ||
+           q_mgr >= cppi41_num_queue_mgr ||
+           !tbl_size || sched_tbl == NULL)
+               return -EINVAL;
+
+       dma_sched_tbl = &sched_tbl[0];
+       num_sched_tbl_entry = tbl_size;
+
+       error = cppi41_queue_alloc(CPPI41_FREE_DESC_QUEUE |
+                                  CPPI41_UNASSIGNED_QUEUE, q_mgr, &q_num);
+       if (error) {
+               pr_err("%s: fail to allocate td desc queue.\n", __func__);
+               return error;
+       }
+       DBG("Td desc queue %d in q_mgr %d allocated\n", q_num, q_mgr);
+
+       /*
+        * Tell the hardware about the Teardown descriptor
+        * queue manager and queue number.
+        */
+       dma_block = &cppi41_dma_block[dma_num];
+       cppi_writel((q_mgr << DMA_TD_DESC_QMGR_SHIFT) |
+                    (q_num << DMA_TD_DESC_QNUM_SHIFT),
+                    dma_block->global_ctrl_base +
+                    DMA_TEARDOWN_FREE_DESC_CTRL_REG);
+       DBG("Teardown free descriptor control @ %p, value: %x\n",
+           dma_block->global_ctrl_base + DMA_TEARDOWN_FREE_DESC_CTRL_REG,
+           cppi_readl(dma_block->global_ctrl_base +
+                       DMA_TEARDOWN_FREE_DESC_CTRL_REG));
+
+       num_desc = 1 << num_order;
+       dma_teardown[dma_num].rgn_size = num_desc *
+                                        sizeof(struct cppi41_teardown_desc);
+
+       /* Pre-allocate teardown descriptors. */
+       ptr = dma_alloc_coherent(NULL, dma_teardown[dma_num].rgn_size,
+                                &dma_teardown[dma_num].phys_addr,
+                                GFP_KERNEL | GFP_DMA);
+       if (ptr == NULL) {
+               error = -ENOMEM;
+               goto free_queue;
+       }
+       dma_teardown[dma_num].virt_addr = ptr;
+
+       error = cppi41_mem_rgn_alloc(q_mgr, dma_teardown[dma_num].phys_addr, 5,
+                                    num_order, &dma_teardown[dma_num].mem_rgn);
+       if (error) {
+               pr_err("%s: fail to allocate td-desc mem region\n", __func__);
+               goto free_mem;
+       }
+
+       error = cppi41_queue_init(&dma_teardown[dma_num].queue_obj, 0, q_num);
+       if (error) {
+               pr_err("%s: fail to init td free desc queue.\n", __func__);
+               goto free_rgn;
+       }
+
+       dma_teardown[dma_num].q_num = q_num;
+       dma_teardown[dma_num].q_mgr = q_mgr;
+       dma_teardown[dma_num].num_desc = num_desc;
+       /*
+        * Push all teardown descriptors to the free teardown queue
+        * for the CPPI 4.1 system.
+        */
+       cppi41_init_teardown_queue(dma_num);
+
+       /* Initialize the DMA scheduler. */
+       cppi41_dma_sched_tbl_init(dma_num, q_mgr);
+
+       return 0;
+
+free_rgn:
+       cppi41_mem_rgn_free(q_mgr, dma_teardown[dma_num].mem_rgn);
+free_mem:
+       dma_free_coherent(NULL, dma_teardown[dma_num].rgn_size,
+                         dma_teardown[dma_num].virt_addr,
+                         dma_teardown[dma_num].phys_addr);
+free_queue:
+       cppi41_queue_free(q_mgr, q_num);
+       return error;
+}
+EXPORT_SYMBOL_GPL(cppi41_dma_block_init);
+
+int cppi41_dma_block_uninit(u8 dma_num, u8 q_mgr, u8 num_order,
+       struct cppi41_sched_tbl_t *sched_tbl, u8 tbl_size)
+{
+       /* popout all teardown descriptors */
+       cppi41_free_teardown_queue(dma_num);
+
+       /* free queue mgr region */
+       cppi41_mem_rgn_free(q_mgr, dma_teardown[dma_num].mem_rgn);
+       /* free the allocated teardown descriptors */
+       dma_free_coherent(NULL, dma_teardown[dma_num].rgn_size,
+                       dma_teardown[dma_num].virt_addr,
+                       dma_teardown[dma_num].phys_addr);
+
+       /* free the teardown queue*/
+       cppi41_queue_free(dma_teardown[dma_num].q_mgr,
+                       dma_teardown[dma_num].q_num);
+
+       /* disable the dma schedular */
+       cppi41_disable_schedular(dma_num);
+
+       dma_sched_tbl = NULL;
+       num_sched_tbl_entry = 0;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppi41_dma_block_uninit);
+/*
+ * cppi41_mem_rgn_alloc - allocate a memory region within the queue manager
+ */
+int cppi41_mem_rgn_alloc(u8 q_mgr, dma_addr_t rgn_addr, u8 size_order,
+                        u8 num_order, u8 *mem_rgn)
+{
+       void __iomem *desc_mem_regs;
+       u32 num_desc = 1 << num_order, index, ctrl;
+       int rgn;
+
+       DBG("%s called with rgn_addr = %08x, size_order = %d, num_order = %d\n",
+           __func__, rgn_addr, size_order, num_order);
+
+       if (q_mgr >= cppi41_num_queue_mgr ||
+           size_order < 5 || size_order > 13 ||
+           num_order  < 5 || num_order  > 12 ||
+           (rgn_addr & ((1 << size_order) - 1)))
+               return -EINVAL;
+
+       rgn = next_mem_rgn[q_mgr];
+       index = next_desc_index[q_mgr];
+       if (rgn >= CPPI41_MAX_MEM_RGN || index + num_desc > 0x4000)
+               return -ENOSPC;
+
+       next_mem_rgn[q_mgr] = rgn + 1;
+       next_desc_index[q_mgr] = index + num_desc;
+
+       desc_mem_regs = cppi41_queue_mgr[q_mgr].desc_mem_rgn_base;
+
+       /* Write the base register */
+       cppi_writel(rgn_addr, desc_mem_regs + QMGR_MEM_RGN_BASE_REG(rgn));
+       DBG("Descriptor region base @ %p, value: %x\n",
+           desc_mem_regs + QMGR_MEM_RGN_BASE_REG(rgn),
+           cppi_readl(desc_mem_regs + QMGR_MEM_RGN_BASE_REG(rgn)));
+
+       /* Write the control register */
+       ctrl = ((index << QMGR_MEM_RGN_INDEX_SHIFT) &
+               QMGR_MEM_RGN_INDEX_MASK) |
+              (((size_order - 5) << QMGR_MEM_RGN_DESC_SIZE_SHIFT) &
+               QMGR_MEM_RGN_DESC_SIZE_MASK) |
+              (((num_order - 5) << QMGR_MEM_RGN_SIZE_SHIFT) &
+               QMGR_MEM_RGN_SIZE_MASK);
+       cppi_writel(ctrl, desc_mem_regs + QMGR_MEM_RGN_CTRL_REG(rgn));
+       DBG("Descriptor region control @ %p, value: %x\n",
+           desc_mem_regs + QMGR_MEM_RGN_CTRL_REG(rgn),
+           cppi_readl(desc_mem_regs + QMGR_MEM_RGN_CTRL_REG(rgn)));
+
+       *mem_rgn = rgn;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppi41_mem_rgn_alloc);
+
+/*
+ * cppi41_mem_rgn_free - free the memory region within the queue manager
+ */
+int cppi41_mem_rgn_free(u8 q_mgr, u8 mem_rgn)
+{
+       void __iomem *desc_mem_regs;
+
+       DBG("%s called.\n", __func__);
+
+       if (q_mgr >= cppi41_num_queue_mgr || mem_rgn >= next_mem_rgn[q_mgr])
+               return -EINVAL;
+
+       desc_mem_regs = cppi41_queue_mgr[q_mgr].desc_mem_rgn_base;
+
+       if (cppi_readl(desc_mem_regs + QMGR_MEM_RGN_BASE_REG(mem_rgn)) == 0)
+               return -ENOENT;
+
+       cppi_writel(0, desc_mem_regs + QMGR_MEM_RGN_BASE_REG(mem_rgn));
+       cppi_writel(0, desc_mem_regs + QMGR_MEM_RGN_CTRL_REG(mem_rgn));
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppi41_mem_rgn_free);
+
+/*
+ * cppi41_tx_ch_init - initialize a CPPI 4.1 Tx channel object
+ *
+ * Verify the channel info (range checking, etc.) and store the channel
+ * information within the object structure.
+ */
+int cppi41_tx_ch_init(struct cppi41_dma_ch_obj *tx_ch_obj,
+                     u8 dma_num, u8 ch_num)
+{
+       if (dma_num >= cppi41_num_dma_block ||
+           ch_num  >= cppi41_dma_block[dma_num].num_tx_ch)
+               return -EINVAL;
+
+       /* Populate the channel object structure */
+       tx_ch_obj->base_addr  = cppi41_dma_block[dma_num].ch_ctrl_stat_base +
+                               DMA_CH_TX_GLOBAL_CFG_REG(ch_num);
+       tx_ch_obj->global_cfg = cppi_readl(tx_ch_obj->base_addr);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppi41_tx_ch_init);
+
+/*
+ * cppi41_rx_ch_init - initialize a CPPI 4.1 Rx channel object
+ *
+ * Verify the channel info (range checking, etc.) and store the channel
+ * information within the object structure.
+ */
+int cppi41_rx_ch_init(struct cppi41_dma_ch_obj *rx_ch_obj,
+                     u8 dma_num, u8 ch_num)
+{
+       if (dma_num >= cppi41_num_dma_block ||
+           ch_num  >= cppi41_dma_block[dma_num].num_rx_ch)
+               return -EINVAL;
+
+       /* Populate the channel object structure */
+       rx_ch_obj->base_addr  = cppi41_dma_block[dma_num].ch_ctrl_stat_base +
+                               DMA_CH_RX_GLOBAL_CFG_REG(ch_num);
+       rx_ch_obj->global_cfg = cppi_readl(rx_ch_obj->base_addr);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppi41_rx_ch_init);
+
+/*
+ * We have to cache the last written Rx/Tx channel global configration register
+ * value due to its bits other than enable/teardown being write-only. Yet there
+ * is a caveat related to caching the enable bit: this bit may be automatically
+ * cleared as a result of teardown, so we can't trust its cached value!
+ * When modifying the write only register fields, we're making use of the fact
+ * that they read back as zeros, and not clearing them explicitly...
+ */
+
+/*
+ * cppi41_dma_ch_default_queue - set CPPI 4.1 channel default completion queue
+ */
+void cppi41_dma_ch_default_queue(struct cppi41_dma_ch_obj *dma_ch_obj,
+                                u8 q_mgr, u16 q_num)
+{
+       u32 val = dma_ch_obj->global_cfg;
+
+       /* Clear the fields to be modified. */
+       val &= ~(DMA_CH_TX_DEFAULT_QMGR_MASK | DMA_CH_TX_DEFAULT_QNUM_MASK |
+                DMA_CH_TX_ENABLE_MASK);
+
+       /* Set the default completion queue. */
+       val |= ((q_mgr << DMA_CH_TX_DEFAULT_QMGR_SHIFT) &
+               DMA_CH_TX_DEFAULT_QMGR_MASK) |
+              ((q_num << DMA_CH_TX_DEFAULT_QNUM_SHIFT) &
+               DMA_CH_TX_DEFAULT_QNUM_MASK);
+
+       /* Get the current state of the enable bit. */
+       dma_ch_obj->global_cfg = val |= cppi_readl(dma_ch_obj->base_addr);
+       cppi_writel(val, dma_ch_obj->base_addr);
+       DBG("dmach global cfg @ %p, write-val %x read-val %x\n",
+               da_ch_obj->base_addr, val, cppi_readl(dma_ch_obj->base_addr));
+
+}
+EXPORT_SYMBOL_GPL(cppi41_dma_ch_default_queue);
+
+/*
+ * cppi41_rx_ch_configure - configure CPPI 4.1 Rx channel
+ */
+void cppi41_rx_ch_configure(struct cppi41_dma_ch_obj *rx_ch_obj,
+                           struct cppi41_rx_ch_cfg  *cfg)
+{
+       void __iomem *base = rx_ch_obj->base_addr;
+       u32 val = cppi_readl(rx_ch_obj->base_addr);
+
+       val |= ((cfg->sop_offset << DMA_CH_RX_SOP_OFFSET_SHIFT) &
+               DMA_CH_RX_SOP_OFFSET_MASK) |
+              ((cfg->default_desc_type << DMA_CH_RX_DEFAULT_DESC_TYPE_SHIFT) &
+               DMA_CH_RX_DEFAULT_DESC_TYPE_MASK) |
+              ((cfg->retry_starved << DMA_CH_RX_ERROR_HANDLING_SHIFT) &
+               DMA_CH_RX_ERROR_HANDLING_MASK) |
+              ((cfg->rx_queue.q_mgr << DMA_CH_RX_DEFAULT_RQ_QMGR_SHIFT) &
+               DMA_CH_RX_DEFAULT_RQ_QMGR_MASK) |
+              ((cfg->rx_queue.q_num << DMA_CH_RX_DEFAULT_RQ_QNUM_SHIFT) &
+               DMA_CH_RX_DEFAULT_RQ_QNUM_MASK);
+
+       val &= ~(0x7 << DMA_CH_RX_MAX_BUF_CNT_SHIFT);
+       val |= (cfg->rx_max_buf_cnt << DMA_CH_RX_MAX_BUF_CNT_SHIFT);
+
+       rx_ch_obj->global_cfg = val;
+       cppi_writel(val, base);
+       DBG("rxch global cfg @ %p, write-val: %x, read-val: %x",
+               base, val, cppi_readl(base));
+
+       base -= DMA_CH_RX_GLOBAL_CFG_REG(0);
+
+       /*
+        * Set up the packet configuration register
+        * based on the descriptor type...
+        */
+       switch (cfg->default_desc_type) {
+       case DMA_CH_RX_DEFAULT_DESC_EMBED:
+               val = ((cfg->cfg.embed_pkt.fd_queue.q_mgr <<
+                       DMA_CH_RX_EMBED_FDQ_QMGR_SHIFT) &
+                      DMA_CH_RX_EMBED_FDQ_QMGR_MASK) |
+                     ((cfg->cfg.embed_pkt.fd_queue.q_num <<
+                       DMA_CH_RX_EMBED_FDQ_QNUM_SHIFT) &
+                      DMA_CH_RX_EMBED_FDQ_QNUM_MASK) |
+                     ((cfg->cfg.embed_pkt.num_buf_slot <<
+                       DMA_CH_RX_EMBED_NUM_SLOT_SHIFT) &
+                      DMA_CH_RX_EMBED_NUM_SLOT_MASK) |
+                     ((cfg->cfg.embed_pkt.sop_slot_num <<
+                       DMA_CH_RX_EMBED_SOP_SLOT_SHIFT) &
+                      DMA_CH_RX_EMBED_SOP_SLOT_MASK);
+
+               cppi_writel(val, base + DMA_CH_RX_EMBED_PKT_CFG_REG_B(0));
+               DBG("rxch  embeded pkt cfg-B @ %p, write-val: %x\n"
+                   base + DMA_CH_RX_EMBED_PKT_CFG_REG_B(0), val);
+
+               val = ((cfg->cfg.embed_pkt.free_buf_pool[0].b_pool <<
+                       DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(0)) &
+                      DMA_CH_RX_EMBED_FBP_PNUM_MASK(0)) |
+                     ((cfg->cfg.embed_pkt.free_buf_pool[0].b_mgr <<
+                       DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(0)) &
+                      DMA_CH_RX_EMBED_FBP_BMGR_MASK(0)) |
+                     ((cfg->cfg.embed_pkt.free_buf_pool[1].b_pool <<
+                       DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(1)) &
+                      DMA_CH_RX_EMBED_FBP_PNUM_MASK(1)) |
+                     ((cfg->cfg.embed_pkt.free_buf_pool[1].b_mgr <<
+                       DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(1)) &
+                      DMA_CH_RX_EMBED_FBP_BMGR_MASK(1)) |
+                     ((cfg->cfg.embed_pkt.free_buf_pool[2].b_pool <<
+                       DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(2)) &
+                      DMA_CH_RX_EMBED_FBP_PNUM_MASK(2)) |
+                     ((cfg->cfg.embed_pkt.free_buf_pool[2].b_mgr <<
+                       DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(2)) &
+                      DMA_CH_RX_EMBED_FBP_BMGR_MASK(2)) |
+                     ((cfg->cfg.embed_pkt.free_buf_pool[3].b_pool <<
+                       DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(3)) &
+                      DMA_CH_RX_EMBED_FBP_PNUM_MASK(3)) |
+                     ((cfg->cfg.embed_pkt.free_buf_pool[3].b_mgr <<
+                       DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(3)) &
+                      DMA_CH_RX_EMBED_FBP_BMGR_MASK(3));
+
+               cppi_writel(val, base + DMA_CH_RX_EMBED_PKT_CFG_REG_A(0));
+               DBG("rxch  embeded pkt cfg-A @ %p, write-val: %x\n"
+                   base + DMA_CH_RX_EMBED_PKT_CFG_REG_A(0), val);
+               break;
+       case DMA_CH_RX_DEFAULT_DESC_HOST:
+               val = ((cfg->cfg.host_pkt.fdb_queue[0].q_num <<
+                       DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(0)) &
+                      DMA_CH_RX_HOST_FDQ_QNUM_MASK(0)) |
+                     ((cfg->cfg.host_pkt.fdb_queue[0].q_mgr <<
+                       DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(0)) &
+                      DMA_CH_RX_HOST_FDQ_QMGR_MASK(0)) |
+                     ((cfg->cfg.host_pkt.fdb_queue[1].q_num <<
+                       DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(1)) &
+                      DMA_CH_RX_HOST_FDQ_QNUM_MASK(1)) |
+                     ((cfg->cfg.host_pkt.fdb_queue[1].q_mgr <<
+                       DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(1)) &
+                      DMA_CH_RX_HOST_FDQ_QMGR_MASK(1));
+
+               cppi_writel(val, base + DMA_CH_RX_HOST_PKT_CFG_REG_A(0));
+               DBG("rxch host packet cfg-A @ %p, write-val: %x\n",
+                   base + DMA_CH_RX_HOST_PKT_CFG_REG_A(0), val);
+
+               val = ((cfg->cfg.host_pkt.fdb_queue[2].q_num <<
+                       DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(2)) &
+                      DMA_CH_RX_HOST_FDQ_QNUM_MASK(2)) |
+                     ((cfg->cfg.host_pkt.fdb_queue[2].q_mgr <<
+                       DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(2)) &
+                      DMA_CH_RX_HOST_FDQ_QMGR_MASK(2)) |
+                     ((cfg->cfg.host_pkt.fdb_queue[3].q_num <<
+                      DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(3)) &
+                      DMA_CH_RX_HOST_FDQ_QNUM_MASK(3)) |
+                     ((cfg->cfg.host_pkt.fdb_queue[3].q_mgr <<
+                       DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(3)) &
+                      DMA_CH_RX_HOST_FDQ_QMGR_MASK(3));
+
+               cppi_writel(val, base + DMA_CH_RX_HOST_PKT_CFG_REG_B(0));
+               DBG("rxch host packet cfg-B @ %p, write-val: %x\n",
+                   base + DMA_CH_RX_HOST_PKT_CFG_REG_B(0), val);
+               break;
+       case DMA_CH_RX_DEFAULT_DESC_MONO:
+               val = ((cfg->cfg.mono_pkt.fd_queue.q_num <<
+                       DMA_CH_RX_MONO_FDQ_QNUM_SHIFT) &
+                      DMA_CH_RX_MONO_FDQ_QNUM_MASK) |
+                     ((cfg->cfg.mono_pkt.fd_queue.q_mgr <<
+                       DMA_CH_RX_MONO_FDQ_QMGR_SHIFT) &
+                      DMA_CH_RX_MONO_FDQ_QMGR_MASK) |
+                     ((cfg->cfg.mono_pkt.sop_offset <<
+                       DMA_CH_RX_MONO_SOP_OFFSET_SHIFT) &
+                      DMA_CH_RX_MONO_SOP_OFFSET_MASK);
+
+               cppi_writel(val, base + DMA_CH_RX_MONO_PKT_CFG_REG(0));
+               DBG("rxch monolithic packet cfg @ %p, write-val: %x\n",
+                   base + DMA_CH_RX_MONO_PKT_CFG_REG(0), val);
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(cppi41_rx_ch_configure);
+
+void cppi41_rx_ch_set_maxbufcnt(struct cppi41_dma_ch_obj *rx_ch_obj,
+                           u8 rx_max_buf_cnt)
+{
+       void __iomem *base = rx_ch_obj->base_addr;
+       u32 val = cppi_readl(rx_ch_obj->base_addr);
+
+       val = rx_ch_obj->global_cfg;
+       val &= ~(0x7 << DMA_CH_RX_MAX_BUF_CNT_SHIFT);
+       val |= (rx_max_buf_cnt << DMA_CH_RX_MAX_BUF_CNT_SHIFT);
+
+       rx_ch_obj->global_cfg = val;
+       cppi_writel(val, base);
+
+       DBG("%s: rx-global-cfg @ %p, write-val: %x, read-val: %x\n"
+               __func__, base, val, cppi_readl(base));
+
+}
+EXPORT_SYMBOL_GPL(cppi41_rx_ch_set_maxbufcnt);
+/*
+ * cppi41_dma_ch_teardown - teardown a given Tx/Rx channel
+ */
+void cppi41_dma_ch_teardown(struct cppi41_dma_ch_obj *dma_ch_obj)
+{
+       u32 val = cppi_readl(dma_ch_obj->base_addr);
+
+       /* Initiate channel teardown. */
+       val |= dma_ch_obj->global_cfg & ~DMA_CH_TX_ENABLE_MASK;
+       dma_ch_obj->global_cfg = val |= DMA_CH_TX_TEARDOWN_MASK;
+       cppi_writel(val, dma_ch_obj->base_addr);
+       DBG("Tear down channel @ %p, value written: %x, value read: %x\n",
+           dma_ch_obj->base_addr, val, cppi_readl(dma_ch_obj->base_addr));
+}
+EXPORT_SYMBOL_GPL(cppi41_dma_ch_teardown);
+
+/*
+ * cppi41_dma_ch_enable - enable Tx/Rx DMA channel in hardware
+ *
+ * Makes the channel ready for data transmission/reception.
+ */
+void cppi41_dma_ch_enable(struct cppi41_dma_ch_obj *dma_ch_obj)
+{
+       u32 val = dma_ch_obj->global_cfg | DMA_CH_TX_ENABLE_MASK;
+
+       /* Teardown bit remains set after completion, so clear it now... */
+       dma_ch_obj->global_cfg = val &= ~DMA_CH_TX_TEARDOWN_MASK;
+       cppi_writel(val, dma_ch_obj->base_addr);
+       DBG("Enable channel @ %p, value written: %x, value read: %x\n",
+           dma_ch_obj->base_addr, val, cppi_readl(dma_ch_obj->base_addr));
+}
+EXPORT_SYMBOL_GPL(cppi41_dma_ch_enable);
+
+/*
+ * cppi41_dma_ch_disable - disable Tx/Rx DMA channel in hardware
+ */
+void cppi41_dma_ch_disable(struct cppi41_dma_ch_obj *dma_ch_obj)
+{
+       dma_ch_obj->global_cfg &= ~DMA_CH_TX_ENABLE_MASK;
+       cppi_writel(dma_ch_obj->global_cfg, dma_ch_obj->base_addr);
+       DBG("Disable channel @ %p, value written: %x, value read: %x\n",
+           dma_ch_obj->base_addr, dma_ch_obj->global_cfg,
+           cppi_readl(dma_ch_obj->base_addr));
+}
+EXPORT_SYMBOL_GPL(cppi41_dma_ch_disable);
+
+void cppi41_init_teardown_queue(int dma_num)
+{
+       dma_addr_t td_addr;
+       struct cppi41_teardown_desc *curr_td;
+       u32 num_desc = dma_teardown[dma_num].num_desc;
+       int i;
+
+       curr_td = dma_teardown[dma_num].virt_addr;
+       td_addr = dma_teardown[dma_num].phys_addr;
+
+       for (i = 0; i < num_desc; i++) {
+               cppi41_queue_push(&dma_teardown[dma_num].queue_obj, td_addr,
+                                 sizeof(*curr_td), 0);
+               td_addr += sizeof(*curr_td);
+       }
+}
+EXPORT_SYMBOL_GPL(cppi41_init_teardown_queue);
+
+void cppi41_free_teardown_queue(int dma_num)
+{
+       unsigned long td_addr;
+       u32 num_desc = dma_teardown[dma_num].num_desc;
+
+       while (num_desc--) {
+               td_addr = cppi41_queue_pop(&dma_teardown[dma_num].queue_obj);
+
+               if (td_addr == 0)
+                       break;
+       }
+}
+EXPORT_SYMBOL_GPL(cppi41_free_teardown_queue);
+
+/**
+ * alloc_queue - allocate a queue in the given range
+ * @allocated: pointer to the bitmap of the allocated queues
+ * @excluded:  pointer to the bitmap of the queues excluded from allocation
+ *             (optional)
+ * @start:     starting queue number
+ * @count:     number of queues available
+ *
+ * Returns queue number on success, -ENOSPC otherwise.
+ */
+static int alloc_queue(u32 *allocated, const u32 *excluded, unsigned start,
+                      unsigned count)
+{
+       u32 bit, mask = 0;
+       int index = -1;
+
+       /*
+        * We're starting the loop as if we've just wrapped around 32 bits
+        * in order to save on preloading the bitmasks.
+        */
+       for (bit = 0; count--; start++, bit <<= 1) {
+               /* Have we just wrapped around 32 bits? */
+               if (!bit) {
+                       /* Start over with the next bitmask word */
+                       bit = 1;
+                       index++;
+                       /* Have we just entered the loop? */
+                       if (!index) {
+                               /* Calculate the starting values */
+                               bit <<= start & 0x1f;
+                               index = start >> 5;
+                       }
+                       /*
+                        * Load the next word of the allocated bitmask OR'ing
+                        * it with the excluded bitmask if it's been passed.
+                        */
+                       mask = allocated[index];
+                       if (excluded != NULL)
+                               mask |= excluded[index];
+               }
+               /*
+                * If the bit in the combined bitmask is zero,
+                * we've just found a free queue.
+                */
+               if (!(mask & bit)) {
+                       allocated[index] |= bit;
+                       return start;
+               }
+       }
+       return -ENOSPC;
+}
+
+/*
+ * cppi41_queue_alloc - allocate a queue of a given type in the queue manager
+ */
+int cppi41_queue_alloc(u8 type, u8 q_mgr, u16 *q_num)
+{
+       int res = -ENOSPC;
+
+       if (q_mgr >= cppi41_num_queue_mgr)
+               return -EINVAL;
+
+       /* Mask out the unsupported queue types */
+       type &= cppi41_queue_mgr[q_mgr].queue_types;
+       /* First see if a free descriptor queue was requested... */
+       if (type & CPPI41_FREE_DESC_QUEUE)
+               res = alloc_queue(allocated_queues[q_mgr], NULL,
+                                 cppi41_queue_mgr[q_mgr].base_fdq_num,  16);
+
+       /* Then see if a free descriptor/buffer queue was requested... */
+       if (res < 0 && (type & CPPI41_FREE_DESC_BUF_QUEUE))
+               res = alloc_queue(allocated_queues[q_mgr], NULL,
+                                 cppi41_queue_mgr[q_mgr].base_fdbq_num, 16);
+
+       /* Last see if an unassigned queue was requested... */
+       if (res < 0 && (type & CPPI41_UNASSIGNED_QUEUE))
+               res = alloc_queue(allocated_queues[q_mgr],
+                                 cppi41_queue_mgr[q_mgr].assigned, 0,
+                                 cppi41_queue_mgr[q_mgr].num_queue);
+
+       /* See if any queue was allocated... */
+       if (res < 0)
+               return res;
+
+       /* Return the queue allocated */
+       *q_num = res;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppi41_queue_alloc);
+
+/*
+ * cppi41_queue_free - free the given queue in the queue manager
+ */
+int cppi41_queue_free(u8 q_mgr, u16 q_num)
+{
+       int index = q_num >> 5, bit = 1 << (q_num & 0x1f);
+
+       if (allocated_queues[q_mgr] != NULL) {
+               if (q_mgr >= cppi41_num_queue_mgr ||
+                   q_num >= cppi41_queue_mgr[q_mgr].num_queue ||
+                   !(allocated_queues[q_mgr][index] & bit))
+                       return -EINVAL;
+               allocated_queues[q_mgr][index] &= ~bit;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppi41_queue_free);
+
+/*
+ * cppi41_queue_init - initialize a CPPI 4.1 queue object
+ */
+int cppi41_queue_init(struct cppi41_queue_obj *queue_obj, u8 q_mgr, u16 q_num)
+{
+       if (q_mgr >= cppi41_num_queue_mgr ||
+           q_num >= cppi41_queue_mgr[q_mgr].num_queue)
+               return -EINVAL;
+
+       queue_obj->base_addr = cppi41_queue_mgr[q_mgr].q_mgmt_rgn_base +
+                              QMGR_QUEUE_STATUS_REG_A(q_num);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppi41_queue_init);
+
+/*
+ * cppi41_queue_push - push a descriptor into the given queue
+ */
+void cppi41_queue_push(const struct cppi41_queue_obj *queue_obj, u32 desc_addr,
+                      u32 desc_size, u32 pkt_size)
+{
+       u32 val;
+
+       val = (((desc_size - 24) >> (2 - QMGR_QUEUE_DESC_SIZE_SHIFT)) &
+              QMGR_QUEUE_DESC_SIZE_MASK) |
+             (desc_addr & QMGR_QUEUE_DESC_PTR_MASK);
+
+       DBG("Pushing value %x to queue @ %p\n", val, queue_obj->base_addr);
+
+       cppi_writel(val, queue_obj->base_addr + QMGR_QUEUE_REG_D(0));
+}
+EXPORT_SYMBOL_GPL(cppi41_queue_push);
+
+/*
+ * cppi41_queue_pop - pop a descriptor from a given queue
+ */
+unsigned long cppi41_queue_pop(const struct cppi41_queue_obj *queue_obj)
+{
+       u32 val = cppi_readl(queue_obj->base_addr + QMGR_QUEUE_REG_D(0));
+
+       DBG("Popping value %x from queue @ %p\n", val, queue_obj->base_addr);
+
+       return val & QMGR_QUEUE_DESC_PTR_MASK;
+}
+EXPORT_SYMBOL_GPL(cppi41_queue_pop);
+
+/*
+ * cppi41_get_teardown_info - extract information from a teardown descriptor
+ */
+int cppi41_get_teardown_info(unsigned long addr, u32 *info)
+{
+       struct cppi41_teardown_desc *desc;
+       int dma_num;
+
+       for (dma_num = 0; dma_num < cppi41_num_dma_block; dma_num++)
+               if (addr >= dma_teardown[dma_num].phys_addr &&
+                   addr <  dma_teardown[dma_num].phys_addr +
+                           dma_teardown[dma_num].rgn_size)
+                       break;
+
+       if (dma_num == cppi41_num_dma_block)
+               return -EINVAL;
+
+       desc = addr - dma_teardown[dma_num].phys_addr +
+              dma_teardown[dma_num].virt_addr;
+
+       if ((desc->teardown_info & CPPI41_DESC_TYPE_MASK) !=
+           (CPPI41_DESC_TYPE_TEARDOWN << CPPI41_DESC_TYPE_SHIFT))
+               return -EINVAL;
+
+       *info = desc->teardown_info;
+#if 1
+       /* Hardware is not giving the current DMA number as of now. :-/ */
+       *info |= (dma_num << CPPI41_TEARDOWN_DMA_NUM_SHIFT) &
+                CPPI41_TEARDOWN_DMA_NUM_MASK;
+#else
+       dma_num = (desc->teardown_info & CPPI41_TEARDOWN_DMA_NUM_MASK) >>
+                CPPI41_TEARDOWN_DMA_NUM_SHIFT;
+#endif
+
+       cppi41_queue_push(&dma_teardown[dma_num].queue_obj, addr,
+                         sizeof(struct cppi41_teardown_desc), 0);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cppi41_get_teardown_info);
+
+/*
+ * cppi41_save_context - save regsiter context before going to suspend.
+ */
+void cppi41_save_context(u8 dma_num)
+{
+       const struct cppi41_dma_block *dma_block;
+       struct cppi41_dma_regs *cppi41;
+       struct cppi41_queue_manager *qmgr;
+       void __iomem *q_mgr_regs, *desc_mem_regs;
+       u8 i, q_mgr = 0;
+
+       dma_block = (struct cppi41_dma_block *)&cppi41_dma_block[dma_num];
+       cppi41 = (struct cppi41_dma_regs *)&dma_block->cppi41_regs;
+       qmgr = &cppi41->qmgr;
+       q_mgr_regs = cppi41_queue_mgr[q_mgr].q_mgr_rgn_base;
+       desc_mem_regs = cppi41_queue_mgr[q_mgr].desc_mem_rgn_base;
+
+       /* popout all teardown descriptors */
+       cppi41_free_teardown_queue(dma_num);
+
+       cppi41->teardn_fdq_ctrl = cppi_readl(dma_block->global_ctrl_base +
+                       DMA_TEARDOWN_FREE_DESC_CTRL_REG);
+       cppi41->emulation_ctrl = cppi_readl(dma_block->global_ctrl_base +
+                       DMA_EMULATION_CTRL_REG);
+
+       qmgr->link_ram_rgn0_base = cppi_readl(q_mgr_regs +
+                               QMGR_LINKING_RAM_RGN0_BASE_REG);
+       qmgr->link_ram_rgn0_size = cppi_readl(q_mgr_regs +
+                               QMGR_LINKING_RAM_RGN0_SIZE_REG);
+       qmgr->link_ram_rgn1_base = cppi_readl(q_mgr_regs +
+                               QMGR_LINKING_RAM_RGN1_BASE_REG);
+
+       for (i = 0 ; i < 8 ; i++) {
+               qmgr->memr_base[i] = cppi_readl(desc_mem_regs +
+                               QMGR_MEM_RGN_BASE_REG(i));
+               qmgr->memr_ctrl[i] = cppi_readl(desc_mem_regs +
+                               QMGR_MEM_RGN_CTRL_REG(i));
+       }
+
+       cppi41->sched_ctrl = cppi_readl(dma_block->sched_ctrl_base +
+                               DMA_SCHED_CTRL_REG);
+
+}
+EXPORT_SYMBOL_GPL(cppi41_save_context);
+
+/*
+ * cppi41_restore_context - restore regsiter context after resume.
+ */
+void cppi41_restore_context(u8 dma_num)
+{
+       const struct cppi41_dma_block *dma_block;
+       struct cppi41_dma_regs *cppi41;
+       struct cppi41_queue_manager *qmgr;
+       void __iomem *q_mgr_regs, *desc_mem_regs;
+       u8 i, q_mgr = 0;
+
+       dma_block = (struct cppi41_dma_block *)&cppi41_dma_block[dma_num];
+       cppi41 = (struct cppi41_dma_regs *)&dma_block->cppi41_regs;
+       qmgr = &cppi41->qmgr;
+       q_mgr_regs = cppi41_queue_mgr[q_mgr].q_mgr_rgn_base;
+       desc_mem_regs = cppi41_queue_mgr[q_mgr].desc_mem_rgn_base;
+
+       cppi_writel(cppi41->teardn_fdq_ctrl, dma_block->global_ctrl_base +
+                       DMA_TEARDOWN_FREE_DESC_CTRL_REG);
+       cppi_writel(cppi41->emulation_ctrl, dma_block->global_ctrl_base +
+                       DMA_EMULATION_CTRL_REG);
+
+       cppi_writel(qmgr->link_ram_rgn0_base, q_mgr_regs +
+                               QMGR_LINKING_RAM_RGN0_BASE_REG);
+       cppi_writel(qmgr->link_ram_rgn0_size, q_mgr_regs +
+                               QMGR_LINKING_RAM_RGN0_SIZE_REG);
+       cppi_writel(qmgr->link_ram_rgn1_base, q_mgr_regs +
+                               QMGR_LINKING_RAM_RGN1_BASE_REG);
+
+       for (i = 0 ; i < 8 ; i++) {
+               cppi_writel(qmgr->memr_base[i], desc_mem_regs +
+                               QMGR_MEM_RGN_BASE_REG(i));
+               cppi_writel(qmgr->memr_ctrl[i], desc_mem_regs +
+                               QMGR_MEM_RGN_CTRL_REG(i));
+       }
+
+       /*
+        * Push all teardown descriptors to the free teardown queue
+        * for the CPPI 4.1 system.
+        */
+       cppi41_init_teardown_queue(dma_num);
+
+       /* Initialize the DMA scheduler. */
+       cppi41_dma_sched_tbl_init(dma_num, q_mgr);
+}
+EXPORT_SYMBOL_GPL(cppi41_restore_context);
+
+MODULE_DESCRIPTION("TI CPPI 4.1 support");
+MODULE_AUTHOR("MontaVista Software");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/musb/cppi41.h b/drivers/usb/musb/cppi41.h
new file mode 100644 (file)
index 0000000..ecc6d11
--- /dev/null
@@ -0,0 +1,860 @@
+/*
+ * CPPI 4.1 definitions
+ *
+ * Copyright (c) 2008-2009, MontaVista Software, Inc. <source@mvista.com>
+ *
+ * Copyright (C) 2012-2013, by Texas Instruments
+ *
+ * This program is free software; you can distribute 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 it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __CPPI41_H__
+#define __CPPI41_H__
+
+#include <linux/types.h>
+
+/*
+ * Queue Manager - Control Registers Region
+ */
+#define QMGR_REVISION_REG              0x00    /* Major and minor versions */
+                                               /* of the module */
+#define QMGR_QUEUE_DIVERSION_REG       0x08    /* Queue Diversion register */
+#define QMGR_FREE_DESC_BUF_STARVED_REG(n) (0x20 + ((n) << 2)) /* Free Desc./ */
+                                               /* Buffer Starvation Count */
+#define QMGR_FREE_DESC_STARVED_REG(n)  (0x30 + ((n) << 2)) /* Free Desc. */
+                                               /* Starvation Count */
+#define QMGR_LINKING_RAM_RGN0_BASE_REG 0x80    /* Linking RAM Region 0 Base */
+                                               /* Address */
+#define QMGR_LINKING_RAM_RGN0_SIZE_REG 0x84    /* Linking RAM Region 0 Size */
+#define QMGR_LINKING_RAM_RGN1_BASE_REG 0x88    /* Linking RAM Region 1 Base */
+                                               /* Address */
+#define QMGR_QUEUE_PENDING_REG(n)      (0x90 + ((n) << 2)) /* Pending status */
+                                               /* for all queues */
+
+/*
+ * Queue Manager - Memory Region Registers
+ */
+#define QMGR_MEM_RGN_BASE_REG(r)       (0x00 + ((r) << 4))
+#define QMGR_MEM_RGN_CTRL_REG(r)       (0x04 + ((r) << 4))
+
+/* Memory Region R Control Register bits */
+#define QMGR_MEM_RGN_INDEX_SHIFT       16
+#define QMGR_MEM_RGN_INDEX_MASK                (0x3fff << QMGR_MEM_RGN_INDEX_SHIFT)
+#define QMGR_MEM_RGN_DESC_SIZE_SHIFT   8
+#define QMGR_MEM_RGN_DESC_SIZE_MASK    (0xf << QMGR_MEM_RGN_DESC_SIZE_SHIFT)
+#define QMGR_MEM_RGN_SIZE_SHIFT                0
+#define QMGR_MEM_RGN_SIZE_MASK         (7 << QMGR_MEM_RGN_SIZE_SHIFT)
+
+/*
+ * Queue Manager - Queues Region
+ */
+#define QMGR_QUEUE_REG_A(n)            (0x00 + ((n) << 4))
+#define QMGR_QUEUE_REG_B(n)            (0x04 + ((n) << 4))
+#define QMGR_QUEUE_REG_C(n)            (0x08 + ((n) << 4))
+#define QMGR_QUEUE_REG_D(n)            (0x0C + ((n) << 4))
+
+/* Queue N Register C bits */
+#define QMGR_QUEUE_HEAD_TAIL_SHIFT     31
+#define QMGR_QUEUE_HEAD_TAIL_MASK      (1 << QMGR_QUEUE_HEAD_TAIL_SHIFT)
+#define QMGR_QUEUE_PKT_SIZE_SHIFT      0
+#define QMGR_QUEUE_PKT_SIZE_MASK       (0x3fff << QMGR_QUEUE_PKT_SIZE_SHIFT)
+/* Queue N Register D bits */
+#define QMGR_QUEUE_DESC_PTR_SHIFT      5
+#define QMGR_QUEUE_DESC_PTR_MASK       (0x7ffffff << QMGR_QUEUE_DESC_PTR_SHIFT)
+#define QMGR_QUEUE_DESC_SIZE_SHIFT     0
+#define QMGR_QUEUE_DESC_SIZE_MASK      (0x1f << QMGR_QUEUE_DESC_SIZE_SHIFT)
+
+/*
+ * Queue Manager - Queue Status Region
+ */
+#define QMGR_QUEUE_STATUS_REG_A(n)     (0x00 + ((n) << 4))
+#define QMGR_QUEUE_STATUS_REG_B(n)     (0x04 + ((n) << 4))
+#define QMGR_QUEUE_STATUS_REG_C(n)     (0x08 + ((n) << 4))
+
+/*
+ * DMA Controller - Global Control Registers Region
+ */
+#define DMA_REVISION_REG               0x00    /* Major and minor versions */
+                                               /* of the module */
+#define DMA_TEARDOWN_FREE_DESC_CTRL_REG 0x04   /* Queue  manager and queue */
+                                               /* number for Teardown free */
+                                               /* descriptor queue */
+#define DMA_EMULATION_CTRL_REG         0x08    /* Emulation control register */
+
+/* Teardown Free Descriptor Queue Control Register bits */
+#define DMA_TD_DESC_QMGR_SHIFT         12
+#define DMA_TD_DESC_QMGR_MASK          (3 << DMA_TD_DESC_QMGR_SHIFT)
+#define DMA_TD_DESC_QNUM_SHIFT         0
+#define DMA_TD_DESC_QNUM_MASK          (0xfff << DMA_TD_DESC_QNUM_SHIFT)
+
+/*
+ * DMA Controller - Channel Control / Status Registers Region
+ */
+#define DMA_CH_TX_GLOBAL_CFG_REG(n)     (0x00 + ((n) << 5))
+#define DMA_CH_RX_GLOBAL_CFG_REG(n)     (0x08 + ((n) << 5))
+#define DMA_CH_RX_HOST_PKT_CFG_REG_A(n)  (0x0C + ((n) << 5))
+#define DMA_CH_RX_HOST_PKT_CFG_REG_B(n)  (0x10 + ((n) << 5))
+#define DMA_CH_RX_EMBED_PKT_CFG_REG_A(n) (0x14 + ((n) << 5))
+#define DMA_CH_RX_EMBED_PKT_CFG_REG_B(n) (0x18 + ((n) << 5))
+#define DMA_CH_RX_MONO_PKT_CFG_REG(n)   (0x1C + ((n) << 5))
+
+/* Tx Channel N Global Configuration Register bits */
+#define DMA_CH_TX_ENABLE_SHIFT         31
+#define DMA_CH_TX_ENABLE_MASK          (1 << DMA_CH_TX_ENABLE_SHIFT)
+#define DMA_CH_TX_TEARDOWN_SHIFT       30
+#define DMA_CH_TX_TEARDOWN_MASK                (1 << DMA_CH_TX_TEARDOWN_SHIFT)
+#define DMA_CH_TX_DEFAULT_QMGR_SHIFT   12
+#define DMA_CH_TX_DEFAULT_QMGR_MASK    (3 << DMA_CH_TX_DEFAULT_QMGR_SHIFT)
+#define DMA_CH_TX_DEFAULT_QNUM_SHIFT   0
+#define DMA_CH_TX_DEFAULT_QNUM_MASK    (0xfff << DMA_CH_TX_DEFAULT_QNUM_SHIFT)
+
+/* Rx Channel N Global Configuration Register bits */
+#define DMA_CH_RX_ENABLE_SHIFT         31
+#define DMA_CH_RX_ENABLE_MASK          (1 << DMA_CH_RX_ENABLE_SHIFT)
+#define DMA_CH_RX_TEARDOWN_SHIFT       30
+#define DMA_CH_RX_TEARDOWN_MASK                (1 << DMA_CH_RX_TEARDOWN_SHIFT)
+#define DMA_CH_RX_ERROR_HANDLING_SHIFT 24
+#define DMA_CH_RX_ERROR_HANDLING_MASK  (1 << DMA_CH_RX_ERROR_HANDLING_SHIFT)
+#define DMA_CH_RX_SOP_OFFSET_SHIFT     16
+#define DMA_CH_RX_SOP_OFFSET_MASK      (0xff << DMA_CH_RX_SOP_OFFSET_SHIFT)
+#define DMA_CH_RX_DEFAULT_DESC_TYPE_SHIFT 14
+#define DMA_CH_RX_DEFAULT_DESC_TYPE_MASK  (3 << \
+                                          DMA_CH_RX_DEFAULT_DESC_TYPE_SHIFT)
+#define DMA_CH_RX_DEFAULT_DESC_EMBED   0
+#define DMA_CH_RX_DEFAULT_DESC_HOST    1
+#define DMA_CH_RX_DEFAULT_DESC_MONO    2
+#define DMA_CH_RX_DEFAULT_RQ_QMGR_SHIFT 12
+#define DMA_CH_RX_DEFAULT_RQ_QMGR_MASK (3 << DMA_CH_RX_DEFAULT_RQ_QMGR_SHIFT)
+#define DMA_CH_RX_DEFAULT_RQ_QNUM_SHIFT 0
+#define DMA_CH_RX_DEFAULT_RQ_QNUM_MASK (0xfff << \
+                                        DMA_CH_RX_DEFAULT_RQ_QNUM_SHIFT)
+#define DMA_CH_RX_MAX_BUF_CNT_SHIFT    26
+#define        DMA_CH_RX_MAX_BUF_CNT_0         0
+#define        DMA_CH_RX_MAX_BUF_CNT_1         1
+#define        DMA_CH_RX_MAX_BUF_CNT_2         2
+#define        DMA_CH_RX_MAX_BUF_CNT_3         3
+
+/* Rx Channel N Host Packet Configuration Register A/B bits */
+#define DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(n) (12 + 16 * ((n) & 1))
+#define DMA_CH_RX_HOST_FDQ_QMGR_MASK(n)  (3 << DMA_CH_RX_HOST_FDQ_QMGR_SHIFT(n))
+#define DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(n) (0 + 16 * ((n) & 1))
+#define DMA_CH_RX_HOST_FDQ_QNUM_MASK(n)  (0xfff << \
+                                         DMA_CH_RX_HOST_FDQ_QNUM_SHIFT(n))
+
+/* Rx Channel N Embedded Packet Configuration Register A bits */
+#define DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(n) (6 + 8 * (n))
+#define DMA_CH_RX_EMBED_FBP_BMGR_MASK(n)  (3 << \
+                                          DMA_CH_RX_EMBED_FBP_BMGR_SHIFT(n))
+#define DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(n) (0 + 8 * (n))
+#define DMA_CH_RX_EMBED_FBP_PNUM_MASK(n)  (0x1f << \
+                                          DMA_CH_RX_EMBED_FBP_PNUM_SHIFT(n))
+
+/* Rx Channel N Embedded Packet Configuration Register B bits */
+#define DMA_CH_RX_EMBED_NUM_SLOT_SHIFT 24
+#define DMA_CH_RX_EMBED_NUM_SLOT_MASK  (7 << DMA_CH_RX_EMBED_NUM_SLOT_SHIFT)
+#define DMA_CH_RX_EMBED_SOP_SLOT_SHIFT 16
+#define DMA_CH_RX_EMBED_SOP_SLOT_MASK  (7 << DMA_CH_RX_EMBED_SOP_SLOT_SHIFT)
+#define DMA_CH_RX_EMBED_FDQ_QMGR_SHIFT 12
+#define DMA_CH_RX_EMBED_FDQ_QMGR_MASK  (3 << DMA_CH_RX_EMBED_FDQ_QMGR_SHIFT)
+#define DMA_CH_RX_EMBED_FDQ_QNUM_SHIFT 0
+#define DMA_CH_RX_EMBED_FDQ_QNUM_MASK  (0xfff << \
+                                        DMA_CH_RX_EMBED_FDQ_QNUM_SHIFT)
+
+/* Rx Channel N Monolithic Packet Configuration Register bits */
+#define DMA_CH_RX_MONO_SOP_OFFSET_SHIFT 16
+#define DMA_CH_RX_MONO_SOP_OFFSET_MASK (0xff << \
+                                        DMA_CH_RX_MONO_SOP_OFFSET_SHIFT)
+#define DMA_CH_RX_MONO_FDQ_QMGR_SHIFT  12
+#define DMA_CH_RX_MONO_FDQ_QMGR_MASK   (3 << DMA_CH_RX_MONO_FDQ_QMGR_SHIFT)
+#define DMA_CH_RX_MONO_FDQ_QNUM_SHIFT  0
+#define DMA_CH_RX_MONO_FDQ_QNUM_MASK   (0xfff << DMA_CH_RX_MONO_FDQ_QNUM_SHIFT)
+
+/*
+ * DMA Scheduler - Control Region
+ */
+#define DMA_SCHED_CTRL_REG             0x00
+
+/* DMA Scheduler Control Register bits */
+#define DMA_SCHED_ENABLE_SHIFT         31
+#define DMA_SCHED_ENABLE_MASK          (1 << DMA_SCHED_ENABLE_SHIFT)
+#define DMA_SCHED_LAST_ENTRY_SHIFT     0
+#define DMA_SCHED_LAST_ENTRY_MASK      (0xff << DMA_SCHED_LAST_ENTRY_SHIFT)
+
+#define CPPI41_TXDMA_MAXLEN            (4 * 1024 * 1024 - 1)
+#define CPPI41_RXDMA_MAXLEN            (64 * 1024)
+
+/*
+ * Queue Status register
+ */
+#define CPPI41_QSTATUS_REG0    0x90
+#define CPPI41_QSTATUS_REG1    0x94
+#define CPPI41_QSTATUS_REG2    0x98
+#define CPPI41_QSTATUS_REG3    0x9c
+#define CPPI41_QSTATUS_REG4    0xa0
+
+/*
+ * DMA Scheduler - Table Region
+ */
+#define DMA_SCHED_TABLE_WORD_REG(n)    ((n) << 2)
+
+/*
+ * CPPI 4.1 Host Packet Descriptor
+ */
+struct cppi41_host_pkt_desc {
+       u32 desc_info;          /* Descriptor type, protocol specific word */
+                               /* count, packet length */
+       u32 tag_info;           /* Source tag (31:16), destination tag (15:0) */
+       u32 pkt_info;           /* Packet error state, type, protocol flags, */
+                               /* return info, descriptor location */
+       u32 buf_len;            /* Number of valid data bytes in the buffer */
+       u32 buf_ptr;            /* Pointer to the buffer associated with */
+                               /* this descriptor */
+       u32 next_desc_ptr;      /* Pointer to the next buffer descriptor */
+       u32 orig_buf_len;       /* Original buffer length */
+       u32 orig_buf_ptr;       /* Original buffer pointer */
+       u32 stk_comms_info[2];  /* Network stack private communications info */
+};
+
+/*
+ * CPPI 4.1 Host Buffer Descriptor
+ */
+struct cppi41_host_buf_desc {
+       u32 reserved[2];
+       u32 buf_recl_info;      /* Return info, descriptor location */
+       u32 buf_len;            /* Number of valid data bytes in the buffer */
+       u32 buf_ptr;            /* Pointer to the buffer associated with */
+                               /* this descriptor */
+       u32 next_desc_ptr;      /* Pointer to the next buffer descriptor */
+       u32 orig_buf_len;       /* Original buffer length */
+       u32 orig_buf_ptr;       /* Original buffer pointer */
+};
+
+#define CPPI41_DESC_TYPE_SHIFT         27
+#define CPPI41_DESC_TYPE_MASK          (0x1f << CPPI41_DESC_TYPE_SHIFT)
+#define CPPI41_DESC_TYPE_HOST          16
+#define CPPI41_DESC_TYPE_MONOLITHIC    18
+#define CPPI41_DESC_TYPE_TEARDOWN      19
+#define CPPI41_PROT_VALID_WORD_CNT_SHIFT 22
+#define CPPI41_PROT_VALID_WORD_CNT_MASK        (0x1f << CPPI41_PROT_WORD_CNT_SHIFT)
+#define CPPI41_PKT_LEN_SHIFT           0
+#define CPPI41_PKT_LEN_MASK            (0x1fffff << CPPI41_PKT_LEN_SHIFT)
+
+#define CPPI41_PKT_ERROR_SHIFT         31
+#define CPPI41_PKT_ERROR_MASK          (1 << CPPI41_PKT_ERROR_SHIFT)
+#define CPPI41_PKT_TYPE_SHIFT          26
+#define CPPI41_PKT_TYPE_MASK           (0x1f << CPPI41_PKT_TYPE_SHIFT)
+#define CPPI41_PKT_TYPE_ATM_AAL5       0
+#define CPPI41_PKT_TYPE_ATM_NULL_AAL   1
+#define CPPI41_PKT_TYPE_ATM_OAM                2
+#define CPPI41_PKT_TYPE_ATM_TRANSPARENT        3
+#define CPPI41_PKT_TYPE_EFM            4
+#define CPPI41_PKT_TYPE_USB            5
+#define CPPI41_PKT_TYPE_GENERIC                6
+#define CPPI41_PKT_TYPE_ETHERNET       7
+#define CPPI41_ZLP                     (1 << 19)
+#define CPPI41_RETURN_POLICY_SHIFT     15
+#define CPPI41_RETURN_POLICY_MASK      (1 << CPPI41_RETURN_POLICY_SHIFT)
+#define CPPI41_RETURN_LINKED           0
+#define CPPI41_RETURN_UNLINKED         1
+#define CPPI41_ONCHIP_SHIFT            14
+#define CPPI41_ONCHIP_MASK             (1 << CPPI41_ONCHIP_SHIFT)
+#define CPPI41_RETURN_QMGR_SHIFT       12
+#define CPPI41_RETURN_QMGR_MASK                (3 << CPPI41_RETURN_QMGR_SHIFT)
+#define CPPI41_RETURN_QNUM_SHIFT       0
+#define CPPI41_RETURN_QNUM_MASK                (0xfff << CPPI41_RETURN_QNUM_SHIFT)
+
+#define CPPI41_SRC_TAG_PORT_NUM_SHIFT  27
+#define CPPI41_SRC_TAG_PORT_NUM_MASK   (0x1f << CPPI41_SRC_TAG_PORT_NUM_SHIFT)
+#define CPPI41_SRC_TAG_CH_NUM_SHIFT    21
+#define CPPI41_SRC_TAG_CH_NUM_MASK     (0x3f << CPPI41_SRC_TAG_CH_NUM_SHIFT)
+#define CPPI41_SRC_TAG_SUB_CH_NUM_SHIFT 16
+#define CPPI41_SRC_TAG_SUB_CH_NUM_MASK (0x1f << \
+                                       CPPI41_SRC_TAG_SUB_CH_NUM_SHIFT)
+#define CPPI41_DEST_TAG_SHIFT          0
+#define CPPI41_DEST_TAG_MASK           (0xffff << CPPI41_DEST_TAG_SHIFT)
+#define CPPI41_PKT_INTR_FLAG           (1 << 31)
+
+/*
+ * CPPI 4.1 Teardown Descriptor
+ */
+struct cppi41_teardown_desc {
+       u32 teardown_info;      /* Teardown information */
+       u32 reserved[7];        /* 28 byte padding */
+};
+
+#define CPPI41_TEARDOWN_TX_RX_SHIFT    16
+#define CPPI41_TEARDOWN_TX_RX_MASK     (1 << CPPI41_TEARDOWN_TX_RX_SHIFT)
+#define CPPI41_TEARDOWN_DMA_NUM_SHIFT  10
+#define CPPI41_TEARDOWN_DMA_NUM_MASK   (0x3f << CPPI41_TEARDOWN_DMA_NUM_SHIFT)
+#define CPPI41_TEARDOWN_CHAN_NUM_SHIFT 0
+#define CPPI41_TEARDOWN_CHAN_NUM_MASK  (0x3f << CPPI41_TEARDOWN_CHAN_NUM_SHIFT)
+
+#define CPPI41_MAX_MEM_RGN             16
+
+/* CPPI 4.1 configuration for AM3517 */
+#define CPPI41_NUM_QUEUE_MGR           1       /* 4  max */
+#define CPPI41_NUM_DMA_BLOCK           1       /* 64 max */
+#define cppi41_num_queue_mgr   CPPI41_NUM_QUEUE_MGR
+#define cppi41_num_dma_block   CPPI41_NUM_DMA_BLOCK
+
+/**
+ * struct cppi41_queue_manager - CPPI 4.1 DMA queue manager registers for
+ * context save and restore.
+ */
+struct cppi41_queue_manager {
+       u32     link_ram_rgn0_base;
+       u32     link_ram_rgn0_size;
+       u32     link_ram_rgn1_base;
+
+       u32     memr_base[8];
+       u32     memr_ctrl[8];
+};
+
+/**
+ * struct cppi41_dma_regs - CPPI 4.1 DMA registers for
+ * context save and restore.
+ */
+struct cppi41_dma_regs {
+       u32     teardn_fdq_ctrl;
+       u32     emulation_ctrl;
+
+       /* CPPI DMA scheduler registers */
+       u32     sched_ctrl;
+
+       /* Queue manager registers */
+       struct cppi41_queue_manager qmgr;
+};
+
+/**
+ * struct cppi41_queue - Queue Tuple
+ *
+ * The basic queue tuple in CPPI 4.1 used across all data structures
+ * where a definition of a queue is required.
+ */
+struct cppi41_queue {
+       u8  q_mgr;              /* The queue manager number */
+       u16 q_num;              /* The queue number */
+};
+
+/**
+ * struct cppi41_buf_pool - Buffer Pool Tuple
+ *
+ * The basic buffer pool tuple in CPPI 4.1 used across all data structures
+ * where a definition of a buffer pool is required.
+ */
+struct cppi41_buf_pool {
+       u8  b_mgr;              /* The buffer manager number */
+       u16 b_pool;             /* The buffer pool number */
+};
+
+/**
+ * struct cppi41_queue_mgr - Queue Manager information
+ *
+ * Contains the information about the queue manager which should be copied from
+ * the hardware spec as is.
+ */
+struct cppi41_queue_mgr {
+       void __iomem *q_mgr_rgn_base; /* Base address of the Control region. */
+       void __iomem *desc_mem_rgn_base; /* Base address of the descriptor */
+                               /* memory region. */
+       void __iomem *q_mgmt_rgn_base; /* Base address of the queues region. */
+       void __iomem *q_stat_rgn_base; /* Base address of the queue status */
+                               /* region. */
+       u16 num_queue;          /* Number of the queues supported. */
+       u8 queue_types;         /* Bitmask of the supported queue types. */
+       u16 base_fdq_num;       /* The base free descriptor queue number. */
+                               /* If present, there's always 16 such queues. */
+       u16 base_fdbq_num;      /* The base free descriptor/buffer queue */
+                               /* number.  If present, there's always 16 */
+                               /* such queues. */
+       const u32 *assigned;    /* Pointer to the bitmask of the pre-assigned */
+                               /* queues. */
+};
+
+/* Queue type flags */
+#define CPPI41_FREE_DESC_QUEUE         0x01
+#define CPPI41_FREE_DESC_BUF_QUEUE     0x02
+#define CPPI41_UNASSIGNED_QUEUE                0x04
+
+/**
+ * struct cppi41_embed_pkt_cfg - Rx Channel Embedded packet configuration
+ *
+ * An instance of this structure forms part of the Rx channel information
+ * structure.
+ */
+struct cppi41_embed_pkt_cfg {
+       struct cppi41_queue fd_queue; /* Free Descriptor queue.*/
+       u8 num_buf_slot;        /* Number of buffer slots in the descriptor */
+       u8 sop_slot_num;        /* SOP buffer slot number. */
+       struct cppi41_buf_pool free_buf_pool[4]; /* Free Buffer pool. Element */
+                               /* 0 used for the 1st Rx buffer, etc. */
+};
+
+/**
+ * struct cppi41_host_pkt_cfg - Rx Channel Host Packet Configuration
+ *
+ * An instance of this structure forms part of the Rx channel information
+ * structure.
+ */
+struct cppi41_host_pkt_cfg {
+       struct cppi41_queue fdb_queue[4]; /* Free Desc/Buffer queue. Element */
+                               /* 0 used for 1st Rx buffer, etc. */
+};
+
+/**
+ * struct cppi41_mono_pkt_cfg - Rx Channel Monolithic Packet Configuration
+ *
+ * An instance of this structure forms part of the Rx channel information
+ * structure.
+ */
+struct cppi41_mono_pkt_cfg {
+       struct cppi41_queue fd_queue; /* Free descriptor queue */
+       u8 sop_offset;          /* Number of bytes to skip before writing */
+                               /* payload */
+};
+
+enum cppi41_rx_desc_type {
+       cppi41_rx_embed_desc,
+       cppi41_rx_host_desc,
+       cppi41_rx_mono_desc,
+};
+
+/**
+ * struct cppi41_rx_ch_cfg - Rx Channel Configuration
+ *
+ * Must be allocated and filled by the caller of cppi41_rx_ch_configure().
+ *
+ * The same channel can be configured to receive different descripor type
+ * packets (not simaltaneously). When the Rx packets on a port need to be sent
+ * to the SR, the channels default descriptor type is set to Embedded and the
+ * Rx completion queue is set to the queue which CPU polls for input packets.
+ * When in SR bypass mode, the same channel's default descriptor type will be
+ * set to Host and the Rx completion queue set to one of the queues which host
+ * can get interrupted on (via the Queuing proxy/accumulator). In this example,
+ * the embedded mode configuration fetches free descriptor from the Free
+ * descriptor queue (as defined by struct cppi41_embed_pkt_cfg) and host
+ * mode configuration fetches free descriptors/buffers from the free descriptor/
+ * buffer queue (as defined by struct cppi41_host_pkt_cfg).
+ *
+ * NOTE: There seems to be no separate configuration for teardown completion
+ * descriptor. The assumption is rxQueue tuple is used for this purpose as well.
+ */
+struct cppi41_rx_ch_cfg {
+       enum cppi41_rx_desc_type default_desc_type; /* Describes which queue */
+                               /* configuration is used for the free */
+                               /* descriptors and/or buffers */
+       u8 sop_offset;          /* Number of bytes to skip in SOP buffer */
+                               /* before writing payload */
+       u8 retry_starved;       /* 0 = Drop packet on descriptor/buffer */
+                               /* starvartion, 1 = DMA retries FIFO block */
+                               /* transfer at a later time */
+       u8 rx_max_buf_cnt;      /* The DMA ignores the SOP bit and closes up
+                                * a packet after a max_buf_cnt buffer has been
+                                * filled OR if the EOP field is set in the
+                                * info word 0
+                                */
+       struct cppi41_queue rx_queue; /* Rx complete packets queue */
+       union {
+               struct cppi41_host_pkt_cfg host_pkt; /* Host packet */
+                               /* configuration. This defines where channel */
+                               /* picks free descriptors from. */
+               struct cppi41_embed_pkt_cfg embed_pkt; /* Embedded packet */
+                               /* configuration. This defines where channel */
+                               /* picks free descriptors/buffers from. */
+                               /* from. */
+               struct cppi41_mono_pkt_cfg mono_pkt; /* Monolithic packet */
+                               /* configuration. This defines where channel */
+                               /* picks free descriptors from. */
+       } cfg;                  /* Union of packet configuration structures */
+                               /* to be filled in depending on the */
+                               /* defDescType field. */
+};
+
+/**
+ * struct cppi41_tx_ch - Tx channel information
+ *
+ * NOTE: The queues that feed into the Tx channel are fixed at SoC design time.
+ */
+struct cppi41_tx_ch {
+       u8 port_num;            /* Port number. */
+       u8 ch_num;              /* Channel number within port. */
+       u8 sub_ch_num;          /* Sub-channel number within channel. */
+       u8 num_tx_queue;        /* Number of queues from which the channel */
+                               /* can feed. */
+       struct cppi41_queue tx_queue[4]; /* List of queues from which the */
+                               /* channel can feed. */
+};
+
+/**
+ * struct cppi41_dma_block - CPPI 4.1 DMA configuration
+ *
+ * Configuration information for CPPI DMA functionality. Includes the Global
+ * configuration, Channel configuration, and the Scheduler configuration.
+ */
+struct cppi41_dma_block {
+       void __iomem *global_ctrl_base; /* Base address of the Global Control */
+                               /* registers. */
+       void __iomem *ch_ctrl_stat_base; /* Base address of the Channel */
+                               /* Control/Status registers. */
+       void __iomem *sched_ctrl_base; /* Base address of the Scheduler */
+                               /* Control register. */
+       void __iomem *sched_table_base; /* Base address of the Scheduler */
+                               /* Table registers. */
+       u8 num_tx_ch;           /* Number of the Tx channels. */
+       u8 num_rx_ch;           /* Number of the Rx channels. */
+       u8 num_max_ch;          /* maximum dma channels */
+       const struct cppi41_tx_ch *tx_ch_info;
+       struct cppi41_dma_regs cppi41_regs; /* registers to save and restore */
+};
+
+/**
+ * struct cppi41_sched_tbl_t - CPPI 4.1 schdular table
+ */
+struct cppi41_sched_tbl_t {
+       u8      dma_ch;
+       u8      is_tx;
+       u8      enb;
+};
+
+extern struct cppi41_queue_mgr cppi41_queue_mgr[];
+extern struct cppi41_dma_block cppi41_dma_block[];
+
+/**
+ * struct cppi41_dma_ch_obj - CPPI 4.1 DMA Channel object
+ */
+struct cppi41_dma_ch_obj {
+       void __iomem *base_addr; /* The address of the channel global */
+                               /* configuration register */
+       u32 global_cfg;         /* Tx/Rx global configuration backed-up value */
+};
+
+/**
+ * struct cppi41_queue_obj - CPPI 4.1 queue object
+ */
+struct cppi41_queue_obj {
+       void __iomem *base_addr; /* The base address of the queue management */
+                               /* registers */
+};
+
+static inline u32 cppi_readl(const void __iomem *addr)
+       { return readl(addr); }
+static inline void cppi_writel(u32 data, void __iomem *addr)
+       { writel(data, addr); }
+/**
+ * cppi41_queue_mgr_init - CPPI 4.1 queue manager initialization.
+ * @q_mgr:     the queue manager to initialize
+ * @rgn0_base: linking RAM region 0 physical address
+ * @rgn0_size: linking RAM region 0 size in 32-bit words (0 to 0x3fff)
+ *
+ * Returns 0 on success, error otherwise.
+ */
+int cppi41_queue_mgr_init(u8 q_mgr, dma_addr_t rgn0_base, u16 rgn0_size);
+
+/**
+ * cppi41_queue_mgr_init - CPPI 4.1 queue manager un-initialization.
+ * @q_mgr:     the queue manager to un-initialize
+ * Returns 0 on success, error otherwise.
+ */
+int cppi41_queue_mgr_uninit(u8 q_mgr);
+
+/*
+ * CPPI 4.1 Queue Manager Memory Region Allocation and De-allocation APIs.
+ */
+
+/**
+ * cppi41_mem_rgn_alloc - CPPI 4.1 queue manager memory region allocation.
+ * @q_mgr:     the queue manager whose memory region to allocate
+ * @rgn_addr:  physical address of the memory region
+ * @size_order:        descriptor size as a power of two (between 5 and 13)
+ * @num_order: number of descriptors as a power of two (between 5 and 12)
+ * @mem_rgn:   pointer to the index of the memory region allocated
+ *
+ * This function allocates a memory region within the queue manager
+ * consisiting of the descriptors of paricular size and number.
+ *
+ * Returns 0 on success, error otherwise.
+ */
+int cppi41_mem_rgn_alloc(u8 q_mgr, dma_addr_t rgn_addr, u8 size_order,
+                        u8 num_order, u8 *mem_rgn);
+
+/**
+ * cppi41_mem_rgn_free - CPPI 4.1 queue manager memory region de-allocation.
+ * @q_mgr:     the queue manager whose memory region was allocated
+ * @mem_rgn:   index of the memory region
+ *
+ * This function frees the memory region allocated by cppi41_mem_rgn_alloc().
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ */
+int cppi41_mem_rgn_free(u8 q_mgr, u8 mem_rgn);
+
+/**
+ * cppi41_dma_block_init - CPPI 4.1 DMA block initialization.
+ * @dma_num:   number of the DMA block
+ * @q_mgr:     the queue manager in which to allocate the free teardown
+ *             descriptor queue
+ * @num_order: number of teardown descriptors as a power of two (at least 5)
+ * @sched_tbl: the DMA scheduler table
+ * @tbl_size:  number of entries in the DMA scheduler table
+ *
+ * This function frees the memory region allocated by cppi41_mem_rgn_alloc().
+ *
+ * Returns 0 on success, error otherwise.
+ */
+int cppi41_dma_block_init(u8 dma_num, u8 q_mgr, u8 num_order,
+                        struct cppi41_sched_tbl_t *sched_tbl, u8 tbl_size);
+
+/**
+ * cppi41_dma_block_init - CPPI 4.1 DMA block un-initialization.
+ * @dma_num:   number of the DMA block
+ * @q_mgr:     the queue manager in which to allocate the free teardown
+ *             descriptor queue
+ * @num_order: number of teardown descriptors as a power of two (at least 5)
+ * @sched_tbl: the DMA scheduler table
+ * @tbl_size:  number of entries in the DMA scheduler table
+ *
+ * Returns 0 on success, error otherwise.
+ */
+int cppi41_dma_block_uninit(u8 dma_num, u8 q_mgr, u8 num_order,
+                       struct cppi41_sched_tbl_t *sched_tbl, u8 tbl_size);
+
+/*
+ * CPPI 4.1 DMA Channel Management APIs
+ */
+
+/**
+ * cppi41_tx_ch_init - initialize CPPI 4.1 transmit channel object
+ * @tx_ch_obj: pointer to Tx channel object
+ * @dma_num:   DMA block to which this channel belongs
+ * @ch_num:    DMA channel number
+ *
+ * Returns 0 if valid Tx channel, -EINVAL otherwise.
+ */
+int cppi41_tx_ch_init(struct cppi41_dma_ch_obj *tx_ch_obj,
+                     u8 dma_num, u8 ch_num);
+
+/**
+ * cppi41_rx_ch_init - initialize CPPI 4.1 receive channel object
+ * @rx_ch_obj: pointer to Rx channel object
+ * @dma_num:   DMA block to which this channel belongs
+ * @ch_num:    DMA channel number
+ *
+ * Returns 0 if valid Rx channel, -EINVAL otherwise.
+ */
+int cppi41_rx_ch_init(struct cppi41_dma_ch_obj *rx_ch_obj,
+                     u8 dma_num, u8 ch_num);
+
+/**
+ * cppi41_dma_ch_default_queue - set CPPI 4.1 channel default completion queue
+ * @dma_ch_obj: pointer to DMA channel object
+ * @q_mgr:     default queue manager
+ * @q_num:     default queue number
+ *
+ * This function configures the specified channel.  The caller is required to
+ * provide the default queue onto which the teardown descriptors will be queued.
+ */
+void cppi41_dma_ch_default_queue(struct cppi41_dma_ch_obj *dma_ch_obj,
+                                u8 q_mgr, u16 q_num);
+
+/**
+ * cppi41_rx_ch_configure - configure CPPI 4.1 receive channel
+ * @rx_ch_obj: pointer to Rx channel object
+ * @cfg:       pointer to Rx channel configuration
+ *
+ * This function configures and opens the specified Rx channel.  The caller
+ * is required to provide channel configuration information by initializing
+ * a struct cppi41_rx_ch_cfg.
+ */
+void cppi41_rx_ch_configure(struct cppi41_dma_ch_obj *rx_ch_obj,
+                           struct cppi41_rx_ch_cfg  *cfg);
+
+/**
+ * cppi41_rx_ch_set_maxbufcnt - configure max rx buffer count
+ * @rx_ch_obj: pointer to Rx channel object
+ * rx_max_buf_cnt: maximum rx buffer count
+ *
+ * This function configures the maximum rx buffer count in rx dma
+ * global configuration register. The valid rx_max_buf_cnt value
+ * must be 0 to 4.
+ */
+void cppi41_rx_ch_set_maxbufcnt(struct cppi41_dma_ch_obj *rx_ch_obj,
+                           u8 rx_max_buf_cnt);
+/**
+ * cppi41_dma_ch_enable - enable CPPI 4.1 Tx/Rx DMA channel
+ * @dma_ch_obj:        pointer to DMA channel object
+ *
+ * This function enables  a specified Tx channel.  The caller is required to
+ * provide a reference to a channel object initialized by an earlier call of
+ * the cppi41_dma_ch_init() function.  After the successful completion of this
+ * function, the Tx DMA channel will be active and ready for data transmission.
+ */
+void cppi41_dma_ch_enable(struct cppi41_dma_ch_obj *dma_ch_obj);
+
+/**
+ * cppi41_dma_ch_disable - disable CPPI 4.1 Tx/Rx DMA channel
+ * @dma_ch_obj:        pointer to DMA channel object
+ *
+ * This function disables a specific Tx channel.  The caller is required to
+ * provide a reference to a channel object initialized by an earlier call of
+ * the cppi41_dma_ch_init() function.  After the successful completion of this
+ * function, the Tx DMA channel will be deactived.
+ */
+void cppi41_dma_ch_disable(struct cppi41_dma_ch_obj *dma_ch_obj);
+
+/**
+ * cppi41_dma_ch_teardown - tear down CPPI 4.1 transmit channel
+ * @dma_ch_obj:        pointer DMA channel object
+ *
+ * This function triggers the teardown of the given DMA channel.
+ *
+ * ATTENTION: Channel disable should not be called before the teardown is
+ * completed as a disable will stop the DMA scheduling on the channel resulting
+ * in the teardown complete event not being registered at all.
+ *
+ * NOTE: A successful channel teardown event is reported via queueing of a
+ * teardown descriptor.
+ *
+ * This function just sets up for the teardown of the channel and returns. The
+ * caller must detect the channel teardown event to assume that the channel is
+ * disabled.
+ *
+ * See cppi41_get_teardown_info() for the teardown completion processing.
+ */
+void cppi41_dma_ch_teardown(struct cppi41_dma_ch_obj *dma_ch_obj);
+
+/*
+ * CPPI 4.1 Queue Allocation and De-allocation APIs.
+ */
+
+/**
+ * cppi41_queue_alloc - allocate CPPI 4.1 queue
+ * @type:      queue type bitmask
+ * @q_mgr:     queue manager
+ * @q_num:     pointer to the queue number
+ *
+ * Returns 0 if queue allocated, error otherwise.
+ */
+int cppi41_queue_alloc(u8 type, u8 q_mgr, u16 *q_num);
+
+/**
+ * cppi41_queue_free - de-allocate CPPI 4.1 queue
+ * @q_mgr:     queue manager
+ * @q_num:     queue number
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ */
+int cppi41_queue_free(u8 q_mgr, u16 q_num);
+
+/*
+ *  CPPI 4.1 Queue Management APIs
+ */
+
+/**
+ * cppi41_queue_init - initialize CPPI 4.1 queue object
+ * @queue_obj: pointer to the queue object
+ * @q_mgr:     queue manager
+ * @q_num:     queue number
+ *
+ * Returns 0 if valid queue, -EINVAL otherwise.
+ */
+int cppi41_queue_init(struct cppi41_queue_obj *queue_obj, u8 q_mgr, u16 q_num);
+
+/**
+ * cppi41_queue_push - push to CPPI 4.1 queue
+ * @queue_obj: pointer to the queue object
+ * @desc_addr: descriptor physical address
+ * @desc_size: descriptor size
+ * @pkt_size:  packet size
+ *
+ * This function is called to queue a descriptor onto a queue.
+ * NOTE: pSize parameter is optional. Pass 0 in case not required.
+ */
+void cppi41_queue_push(const struct cppi41_queue_obj *queue_obj, u32 desc_addr,
+                      u32 desc_size, u32 pkt_size);
+
+/**
+ * cppi41_queue_pop - pop from CPPI 4.1 queue
+ * @queue_obj: pointer to the queue object
+ *
+ * This function is called to pop a single descriptor from the queue.
+ *
+ * Returns a packet descriptor's physical address.
+ */
+unsigned long cppi41_queue_pop(const struct cppi41_queue_obj *queue_obj);
+
+/*
+ * CPPI 4.1 Miscellaneous APIs
+ */
+
+/**
+ * cppi41_get_teardown_info - CPPI 4.1 teardown completion processing function
+ *
+ * @addr:      physical address of teardown descriptor
+ * @info:      pointer to the teardown information word
+ *
+ * This function is called to complete the teardown processing on a channel
+ * and provides teardown information from the teardown descriptor passed to it.
+ * It also recycles the teardown descriptor back to the teardown descriptor
+ * queue.
+ *
+ * Returns 0 if valid descriptor, -EINVAL otherwise.
+ */
+int cppi41_get_teardown_info(unsigned long addr, u32 *info);
+
+/**
+ * cppi41_schedtbl_add_dma_ch - add a dma channel to schedular table
+ *
+ * @dmanum      Number of DMa block
+ * @qmgr        Queue Manager Number
+ * @dma_ch      dma channel number
+ * @is_tx       transmit (is_tx=1) or recieve(is_tx=0)
+ *
+ * returns      number of channel in schedular table
+ */
+int cppi41_schedtbl_add_dma_ch(u8 dmanum, u8 qmgr, u8 dma_ch, u8 is_tx);
+
+/**
+ * cppi41_schedtbl_remove_dma_ch - remove a dma channel from schedular table
+ *
+ * @dmanum      Number of DMa block
+ * @qmgr        Queue Manager Number
+ * @dma_ch      dma channel number
+ * @is_tx       transmit (is_tx=1) or recieve(is_tx=0)
+ *
+ * returns      number of channel in schedular table
+ */
+int cppi41_schedtbl_remove_dma_ch(u8 dmanum, u8 qmgr, u8 dma_ch, u8 is_tx);
+
+/**
+ * cppi41_init_teardown_queue
+ */
+void cppi41_init_teardown_queue(int dma_num);
+
+/**
+ * cppi41_free_teardown_queue
+ */
+void cppi41_free_teardown_queue(int dma_num);
+
+/**
+ * cppi41_save_context
+ */
+void cppi41_save_context(u8 dma_num);
+
+/**
+ * cppi41_restore_context
+ */
+void cppi41_restore_context(u8 dma_num);
+
+#endif /* __CPPI41_H_ */
diff --git a/drivers/usb/musb/cppi41_dma.c b/drivers/usb/musb/cppi41_dma.c
new file mode 100644 (file)
index 0000000..bcc4c7a
--- /dev/null
@@ -0,0 +1,1954 @@
+/*
+ * Copyright (C) 2005-2006 by Texas Instruments
+ * Copyright (c) 2008, MontaVista Software, Inc. <source@mvista.com>
+ *
+ * This file implements a DMA interface using TI's CPPI 4.1 DMA.
+ *
+ * This program is free software; you can distribute 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 it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "cppi41.h"
+
+#include "musb_core.h"
+#include "musb_dma.h"
+#include "cppi41_dma.h"
+
+/* Configuration */
+#define USB_CPPI41_DESC_SIZE_SHIFT 6
+#define USB_CPPI41_DESC_ALIGN  (1 << USB_CPPI41_DESC_SIZE_SHIFT)
+
+#undef DEBUG_CPPI_TD
+#undef USBDRV_DEBUG
+
+#ifdef USBDRV_DEBUG
+#define dprintk(x, ...) printk(x, ## __VA_ARGS__)
+#else
+#define dprintk(x, ...)
+#endif
+
+/*
+ * Data structure definitions
+ */
+
+#define USBREQ_DMA_INIT                        0
+#define USBREQ_DMA_START               1
+#define USBREQ_DMA_INPROGRESS          2
+#define USBREQ_DMA_COMPLETE            3
+#define USBREQ_DMA_SHORTPKT_COMPLETE   4
+/*
+ * USB Packet Descriptor
+ */
+struct usb_pkt_desc;
+
+struct usb_pkt_desc {
+       /* Hardware descriptor fields from this point */
+       struct cppi41_host_pkt_desc hw_desc;    /* 40 bytes */
+       /* Protocol specific data */
+       dma_addr_t dma_addr;                    /* offs:44 byte */
+       struct usb_pkt_desc *next_pd_ptr;       /* offs:48 byte*/
+       u8 ch_num;
+       u8 ep_num;
+       u8 eop;
+       u8 res1;                                /* offs:52 */
+       u8 res2[12];                            /* offs:64 */
+};
+
+/**
+ * struct cppi41_channel - DMA Channel Control Structure
+ *
+ * Using the same for Tx/Rx.
+ */
+struct cppi41_channel {
+       struct dma_channel channel;
+
+       struct cppi41_dma_ch_obj dma_ch_obj; /* DMA channel object */
+       struct cppi41_queue src_queue;  /* Tx queue or Rx free descriptor/ */
+                                       /* buffer queue */
+       struct cppi41_queue_obj queue_obj; /* Tx queue object or Rx free */
+                                       /* descriptor/buffer queue object */
+
+       u32 tag_info;                   /* Tx PD Tag Information field */
+
+       /* Which direction of which endpoint? */
+       struct musb_hw_ep *end_pt;
+       u8 transmit;
+       u8 ch_num;                      /* Channel number of Tx/Rx 0..3 */
+
+       /* DMA mode: "transparent", RNDIS, CDC, or Generic RNDIS */
+       u8 dma_mode;
+       u8 autoreq;
+
+       /* Book keeping for the current transfer request */
+       dma_addr_t start_addr;
+       u32 length;
+       u32 curr_offset;
+       u16 pkt_size;
+       u8  transfer_mode;
+       u8  zlp_queued;
+       u8  inf_mode;
+       u8  tx_complete;
+       u8  rx_complete;
+       u8  hb_mult;
+       u8 xfer_state;
+       u32  count;
+       struct usb_pkt_desc *curr_pd;
+};
+
+
+/**
+ * struct cppi41 - CPPI 4.1 DMA Controller Object
+ *
+ * Encapsulates all book keeping and data structures pertaining to
+ * the CPPI 1.4 DMA controller.
+ */
+struct cppi41 {
+       struct dma_controller controller;
+       struct musb *musb;
+
+       struct cppi41_channel *tx_cppi_ch;
+       struct cppi41_channel *rx_cppi_ch;
+       struct work_struct      txdma_work;
+       struct work_struct      rxdma_work;
+
+       struct usb_pkt_desc *pd_pool_head; /* Free PD pool head */
+       dma_addr_t pd_mem_phys;         /* PD memory physical address */
+       void *pd_mem;                   /* PD memory pointer */
+       u8 pd_mem_rgn;                  /* PD memory region number */
+
+       u16 teardown_qnum;              /* Teardown completion queue number */
+       struct cppi41_queue_obj queue_obj; /* Teardown completion queue */
+                                       /* object */
+       u32 pkt_info;                   /* Tx PD Packet Information field */
+       struct usb_cppi41_info *cppi_info; /* cppi channel information */
+       u32 bd_size;
+       u8  inf_mode;
+       u8  sof_isoc_started;
+};
+
+struct usb_cppi41_info usb_cppi41_info[2];
+EXPORT_SYMBOL_GPL(usb_cppi41_info);
+static void rxdma_completion_work(struct work_struct *data);
+
+#ifdef DEBUG_CPPI_TD
+static void print_pd_list(struct usb_pkt_desc *pd_pool_head)
+{
+       struct usb_pkt_desc *curr_pd = pd_pool_head;
+       int cnt = 0;
+
+       while (curr_pd != NULL) {
+               if (cnt % 8 == 0)
+                       dprintk("\n%02x ", cnt);
+               cnt++;
+               dprintk(" %p", curr_pd);
+               curr_pd = curr_pd->next_pd_ptr;
+       }
+       dprintk("\n");
+}
+#endif
+
+static struct usb_pkt_desc *usb_get_free_pd(struct cppi41 *cppi)
+{
+       struct usb_pkt_desc *free_pd = cppi->pd_pool_head;
+
+       if (free_pd != NULL) {
+               cppi->pd_pool_head = free_pd->next_pd_ptr;
+               free_pd->next_pd_ptr = NULL;
+       }
+       return free_pd;
+}
+
+static void usb_put_free_pd(struct cppi41 *cppi, struct usb_pkt_desc *free_pd)
+{
+       free_pd->next_pd_ptr = cppi->pd_pool_head;
+       cppi->pd_pool_head = free_pd;
+}
+
+int get_xfer_type(struct musb_hw_ep *hw_ep, u8 is_tx)
+{
+       u8 type = musb_readb(hw_ep->regs, is_tx ? MUSB_TXTYPE : MUSB_RXTYPE);
+       return (type >> 4) & 3;
+}
+
+static void musb_enable_tx_dma(struct musb_hw_ep *hw_ep)
+{
+       void __iomem *epio = hw_ep->regs;
+       u16 csr;
+
+       csr = musb_readw(epio, MUSB_TXCSR);
+       csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE;
+       if (is_host_active(hw_ep->musb))
+               csr |= MUSB_TXCSR_H_WZC_BITS;
+       else
+               csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_P_WZC_BITS;
+       musb_writew(epio, MUSB_TXCSR, csr);
+}
+
+/**
+ * dma_controller_start - start DMA controller
+ * @controller: the controller
+ *
+ * This function initializes the CPPI 4.1 Tx/Rx channels.
+ */
+static int dma_controller_start(struct dma_controller *controller)
+{
+       struct cppi41 *cppi;
+       struct cppi41_channel *cppi_ch;
+       void __iomem *reg_base;
+       struct usb_pkt_desc *curr_pd;
+       unsigned long pd_addr;
+       int i, max_pkt_desc;
+       struct usb_cppi41_info *cppi_info;
+       struct musb *musb;
+       struct device *dev;
+
+       cppi = container_of(controller, struct cppi41, controller);
+       cppi_info = cppi->cppi_info;
+       musb = cppi->musb;
+       dev = musb->controller;
+       max_pkt_desc = cppi_info->max_pkt_desc;
+
+       /*
+        * TODO: We may need to check USB_CPPI41_MAX_PD here since CPPI 4.1
+        * requires the descriptor count to be a multiple of 2 ^ 5 (i.e. 32).
+        * Similarly, the descriptor size should also be a multiple of 32.
+        */
+
+       /*
+        * Allocate free packet descriptor pool for all Tx/Rx endpoints --
+        * dma_alloc_coherent()  will return a page aligned address, so our
+        * alignment requirement will be honored.
+        */
+       cppi->bd_size = max_pkt_desc * sizeof(struct usb_pkt_desc);
+       cppi->pd_mem = dma_alloc_coherent(cppi->musb->controller,
+                                         cppi->bd_size,
+                                         &cppi->pd_mem_phys,
+                                         GFP_KERNEL | GFP_DMA);
+       if (!cppi->pd_mem)
+               return -ENOMEM;
+
+       if (cppi41_mem_rgn_alloc(cppi_info->q_mgr, cppi->pd_mem_phys,
+                                USB_CPPI41_DESC_SIZE_SHIFT,
+                                get_count_order(max_pkt_desc),
+                                &cppi->pd_mem_rgn)) {
+               dev_dbg(dev, "failed to alloc q_mgr memory region");
+               goto free_pds;
+       }
+
+       /* Allocate the teardown completion queue */
+       if (cppi41_queue_alloc(CPPI41_UNASSIGNED_QUEUE,
+                              0, &cppi->teardown_qnum)) {
+               dev_dbg(dev, "failed to alloc td compl queue");
+               goto free_mem_rgn;
+       }
+       dev_dbg(dev, "allocated td-compl queue %d q_mgr 0\n",
+               cppi->teardown_qnum);
+
+       if (cppi41_queue_init(&cppi->queue_obj, 0, cppi->teardown_qnum)) {
+               dev_dbg(dev, "faile to init td completion queue");
+               goto free_queue;
+       }
+
+       /*
+        * "Slice" PDs one-by-one from the big chunk and
+        * add them to the free pool.
+        */
+       curr_pd = (struct usb_pkt_desc *)cppi->pd_mem;
+       pd_addr = cppi->pd_mem_phys;
+       for (i = 0; i < max_pkt_desc; i++) {
+               curr_pd->dma_addr = pd_addr;
+
+               usb_put_free_pd(cppi, curr_pd);
+               curr_pd = (struct usb_pkt_desc *)((char *)curr_pd +
+                                                 USB_CPPI41_DESC_ALIGN);
+               pd_addr += USB_CPPI41_DESC_ALIGN;
+       }
+
+       /* Configure the Tx channels */
+       for (i = 0, cppi_ch = cppi->tx_cppi_ch;
+            i < cppi_info->max_dma_ch; ++i, ++cppi_ch) {
+               const struct cppi41_tx_ch *tx_info;
+
+               memset(cppi_ch, 0, sizeof(struct cppi41_channel));
+               cppi_ch->transmit = 1;
+               cppi_ch->ch_num = i;
+               cppi_ch->channel.private_data = cppi;
+
+               /*
+                * Extract the CPPI 4.1 DMA Tx channel configuration and
+                * construct/store the Tx PD tag info field for later use...
+                */
+               tx_info = cppi41_dma_block[cppi_info->dma_block].tx_ch_info
+                         + cppi_info->ep_dma_ch[i];
+               cppi_ch->src_queue = tx_info->tx_queue[0];
+               cppi_ch->tag_info = (tx_info->port_num <<
+                                    CPPI41_SRC_TAG_PORT_NUM_SHIFT) |
+                                   (tx_info->ch_num <<
+                                    CPPI41_SRC_TAG_CH_NUM_SHIFT) |
+                                   (tx_info->sub_ch_num <<
+                                    CPPI41_SRC_TAG_SUB_CH_NUM_SHIFT);
+       }
+
+       /* Configure the Rx channels */
+       for (i = 0, cppi_ch = cppi->rx_cppi_ch;
+            i < cppi_info->max_dma_ch; ++i, ++cppi_ch) {
+               memset(cppi_ch, 0, sizeof(struct cppi41_channel));
+               cppi_ch->ch_num = i;
+               cppi_ch->channel.private_data = cppi;
+       }
+
+       /* Construct/store Tx PD packet info field for later use */
+       cppi->pkt_info = (CPPI41_PKT_TYPE_USB << CPPI41_PKT_TYPE_SHIFT) |
+                        (CPPI41_RETURN_LINKED << CPPI41_RETURN_POLICY_SHIFT);
+
+       /* Do necessary configuartion in hardware to get started */
+       reg_base = cppi->musb->ctrl_base;
+
+       /* Disable auto request mode */
+       musb_writel(reg_base, cppi_info->wrp.autoreq_reg, 0);
+
+       /* Disable the CDC/RNDIS modes */
+       musb_writel(reg_base, cppi_info->wrp.tx_mode_reg, 0);
+       musb_writel(reg_base, cppi_info->wrp.rx_mode_reg, 0);
+
+       return 0;
+
+ free_queue:
+       if (cppi41_queue_free(0, cppi->teardown_qnum))
+               dev_dbg(dev, "failed to free td-compl queue\n");
+
+ free_mem_rgn:
+       if (cppi41_mem_rgn_free(cppi_info->q_mgr, cppi->pd_mem_rgn))
+               dev_dbg(dev, "failed to free q_mgr mem-region\n");
+
+ free_pds:
+       dma_free_coherent(dev, cppi->bd_size, cppi->pd_mem, cppi->pd_mem_phys);
+
+       return -ENOMEM;
+}
+
+/**
+ * dma_controller_stop - stop DMA controller
+ * @controller: the controller
+ *
+ * De-initialize the DMA Controller as necessary.
+ */
+static int dma_controller_stop(struct dma_controller *controller)
+{
+       struct cppi41 *cppi;
+       void __iomem *reg_base;
+       struct usb_cppi41_info *cppi_info;
+       struct musb *musb;
+       struct device *dev;
+
+       cppi = container_of(controller, struct cppi41, controller);
+       cppi_info = cppi->cppi_info;
+       musb = cppi->musb;
+       dev = musb->controller;
+
+       /* Free the teardown completion queue */
+       if (cppi41_queue_free(cppi_info->q_mgr, cppi->teardown_qnum))
+               dev_dbg(dev, "failed to free td compl queue\n");
+
+       /*
+        * Free the packet descriptor region allocated
+        * for all Tx/Rx channels.
+        */
+       if (cppi41_mem_rgn_free(cppi_info->q_mgr, cppi->pd_mem_rgn))
+               dev_dbg(dev, "failed to free q_mgr mem-region\n");
+
+       dma_free_coherent(dev, cppi->bd_size, cppi->pd_mem, cppi->pd_mem_phys);
+
+       cppi->pd_mem = 0;
+       cppi->pd_mem_phys = 0;
+       cppi->pd_pool_head = 0;
+       cppi->bd_size = 0;
+
+       reg_base = cppi->musb->ctrl_base;
+
+       /* Disable auto request mode */
+       musb_writel(reg_base, cppi_info->wrp.autoreq_reg, 0);
+
+       /* Disable the CDC/RNDIS modes */
+       musb_writel(reg_base, cppi_info->wrp.tx_mode_reg, 0);
+       musb_writel(reg_base, cppi_info->wrp.rx_mode_reg, 0);
+
+       return 1;
+}
+
+/**
+ * cppi41_channel_alloc - allocate a CPPI channel for DMA.
+ * @controller: the controller
+ * @ep:                the endpoint
+ * @is_tx:     1 for Tx channel, 0 for Rx channel
+ *
+ * With CPPI, channels are bound to each transfer direction of a non-control
+ * endpoint, so allocating (and deallocating) is mostly a way to notice bad
+ * housekeeping on the software side.  We assume the IRQs are always active.
+ */
+static struct dma_channel *cppi41_channel_alloc(struct dma_controller
+                                               *controller,
+                                               struct musb_hw_ep *ep, u8 is_tx)
+{
+       struct cppi41 *cppi;
+       struct cppi41_channel  *cppi_ch;
+       u32 ch_num, ep_num = ep->epnum;
+       struct usb_cppi41_info *cppi_info;
+       struct musb *musb;
+       struct device *dev;
+
+       cppi = container_of(controller, struct cppi41, controller);
+       cppi_info = cppi->cppi_info;
+       musb = cppi->musb;
+       dev = musb->controller;
+
+       /* Remember, ep_num: 1 .. Max_EP, and CPPI ch_num: 0 .. Max_EP - 1 */
+       ch_num = ep_num - 1;
+
+       if (ep_num > cppi_info->max_dma_ch) {
+               dev_dbg(dev, "No %cx DMA channel for EP%d\n",
+                   is_tx ? 'T' : 'R', ep_num);
+               return NULL;
+       }
+
+       cppi_ch = (is_tx ? cppi->tx_cppi_ch : cppi->rx_cppi_ch) + ch_num;
+
+       /* As of now, just return the corresponding CPPI 4.1 channel handle */
+       if (is_tx) {
+               /* Initialize the CPPI 4.1 Tx DMA channel */
+               if (cppi41_tx_ch_init(&cppi_ch->dma_ch_obj,
+                                     cppi_info->dma_block,
+                                     cppi_info->ep_dma_ch[ch_num])) {
+                       dev_dbg(dev, "failed to init tx_ch %d\n", ch_num);
+                       return NULL;
+               }
+               /*
+                * Teardown descriptors will be pushed to the dedicated
+                * completion queue.
+                */
+               cppi41_dma_ch_default_queue(&cppi_ch->dma_ch_obj,
+                                           0, cppi->teardown_qnum);
+       } else {
+               struct cppi41_rx_ch_cfg rx_cfg;
+               u8 q_mgr = cppi_info->q_mgr;
+               int i;
+
+               /* Initialize the CPPI 4.1 Rx DMA channel */
+               if (cppi41_rx_ch_init(&cppi_ch->dma_ch_obj,
+                                     cppi_info->dma_block,
+                                     cppi_info->ep_dma_ch[ch_num])) {
+                       dev_dbg(dev, "failed to init rx_ch %d\n", ch_num);
+                       return NULL;
+               }
+
+               if (cppi41_queue_alloc(CPPI41_FREE_DESC_BUF_QUEUE |
+                                      CPPI41_UNASSIGNED_QUEUE,
+                                      q_mgr, &cppi_ch->src_queue.q_num)) {
+                       dev_dbg(dev, "fail to alloc free desc queue\n");
+                       return NULL;
+               }
+
+               dev_dbg(dev, "allocated free desc queue %d\n",
+                       cppi_ch->src_queue.q_num);
+
+               rx_cfg.default_desc_type = cppi41_rx_host_desc;
+               rx_cfg.sop_offset = 0;
+               rx_cfg.retry_starved = 1;
+               rx_cfg.rx_max_buf_cnt = 0;
+               rx_cfg.rx_queue.q_mgr = cppi_ch->src_queue.q_mgr = q_mgr;
+               rx_cfg.rx_queue.q_num = cppi_info->rx_comp_q[ch_num];
+               for (i = 0; i < 4; i++)
+                       rx_cfg.cfg.host_pkt.fdb_queue[i] = cppi_ch->src_queue;
+               cppi41_rx_ch_configure(&cppi_ch->dma_ch_obj, &rx_cfg);
+       }
+
+       /* Initialize the CPPI 4.1 DMA source queue */
+       if (cppi41_queue_init(&cppi_ch->queue_obj, cppi_ch->src_queue.q_mgr,
+                              cppi_ch->src_queue.q_num)) {
+               dev_dbg(dev, "failed to init %s queue",
+                   is_tx ? "Tx" : "Rx free descriptor/buffer");
+               if (is_tx == 0 &&
+                   cppi41_queue_free(cppi_ch->src_queue.q_mgr,
+                                     cppi_ch->src_queue.q_num))
+                       dev_dbg(dev, "fail to free rx free desc queue %d\n",
+                               cppi_ch->src_queue.q_num);
+                return NULL;
+       }
+
+       /* Enable the DMA channel */
+       cppi41_dma_ch_enable(&cppi_ch->dma_ch_obj);
+
+       if (cppi_ch->end_pt)
+               dev_dbg(dev, "re-alloc %cx dmach %d (%p)\n",
+                   is_tx ? 'T' : 'R', ch_num, cppi_ch);
+
+       cppi_ch->end_pt = ep;
+       cppi_ch->ch_num = ch_num;
+       cppi_ch->channel.status = MUSB_DMA_STATUS_FREE;
+       cppi_ch->channel.max_len = is_tx ?
+                               CPPI41_TXDMA_MAXLEN : CPPI41_RXDMA_MAXLEN;
+
+       dev_dbg(dev, "allocated %cx dmach %d for ep%d\n",
+               is_tx ? 'T' : 'R', ch_num, ep_num);
+
+       return &cppi_ch->channel;
+}
+
+/**
+ * cppi41_channel_release - release a CPPI DMA channel
+ * @channel: the channel
+ */
+static void cppi41_channel_release(struct dma_channel *channel)
+{
+       struct cppi41_channel *cppi_ch;
+       struct cppi41 *cppi;
+       struct device *dev;
+
+       /* REVISIT: for paranoia, check state and abort if needed... */
+       cppi_ch = container_of(channel, struct cppi41_channel, channel);
+       cppi = cppi_ch->channel.private_data;
+       dev = cppi->musb->controller;
+
+       if (cppi_ch->end_pt == NULL)
+               dev_dbg(dev, "Releasing idle DMA channel %p\n", cppi_ch);
+
+       /* But for now, not its IRQ */
+       cppi_ch->end_pt = NULL;
+       channel->status = MUSB_DMA_STATUS_UNKNOWN;
+
+       cppi41_dma_ch_disable(&cppi_ch->dma_ch_obj);
+
+       /* De-allocate Rx free descriptior/buffer queue */
+       if (cppi_ch->transmit == 0 &&
+           cppi41_queue_free(cppi_ch->src_queue.q_mgr,
+                             cppi_ch->src_queue.q_num))
+               dev_err(dev, "failed to free Rx descriptor/buffer queue %d\n",
+                       cppi_ch->src_queue.q_num);
+}
+
+static void cppi41_mode_update(struct cppi41_channel *cppi_ch, u8 mode)
+{
+       if (mode != cppi_ch->dma_mode) {
+               struct cppi41 *cppi = cppi_ch->channel.private_data;
+               struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+               void *__iomem reg_base = cppi->musb->ctrl_base;
+               u32 reg_val;
+               u8 ep_num = cppi_ch->ch_num + 1;
+
+               if (cppi_ch->transmit) {
+                       reg_val = musb_readl(reg_base,
+                                       cppi_info->wrp.tx_mode_reg);
+                       reg_val &= ~USB_TX_MODE_MASK(ep_num);
+                       reg_val |= mode << USB_TX_MODE_SHIFT(ep_num);
+                       musb_writel(reg_base, cppi_info->wrp.tx_mode_reg,
+                               reg_val);
+               } else {
+                       reg_val = musb_readl(reg_base,
+                                       cppi_info->wrp.rx_mode_reg);
+                       reg_val &= ~USB_RX_MODE_MASK(ep_num);
+                       reg_val |= mode << USB_RX_MODE_SHIFT(ep_num);
+                       musb_writel(reg_base, cppi_info->wrp.rx_mode_reg,
+                               reg_val);
+               }
+               cppi_ch->dma_mode = mode;
+       }
+}
+
+/*
+ * CPPI 4.1 Tx:
+ * ============
+ * Tx is a lot more reasonable than Rx: RNDIS mode seems to behave well except
+ * how it handles the exactly-N-packets case. It appears that there's a hiccup
+ * in that case (maybe the DMA completes before a ZLP gets written?) boiling
+ * down to not being able to rely on the XFER DMA writing any terminating zero
+ * length packet before the next transfer is started...
+ *
+ * The generic RNDIS mode does not have this misfeature, so we prefer using it
+ * instead.  We then send the terminating ZLP *explictly* using DMA instead of
+ * doing it by PIO after an IRQ.
+ *
+ */
+
+/**
+ * cppi41_next_tx_segment - DMA write for the next chunk of a buffer
+ * @tx_ch:     Tx channel
+ *
+ * Context: controller IRQ-locked
+ */
+static unsigned cppi41_next_tx_segment(struct cppi41_channel *tx_ch)
+{
+       struct cppi41 *cppi = tx_ch->channel.private_data;
+       struct musb *musb = cppi->musb;
+       struct device *dev = musb->controller;
+       struct usb_pkt_desc *curr_pd;
+       u32 length = tx_ch->length - tx_ch->curr_offset;
+       u32 pkt_size = tx_ch->pkt_size;
+       unsigned num_pds, n;
+       struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+       u16 q_mgr = cppi_info->q_mgr;
+       u16 tx_comp_q = cppi_info->tx_comp_q[tx_ch->ch_num];
+       u32 residue;
+       u8 is_isoc = get_xfer_type(tx_ch->end_pt, 1) == USB_ENDPOINT_XFER_ISOC;
+
+       if (!is_isoc && length > 128) {
+               residue = length % tx_ch->pkt_size;
+               if (residue <= 128)
+                       length -= residue;
+       }
+
+       /*
+        * Tx can use the generic RNDIS mode where we can probably fit this
+        * transfer in one PD and one IRQ.  The only time we would NOT want
+        * to use it is when the hardware constraints prevent it...
+        */
+       if ((pkt_size & 0x3f) == 0) {
+               num_pds  = length ? 1 : 0;
+               cppi41_mode_update(tx_ch, USB_GENERIC_RNDIS_MODE);
+       } else {
+               num_pds  = (length + pkt_size - 1) / pkt_size;
+               cppi41_mode_update(tx_ch, USB_TRANSPARENT_MODE);
+       }
+
+       pkt_size = length;
+       /*
+        * If length of transmit buffer is 0 or a multiple of the endpoint size,
+        * then send the zero length packet.
+        */
+       if (!length || (tx_ch->transfer_mode && length % pkt_size == 0))
+               num_pds++;
+
+       dev_dbg(dev, "TX DMA%u, %s, maxpkt %u, %u PDs, addr %#x, len %u\n",
+           tx_ch->ch_num, tx_ch->dma_mode ? "accelerated" : "transparent",
+           pkt_size, num_pds, tx_ch->start_addr + tx_ch->curr_offset, length);
+
+       if (is_isoc)
+               tx_ch->xfer_state = USBREQ_DMA_INIT;
+
+       for (n = 0; n < num_pds; n++) {
+               struct cppi41_host_pkt_desc *hw_desc;
+
+               /* Get Tx host packet descriptor from the free pool */
+               curr_pd = usb_get_free_pd(cppi);
+               if (curr_pd == NULL) {
+                       dev_dbg(dev, "No Tx PDs\n");
+                       break;
+               }
+
+               if (length < pkt_size)
+                       pkt_size = length;
+
+               hw_desc = &curr_pd->hw_desc;
+               hw_desc->desc_info = (CPPI41_DESC_TYPE_HOST <<
+                                     CPPI41_DESC_TYPE_SHIFT) | pkt_size;
+               hw_desc->tag_info = tx_ch->tag_info;
+               hw_desc->pkt_info = cppi->pkt_info;
+               hw_desc->pkt_info |= ((q_mgr << CPPI41_RETURN_QMGR_SHIFT) |
+                               (tx_comp_q << CPPI41_RETURN_QNUM_SHIFT));
+
+               hw_desc->buf_ptr = tx_ch->start_addr + tx_ch->curr_offset;
+               hw_desc->buf_len = pkt_size;
+               hw_desc->next_desc_ptr = 0;
+               hw_desc->orig_buf_len = pkt_size;
+
+               curr_pd->ch_num = tx_ch->ch_num;
+               curr_pd->ep_num = tx_ch->end_pt->epnum;
+
+               tx_ch->curr_offset += pkt_size;
+               length -= pkt_size;
+
+               if (pkt_size == 0)
+                       tx_ch->zlp_queued = 1;
+
+               if (cppi_info->bd_intr_enb)
+                       hw_desc->orig_buf_len |= CPPI41_PKT_INTR_FLAG;
+
+               dev_dbg(dev, "TX PD %p: buf %08x, len %08x, pkt info %08x\n",
+                       curr_pd, hw_desc->buf_ptr, hw_desc->buf_len,
+                       hw_desc->pkt_info);
+
+               /* make sure descriptor details are updated to memory*/
+               dsb();
+
+               cppi41_queue_push(&tx_ch->queue_obj, curr_pd->dma_addr,
+                                 USB_CPPI41_DESC_ALIGN, pkt_size);
+
+               if (is_isoc && cppi_info->tx_isoc_sched_enab) {
+                       tx_ch->xfer_state = USBREQ_DMA_START;
+                       musb_enable_sof(cppi->musb);
+                       if (cppi->sof_isoc_started) {
+                               tx_ch->xfer_state = USBREQ_DMA_INPROGRESS;
+                               musb_enable_tx_dma(tx_ch->end_pt);
+                       }
+               }
+
+       }
+
+       return n;
+}
+
+static void cppi41_autoreq_update(struct cppi41_channel *rx_ch, u8 autoreq)
+{
+       struct cppi41 *cppi = rx_ch->channel.private_data;
+       struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+
+       if (is_host_active(cppi->musb) &&
+           autoreq != rx_ch->autoreq) {
+               void *__iomem reg_base = cppi->musb->ctrl_base;
+               u32 reg_val = musb_readl(reg_base,
+                               cppi_info->wrp.autoreq_reg);
+               u8 ep_num = rx_ch->ch_num + 1;
+
+               reg_val &= ~USB_RX_AUTOREQ_MASK(ep_num);
+               reg_val |= autoreq << USB_RX_AUTOREQ_SHIFT(ep_num);
+
+               musb_writel(reg_base, cppi_info->wrp.autoreq_reg, reg_val);
+               rx_ch->autoreq = autoreq;
+       }
+}
+
+static void cppi41_set_ep_size(struct cppi41_channel *rx_ch, u32 pkt_size)
+{
+       struct cppi41 *cppi = rx_ch->channel.private_data;
+       void *__iomem reg_base = cppi->musb->ctrl_base;
+       u8 ep_num = rx_ch->ch_num + 1;
+       u32 res = pkt_size % 64;
+
+       /* epsize register must be multiple of 64 */
+       pkt_size += res ? (64 - res) : res;
+
+       musb_writel(reg_base, USB_GENERIC_RNDIS_EP_SIZE_REG(ep_num), pkt_size);
+}
+
+/*
+ * CPPI 4.1 Rx:
+ * ============
+ * Consider a 1KB bulk Rx buffer in two scenarios: (a) it's fed two 300 byte
+ * packets back-to-back, and (b) it's fed two 512 byte packets back-to-back.
+ * (Full speed transfers have similar scenarios.)
+ *
+ * The correct behavior for Linux is that (a) fills the buffer with 300 bytes,
+ * and the next packet goes into a buffer that's queued later; while (b) fills
+ * the buffer with 1024 bytes.  How to do that with accelerated DMA modes?
+ *
+ * Rx queues in RNDIS mode (one single BD) handle (a) correctly but (b) loses
+ * BADLY because nothing (!) happens when that second packet fills the buffer,
+ * much less when a third one arrives -- which makes it not a "true" RNDIS mode.
+ * In the RNDIS protocol short-packet termination is optional, and it's fine if
+ * the peripherals (not hosts!) pad the messages out to end of buffer. Standard
+ * PCI host controller DMA descriptors implement that mode by default... which
+ * is no accident.
+ *
+ * Generic RNDIS mode is the only way to reliably make both cases work.  This
+ * mode is identical to the "normal" RNDIS mode except for the case where the
+ * last packet of the segment matches the max USB packet size -- in this case,
+ * the packet will be closed when a value (0x10000 max) in the Generic RNDIS
+ * EP Size register is reached.  This mode will work for the network drivers
+ * (CDC/RNDIS) as well as for the mass storage drivers where there is no short
+ * packet.
+ *
+ * BUT we can only use non-transparent modes when USB packet size is a multiple
+ * of 64 bytes. Let's see what happens when  this is not the case...
+ *
+ * Rx queues (2 BDs with 512 bytes each) have converse problems to RNDIS mode:
+ * (b) is handled right but (a) loses badly.  DMA doesn't stop after receiving
+ * a short packet and processes both of those PDs; so both packets are loaded
+ * into the buffer (with 212 byte gap between them), and the next buffer queued
+ * will NOT get its 300 bytes of data.  Even in the case when there should be
+ * no short packets (URB_SHORT_NOT_OK is set), queueing several packets in the
+ * host mode doesn't win us anything since we have to manually "prod" the Rx
+ * process after each packet is received by setting ReqPkt bit in endpoint's
+ * RXCSR; in the peripheral mode without short packets, queueing could be used
+ * BUT we'll have to *teardown* the channel if a short packet still arrives in
+ * the peripheral mode, and to "collect" the left-over packet descriptors from
+ * the free descriptor/buffer queue in both cases...
+ *
+ * One BD at a time is the only way to make make both cases work reliably, with
+ * software handling both cases correctly, at the significant penalty of needing
+ * an IRQ per packet.  (The lack of I/O overlap can be slightly ameliorated by
+ * enabling double buffering.)
+ *
+ * There seems to be no way to identify for sure the cases where the CDC mode
+ * is appropriate...
+ *
+ */
+
+/**
+ * cppi41_next_rx_segment - DMA read for the next chunk of a buffer
+ * @rx_ch:     Rx channel
+ *
+ * Context: controller IRQ-locked
+ *
+ * NOTE: In the transparent mode, we have to queue one packet at a time since:
+ *      - we must avoid starting reception of another packet after receiving
+ *        a short packet;
+ *      - in host mode we have to set ReqPkt bit in the endpoint's RXCSR after
+ *        receiving each packet but the last one... ugly!
+ */
+static unsigned cppi41_next_rx_segment(struct cppi41_channel *rx_ch)
+{
+       struct cppi41 *cppi = rx_ch->channel.private_data;
+       struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+       struct musb *musb = cppi->musb;
+       struct device *dev = musb->controller;
+       struct usb_pkt_desc *curr_pd;
+       struct cppi41_host_pkt_desc *hw_desc;
+       u32 length = rx_ch->length - rx_ch->curr_offset;
+       u32 pkt_size = rx_ch->pkt_size;
+       u32 max_rx_transfer_size = MAX_GRNDIS_PKTSIZE;
+       u32 i, n_bd , pkt_len;
+       u8 dma_mode, autoreq;
+       u8 rx_dma_mode = cppi_info->rx_dma_mode;
+       struct platform_device *pdev = to_platform_device(dev);
+       int id = pdev->id;
+
+       pkt_len = rx_ch->length;
+       /*
+        * Rx can use the generic RNDIS mode where we can
+        * probably fit this transfer in one PD and one IRQ
+        * (or two with a short packet).
+        */
+       dma_mode = USB_TRANSPARENT_MODE;
+       autoreq = USB_AUTOREQ_ALL_BUT_EOP;
+
+       if (is_peripheral_active(cppi->musb))
+               rx_dma_mode = USB_TRANSPARENT_MODE;
+
+       if (((pkt_size & 0x3f) == 0) &&
+               rx_dma_mode == USB_GENERIC_RNDIS_MODE) {
+                       dma_mode = USB_GENERIC_RNDIS_MODE;
+       }
+
+       if (dma_mode == USB_GENERIC_RNDIS_MODE) {
+               if (cppi->cppi_info->rx_inf_mode) {
+                       if (length >= 2 * rx_ch->pkt_size)
+                               dma_mode = USB_INFINITE_DMAMODE;
+                       else
+                               dma_mode = USB_TRANSPARENT_MODE;
+               }
+       }
+
+       if (length < rx_ch->pkt_size)
+               dma_mode = USB_TRANSPARENT_MODE;
+
+       if (dma_mode == USB_INFINITE_DMAMODE) {
+               pkt_len = 0;
+               length = length - rx_ch->pkt_size;
+               cppi41_rx_ch_set_maxbufcnt(
+                       &rx_ch->dma_ch_obj,
+                       DMA_CH_RX_MAX_BUF_CNT_1);
+                       rx_ch->inf_mode = 1;
+               dma_mode = USB_GENERIC_RNDIS_MODE;
+               autoreq = USB_AUTOREQ_ALWAYS;
+       } else {
+               if (pkt_len > max_rx_transfer_size)
+                       pkt_len = max_rx_transfer_size;
+       }
+
+       /* update cppi mode */
+       cppi41_mode_update(rx_ch, dma_mode);
+
+       if (dma_mode != USB_TRANSPARENT_MODE) {
+               if (is_host_active(cppi->musb))
+                       cppi41_autoreq_update(rx_ch, autoreq);
+               cppi41_set_ep_size(rx_ch, pkt_len);
+       } else if (is_host_active(cppi->musb)) {
+               cppi41_autoreq_update(rx_ch, USB_NO_AUTOREQ);
+       }
+
+       dev_dbg(dev, "RX DMA%u, %s, maxpkt %u, addr %#x, rec'd %u/%u\n",
+           rx_ch->ch_num, rx_ch->dma_mode ? "accelerated" : "transparent",
+           pkt_size, rx_ch->start_addr + rx_ch->curr_offset,
+           rx_ch->curr_offset, rx_ch->length);
+
+       /* calculate number of bd required */
+       n_bd = (length + max_rx_transfer_size - 1)/max_rx_transfer_size;
+       if (dma_mode == USB_TRANSPARENT_MODE) {
+               if (!rx_ch->hb_mult)
+                       max_rx_transfer_size = rx_ch->pkt_size;
+               else
+                       max_rx_transfer_size = rx_ch->hb_mult * rx_ch->pkt_size;
+       }
+
+       for (i = 0; i < n_bd ; ++i) {
+               /* Get Rx packet descriptor from the free pool */
+               curr_pd = usb_get_free_pd(cppi);
+               if (curr_pd == NULL) {
+                       /* Shouldn't ever happen! */
+                       dev_dbg(dev, "No Rx PDs\n");
+                       goto sched;
+               }
+
+               pkt_len =
+               (length > max_rx_transfer_size) ? max_rx_transfer_size : length;
+
+               hw_desc = &curr_pd->hw_desc;
+               hw_desc->desc_info = (CPPI41_DESC_TYPE_HOST <<
+                                     CPPI41_DESC_TYPE_SHIFT);
+               hw_desc->orig_buf_ptr = rx_ch->start_addr + rx_ch->curr_offset;
+               hw_desc->orig_buf_len = pkt_len;
+
+               /* buf_len field of buffer descriptor updated by dma
+                * after reception of data is completed
+                */
+               hw_desc->buf_len = 0;
+
+               curr_pd->ch_num = rx_ch->ch_num;
+               curr_pd->ep_num = rx_ch->end_pt->epnum;
+
+               curr_pd->eop = (length -= pkt_len) ? 0 : 1;
+               rx_ch->curr_offset += pkt_len;
+
+               if (cppi_info->bd_intr_enb)
+                       hw_desc->orig_buf_len |= CPPI41_PKT_INTR_FLAG;
+
+               /* make sure descriptor details are updated to memory*/
+               dsb();
+
+               /*
+                * Push the free Rx packet descriptor
+                * to the free descriptor/buffer queue.
+                */
+               cppi41_queue_push(&rx_ch->queue_obj, curr_pd->dma_addr,
+                       USB_CPPI41_DESC_ALIGN, 0);
+       }
+
+sched:
+       /*
+        * HCD arranged ReqPkt for the first packet.
+        * We arrange it for all but the last one.
+        */
+       if (is_host_active(cppi->musb) && rx_ch->channel.actual_len &&
+               !rx_ch->inf_mode) {
+               void __iomem *epio = rx_ch->end_pt->regs;
+               u16 csr = musb_readw(epio, MUSB_RXCSR);
+               u8 curr_toggle = (csr & MUSB_RXCSR_H_DATATOGGLE) ? 1 : 0;
+
+               /* check if data toggle bit got out of sync */
+               if (curr_toggle == rx_ch->end_pt->prev_toggle) {
+                       dev_dbg(musb->controller,
+                               "Data toggle same as previous (=%d) on ep%d\n",
+                                       curr_toggle, rx_ch->end_pt->epnum);
+
+                       csr |= MUSB_RXCSR_H_DATATOGGLE |
+                                       MUSB_RXCSR_H_WR_DATATOGGLE;
+                       musb_writew(epio, MUSB_RXCSR, csr);
+                       rx_ch->end_pt->prev_toggle = !curr_toggle;
+               } else
+                       rx_ch->end_pt->prev_toggle = curr_toggle;
+
+               csr = musb_readw(epio, MUSB_RXCSR);
+               csr |= MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_H_WZC_BITS;
+               musb_writew(epio, MUSB_RXCSR, csr);
+       }
+
+       /* enable schedular if not enabled */
+       if (cppi_info->sched_tbl_ctrl &&
+               is_peripheral_active(cppi->musb) && (n_bd > 0))
+               cppi41_schedtbl_add_dma_ch(0, 0, 15 * id + rx_ch->ch_num, 0);
+       return 1;
+}
+
+/**
+ * cppi41_channel_program - program channel for data transfer
+ * @channel:   the channel
+ * @maxpacket: max packet size
+ * @mode:      for Rx, 1 unless the USB protocol driver promised to treat
+ *             all short reads as errors and kick in high level fault recovery;
+ *             for Tx, 0 unless the protocol driver _requires_ short-packet
+ *             termination mode
+ * @dma_addr:  DMA address of buffer
+ * @length:    length of buffer
+ *
+ * Context: controller IRQ-locked
+ */
+static int cppi41_channel_program(struct dma_channel *channel, u16 maxpacket,
+                                 u8 mode, dma_addr_t dma_addr, u32 length)
+{
+       struct cppi41_channel *cppi_ch;
+       unsigned queued;
+
+       cppi_ch = container_of(channel, struct cppi41_channel, channel);
+
+       switch (channel->status) {
+       case MUSB_DMA_STATUS_BUS_ABORT:
+       case MUSB_DMA_STATUS_CORE_ABORT:
+               /* Fault IRQ handler should have handled cleanup */
+               WARNING("%cx DMA%d not cleaned up after abort!\n",
+                       cppi_ch->transmit ? 'T' : 'R', cppi_ch->ch_num);
+               break;
+       case MUSB_DMA_STATUS_BUSY:
+               WARNING("Program active channel? %cx DMA%d\n",
+                       cppi_ch->transmit ? 'T' : 'R', cppi_ch->ch_num);
+               break;
+       case MUSB_DMA_STATUS_UNKNOWN:
+               WARNING("%cx DMA%d not allocated!\n",
+                   cppi_ch->transmit ? 'T' : 'R', cppi_ch->ch_num);
+               return 0;
+       case MUSB_DMA_STATUS_FREE:
+               break;
+       }
+
+       channel->status = MUSB_DMA_STATUS_BUSY;
+
+       /* Set the transfer parameters, then queue up the first segment */
+       cppi_ch->start_addr = dma_addr;
+       cppi_ch->curr_offset = 0;
+       cppi_ch->hb_mult = (maxpacket >> 11) & 0x03;
+       cppi_ch->pkt_size = maxpacket & ~(3 << 11);
+       cppi_ch->length = length;
+       cppi_ch->transfer_mode = mode;
+       cppi_ch->zlp_queued = 0;
+       cppi_ch->channel.actual_len = 0;
+
+       /* Tx or Rx channel? */
+       if (cppi_ch->transmit)
+               queued = cppi41_next_tx_segment(cppi_ch);
+       else
+               queued = cppi41_next_rx_segment(cppi_ch);
+
+       return  queued > 0;
+}
+
+static struct usb_pkt_desc *usb_get_pd_ptr(struct cppi41 *cppi,
+                                          unsigned long pd_addr)
+{
+       if (pd_addr >= cppi->pd_mem_phys && pd_addr < cppi->pd_mem_phys +
+           cppi->cppi_info->max_pkt_desc * USB_CPPI41_DESC_ALIGN)
+               return pd_addr - cppi->pd_mem_phys + cppi->pd_mem;
+       else
+               return NULL;
+}
+
+static int usb_check_teardown(struct cppi41_channel *cppi_ch,
+                             unsigned long pd_addr)
+{
+       u32 info;
+       struct cppi41 *cppi = cppi_ch->channel.private_data;
+       struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+       struct musb *musb = cppi->musb;
+       struct device *dev = musb->controller;
+
+       if (cppi41_get_teardown_info(pd_addr, &info)) {
+               dev_dbg(dev, "ERROR: not a teardown descriptor\n");
+               return 0;
+       }
+
+       if ((info & CPPI41_TEARDOWN_TX_RX_MASK) ==
+           (!cppi_ch->transmit << CPPI41_TEARDOWN_TX_RX_SHIFT) &&
+           (info & CPPI41_TEARDOWN_DMA_NUM_MASK) ==
+           (cppi_info->dma_block << CPPI41_TEARDOWN_DMA_NUM_SHIFT) &&
+           (info & CPPI41_TEARDOWN_CHAN_NUM_MASK) ==
+           (cppi_info->ep_dma_ch[cppi_ch->ch_num] <<
+            CPPI41_TEARDOWN_CHAN_NUM_SHIFT))
+               return 1;
+
+       dev_dbg(dev, "unexpected values in teardown descriptor\n");
+       return 0;
+}
+
+/*
+ * We can't handle the channel teardown via the default completion queue in
+ * context of the controller IRQ-locked, so we use the dedicated teardown
+ * completion queue which we can just poll for a teardown descriptor, not
+ * interfering with the Tx completion queue processing.
+ */
+static void usb_tx_ch_teardown(struct cppi41_channel *tx_ch)
+{
+       struct cppi41 *cppi = tx_ch->channel.private_data;
+       struct musb *musb = cppi->musb;
+       struct device *dev = musb->controller;
+       void __iomem *reg_base = musb->ctrl_base;
+       u32 td_reg, timeout = 0xfffff;
+       u8 ep_num = tx_ch->ch_num + 1;
+       unsigned long pd_addr;
+       struct cppi41_queue_obj tx_queue_obj;
+       struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+
+       /* Initiate teardown for Tx DMA channel */
+       cppi41_dma_ch_teardown(&tx_ch->dma_ch_obj);
+
+       /* Wait for a descriptor to be queued and pop it... */
+       do {
+               td_reg  = musb_readl(reg_base,
+                               cppi_info->wrp.teardown_reg);
+               td_reg |= USB_TX_TDOWN_MASK(ep_num);
+               musb_writel(reg_base, cppi_info->wrp.teardown_reg,
+                               td_reg);
+
+               pd_addr = cppi41_queue_pop(&cppi->queue_obj);
+       } while (!pd_addr && timeout--);
+
+       if (pd_addr) {
+               dev_dbg(dev, "desc (%lx) popped from td completion queue\n",
+                               pd_addr);
+
+               if (usb_check_teardown(tx_ch, pd_addr))
+                       dev_dbg(dev, "Teardown Desc (%lx) rcvd\n", pd_addr);
+               else
+                       dev_dbg(dev, "Invalid PD(%lx)popped from TdCompQueue\n",
+                               pd_addr);
+       } else {
+               if (timeout <= 0)
+                       pr_err("Teardown Desc not rcvd\n");
+       }
+
+       /* read the tx completion queue and remove
+        * completion bd if any
+        */
+       if (cppi41_queue_init(&tx_queue_obj, cppi_info->q_mgr,
+                             cppi_info->tx_comp_q[tx_ch->ch_num])) {
+               pr_err("failed to init tx completion queue %d",
+                       cppi_info->tx_comp_q[tx_ch->ch_num]);
+               return;
+       }
+
+       while ((pd_addr = cppi41_queue_pop(&tx_queue_obj)) != 0) {
+               struct usb_pkt_desc *curr_pd;
+
+               curr_pd = usb_get_pd_ptr(cppi, pd_addr);
+               if (curr_pd == NULL) {
+                       pr_err("Invalid PD popped from Tx completion queue\n");
+                       continue;
+               }
+
+               dev_dbg(dev, "Tx-PD(%p) popped from compl queue\n", curr_pd);
+               dev_dbg(dev, "ch(%d) epnum(%d) len(%d)\n", curr_pd->ch_num,
+                       curr_pd->ep_num, curr_pd->hw_desc.buf_len);
+
+               usb_put_free_pd(cppi, curr_pd);
+       }
+}
+
+/*
+ * For Rx DMA channels, the situation is more complex: there's only a single
+ * completion queue for all our needs, so we have to temporarily redirect the
+ * completed descriptors to our teardown completion queue, with a possibility
+ * of a completed packet landing there as well...
+ */
+static void usb_rx_ch_teardown(struct cppi41_channel *rx_ch)
+{
+       struct cppi41 *cppi = rx_ch->channel.private_data;
+       struct musb *musb = cppi->musb;
+       struct device *dev = musb->controller;
+       struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+       u32 timeout = 0xfffff, pd_addr;
+       struct cppi41_queue_obj rx_queue_obj;
+
+       cppi41_dma_ch_default_queue(&rx_ch->dma_ch_obj, 0, cppi->teardown_qnum);
+
+       /* Initiate teardown for Rx DMA channel */
+       cppi41_dma_ch_teardown(&rx_ch->dma_ch_obj);
+
+       do {
+               struct usb_pkt_desc *curr_pd;
+               unsigned long pd_addr;
+
+               /* Wait for a descriptor to be queued and pop it... */
+               do {
+                       pd_addr = cppi41_queue_pop(&cppi->queue_obj);
+               } while (!pd_addr && timeout--);
+
+               if (timeout <= 0 || !pd_addr) {
+                       pr_err("teardown Desc not found\n");
+                       break;
+               }
+
+               dev_dbg(dev, "desc (%08lx) popped from td completion queue\n",
+                       pd_addr);
+
+               /*
+                * We might have popped a completed Rx PD, so check if the
+                * physical address is within the PD region first.  If it's
+                * not the case, it must be a teardown descriptor...
+                * */
+               curr_pd = usb_get_pd_ptr(cppi, pd_addr);
+               if (curr_pd == NULL) {
+                       if (usb_check_teardown(rx_ch, pd_addr))
+                               break;
+                       continue;
+               }
+
+               /* Paranoia: check if PD is from the right channel... */
+               if (curr_pd->ch_num != rx_ch->ch_num) {
+                       pr_err("Unexpected channel %d in Rx PD\n",
+                           curr_pd->ch_num);
+                       continue;
+               }
+
+               /* Extract the buffer length from the completed PD */
+               rx_ch->channel.actual_len += curr_pd->hw_desc.buf_len;
+
+               /*
+                * Return Rx PDs to the software list --
+                * this is protected by critical section.
+                */
+               usb_put_free_pd(cppi, curr_pd);
+       } while (0);
+
+       /* read the rx completion queue and remove
+        * completion bd if any
+        */
+       if (cppi41_queue_init(&rx_queue_obj, cppi_info->q_mgr,
+                             cppi_info->rx_comp_q[rx_ch->ch_num])) {
+               pr_err("failed to init rx completion queue %d\n",
+                       cppi_info->rx_comp_q[rx_ch->ch_num]);
+               return;
+       }
+
+       while ((pd_addr = cppi41_queue_pop(&rx_queue_obj)) != 0) {
+               struct usb_pkt_desc *curr_pd;
+
+               curr_pd = usb_get_pd_ptr(cppi, pd_addr);
+               if (curr_pd == NULL) {
+                       pr_err("Invalid PD popped from Rx completion queue\n");
+                       continue;
+               }
+
+               dev_dbg(dev, "Rx-PD(%p) popped from compl queue\n", curr_pd);
+               dev_dbg(dev, "ch(%d)epnum(%d)len(%d)\n", curr_pd->ch_num,
+                       curr_pd->ep_num, curr_pd->hw_desc.buf_len);
+
+               usb_put_free_pd(cppi, curr_pd);
+       }
+
+       /* Now restore the default Rx completion queue... */
+       cppi41_dma_ch_default_queue(&rx_ch->dma_ch_obj, cppi_info->q_mgr,
+                                   cppi_info->rx_comp_q[rx_ch->ch_num]);
+}
+
+/*
+ * cppi41_channel_abort
+ *
+ * Context: controller IRQ-locked, endpoint selected.
+ */
+static int cppi41_channel_abort(struct dma_channel *channel)
+{
+       struct cppi41 *cppi;
+       struct cppi41_channel *cppi_ch;
+       struct usb_cppi41_info *cppi_info;
+       struct musb  *musb;
+       void __iomem *reg_base, *epio;
+       struct device *dev;
+       unsigned long pd_addr;
+       u32 csr, td_reg;
+       u8 ch_num, ep_num, i;
+
+       cppi_ch = container_of(channel, struct cppi41_channel, channel);
+       ch_num = cppi_ch->ch_num;
+       cppi = cppi_ch->channel.private_data;
+       cppi_info = cppi->cppi_info;
+       musb = cppi->musb;
+       dev = musb->controller;
+
+       if (cppi_info->tx_isoc_sched_enab &&
+               get_xfer_type(cppi_ch->end_pt, 1) == USB_ENDPOINT_XFER_ISOC) {
+               cppi_ch->xfer_state = USBREQ_DMA_INIT;
+               musb_disable_sof(musb);
+       }
+
+       switch (channel->status) {
+       case MUSB_DMA_STATUS_BUS_ABORT:
+       case MUSB_DMA_STATUS_CORE_ABORT:
+               /* From Rx or Tx fault IRQ handler */
+       case MUSB_DMA_STATUS_BUSY:
+               /* The hardware needs shutting down... */
+               dev_dbg(dev, "%s: DMA busy, status = %x\n",
+                       __func__, channel->status);
+               break;
+       case MUSB_DMA_STATUS_UNKNOWN:
+               dev_dbg(dev, "%cx DMA%d not allocated\n",
+                   cppi_ch->transmit ? 'T' : 'R', ch_num);
+               /* FALLTHROUGH */
+       case MUSB_DMA_STATUS_FREE:
+               return 0;
+       }
+
+       reg_base = musb->ctrl_base;
+       epio = cppi_ch->end_pt->regs;
+       ep_num = ch_num + 1;
+
+#ifdef DEBUG_CPPI_TD
+       dprintk("Before teardown:");
+       print_pd_list(cppi->pd_pool_head);
+#endif
+
+       if (cppi_ch->transmit) {
+               dev_dbg(dev, "Tx channel teardown, cppi_ch = %p\n", cppi_ch);
+
+               /* disable the DMAreq before teardown */
+               csr  = musb_readw(epio, MUSB_TXCSR);
+               csr &= ~MUSB_TXCSR_DMAENAB;
+               musb_writew(epio, MUSB_TXCSR, csr);
+
+               /* Tear down Tx DMA channel */
+               usb_tx_ch_teardown(cppi_ch);
+
+               /* Issue CPPI FIFO teardown for Tx channel */
+               td_reg  = musb_readl(reg_base,
+                               cppi_info->wrp.teardown_reg);
+               td_reg |= USB_TX_TDOWN_MASK(ep_num);
+               musb_writel(reg_base, cppi_info->wrp.teardown_reg,
+                       td_reg);
+
+               /* Flush FIFO of the endpoint */
+               for (i = 0; i < 2; ++i) {
+                       csr  = musb_readw(epio, MUSB_TXCSR);
+                       if (csr & MUSB_TXCSR_TXPKTRDY) {
+                               csr |= MUSB_TXCSR_FLUSHFIFO |
+                                       MUSB_TXCSR_H_WZC_BITS;
+                               musb_writew(epio, MUSB_TXCSR, csr);
+                       }
+               }
+               cppi_ch->tx_complete = 0;
+
+       } else { /* Rx */
+               dev_dbg(dev, "Rx channel teardown, cppi_ch = %p\n", cppi_ch);
+
+               cppi_ch->rx_complete = 0;
+               /* For host, ensure ReqPkt is never set again */
+               cppi41_autoreq_update(cppi_ch, USB_NO_AUTOREQ);
+
+               /* disable the DMAreq and remove reqpkt */
+               csr  = musb_readw(epio, MUSB_RXCSR);
+               dev_dbg(dev, "before rx-teardown: rxcsr %x rxcount %x\n", csr,
+                       musb_readw(epio, MUSB_RXCOUNT));
+
+               /* 250usec delay to drain to cppi dma pipe line */
+               udelay(250);
+
+               /* For host, clear (just) ReqPkt at end of current packet(s) */
+               if (is_host_active(cppi->musb))
+                       csr &= ~MUSB_RXCSR_H_REQPKT;
+
+               csr &= ~MUSB_RXCSR_DMAENAB;
+               musb_writew(epio, MUSB_RXCSR, csr);
+
+               /* wait till xdma completes last 64 bytes transfer from
+                * mentor fifo to internal cppi fifo and drain
+                * cppi dma pipe line
+                */
+               udelay(250);
+
+               /* Flush FIFO of the endpoint */
+               csr  = musb_readw(epio, MUSB_RXCSR);
+
+               if (csr & MUSB_RXCSR_RXPKTRDY)
+                       csr |= MUSB_RXCSR_FLUSHFIFO;
+
+               csr |= MUSB_RXCSR_H_WZC_BITS;
+               musb_writew(epio, MUSB_RXCSR, csr);
+               musb_writew(epio, MUSB_RXCSR, csr);
+               csr  = musb_readw(epio, MUSB_RXCSR);
+
+               /* Issue CPPI FIFO teardown for Rx channel */
+               td_reg  = musb_readl(reg_base,
+                               cppi_info->wrp.teardown_reg);
+               td_reg |= USB_RX_TDOWN_MASK(ep_num);
+               musb_writel(reg_base, cppi_info->wrp.teardown_reg,
+                       td_reg);
+
+               /* Tear down Rx DMA channel */
+               usb_rx_ch_teardown(cppi_ch);
+
+               /*
+                * NOTE: docs don't guarantee any of this works...  we expect
+                * that if the USB core stops telling the CPPI core to pull
+                * more data from it, then it'll be safe to flush current Rx
+                * DMA state iff any pending FIFO transfer is done.
+                */
+
+               /* For host, ensure ReqPkt is never set again */
+               cppi41_autoreq_update(cppi_ch, USB_NO_AUTOREQ);
+       }
+
+       /*
+        * There might be PDs in the Rx/Tx source queue that were not consumed
+        * by the DMA controller -- they need to be recycled properly.
+        */
+       while ((pd_addr = cppi41_queue_pop(&cppi_ch->queue_obj)) != 0) {
+               struct usb_pkt_desc *curr_pd;
+
+               curr_pd = usb_get_pd_ptr(cppi, pd_addr);
+               if (curr_pd == NULL) {
+                       pr_err("Invalid PD popped from source queue\n");
+                       continue;
+               }
+
+               /*
+                * Return Rx/Tx PDs to the software list --
+                * this is protected by critical section.
+                */
+               dev_dbg(dev, "Returning PD %p to the free PD list\n", curr_pd);
+               usb_put_free_pd(cppi, curr_pd);
+       }
+
+#ifdef DEBUG_CPPI_TD
+       dprintk(dev, "After teardown:");
+       print_pd_list(cppi->pd_pool_head);
+#endif
+
+       /* Re-enable the DMA channel */
+       cppi41_dma_ch_enable(&cppi_ch->dma_ch_obj);
+
+       channel->status = MUSB_DMA_STATUS_FREE;
+
+       return 0;
+}
+
+int cppi41_isoc_schedular(struct musb *musb)
+{
+       struct cppi41 *cppi;
+       struct cppi41_channel *tx_ch;
+       struct usb_cppi41_info *cppi_info;
+       int index;
+
+       cppi = container_of(musb->dma_controller, struct cppi41, controller);
+       cppi_info = cppi->cppi_info;
+       for (index = 0; index < cppi_info->max_dma_ch; index++) {
+               void __iomem *epio;
+               u16 csr;
+
+               tx_ch = &cppi->tx_cppi_ch[index];
+
+               if (tx_ch->xfer_state == USBREQ_DMA_INIT ||
+                       tx_ch->xfer_state == USBREQ_DMA_INPROGRESS)
+                       continue;
+
+               epio = tx_ch->end_pt->regs;
+               csr = musb_readw(epio, MUSB_TXCSR);
+
+               switch (tx_ch->xfer_state) {
+
+               case USBREQ_DMA_SHORTPKT_COMPLETE:
+                       if (cppi->sof_isoc_started) {
+                               dev_dbg(musb->controller,
+                               "Invalid state shortpkt complete occur ep%d\n",
+                                       index+1);
+                               break;
+                       }
+
+               case USBREQ_DMA_START:
+                       if (tx_ch->xfer_state == USBREQ_DMA_SHORTPKT_COMPLETE)
+                               tx_ch->xfer_state = USBREQ_DMA_COMPLETE;
+                       else
+                               tx_ch->xfer_state = USBREQ_DMA_INPROGRESS;
+
+                       cppi->sof_isoc_started = 1;
+                       musb_enable_tx_dma(tx_ch->end_pt);
+                       dev_dbg(musb->controller, "isoc_sched: DMA_INP ep%d\n",
+                                       index+1);
+                       break;
+
+               case USBREQ_DMA_COMPLETE:
+                       tx_ch->channel.status = MUSB_DMA_STATUS_FREE;
+                       tx_ch->xfer_state = USBREQ_DMA_INIT;
+                       dev_dbg(musb->controller,
+                               "isoc_sched: gvbk DMA_FREE  ep%d\n", index+1);
+                       musb_dma_completion(cppi->musb, index+1, 1);
+                       musb_disable_sof(cppi->musb);
+                       break;
+
+               default:
+                       dev_dbg(musb->controller,
+                               "isoc_sched: invalid state%d ep%d\n",
+                               tx_ch->xfer_state, index+1);
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL(cppi41_isoc_schedular);
+
+void txdma_completion_work(struct work_struct *data)
+{
+       struct cppi41 *cppi = container_of(data, struct cppi41, txdma_work);
+       struct cppi41_channel *tx_ch;
+       struct musb *musb = cppi->musb;
+       struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+       unsigned index;
+       u8 resched = 0;
+       unsigned long flags = 0;
+
+       while (1) {
+               for (index = 0; index < cppi_info->max_dma_ch; index++) {
+                       void __iomem *epio;
+                       u16 csr, len = 0;
+
+                       tx_ch = &cppi->tx_cppi_ch[index];
+                       if (tx_ch->tx_complete) {
+                               /* Sometimes a EP can unregister from a DMA
+                                * channel while the data is still in the FIFO.
+                                * Probable reason a proper abort was not
+                                * called before taking such a step.
+                                * Protect against such cases.
+                                */
+                               if (!tx_ch->end_pt) {
+                                       tx_ch->tx_complete = 0;
+                                       tx_ch->count = 0;
+                                       continue;
+                               }
+
+                               epio = tx_ch->end_pt->regs;
+                               csr = musb_readw(epio, MUSB_TXCSR);
+
+                               if (csr & (MUSB_TXCSR_TXPKTRDY |
+                                       MUSB_TXCSR_FIFONOTEMPTY)) {
+                                       resched = 1;
+                               } else {
+                                       if (tx_ch->count > 0) {
+                                               tx_ch->count--;
+                                               resched = 1;
+                                               continue;
+                                       }
+
+                                       len = tx_ch->length -
+                                               tx_ch->curr_offset;
+                                       if (len > 0) {
+                                               tx_ch->tx_complete = 0;
+                                               cppi41_next_tx_segment(tx_ch);
+                                               continue;
+                                       }
+
+                                       tx_ch->channel.status =
+                                               MUSB_DMA_STATUS_FREE;
+                                       tx_ch->tx_complete = 0;
+                                       spin_lock_irqsave(&musb->lock, flags);
+                                       musb_dma_completion(musb, index+1, 1);
+                                       spin_unlock_irqrestore(&musb->lock,
+                                               flags);
+                               }
+                       }
+
+                       if (!resched)
+                               cond_resched();
+               }
+
+               if (resched) {
+                       resched = 0;
+                       cond_resched();
+               } else {
+                       return ;
+               }
+       }
+
+}
+
+/**
+ * cppi41_dma_controller_create -
+ * instantiate an object representing DMA controller.
+ */
+struct dma_controller *dma_controller_create(struct musb  *musb,
+               void __iomem *mregs)
+{
+       struct cppi41 *cppi;
+       struct cppi41_channel *cppi_ch;
+       struct platform_device *pdev = to_platform_device(musb->controller);
+       int id = pdev->id, max_dma_ch;
+
+       cppi = kzalloc(sizeof(*cppi), GFP_KERNEL);
+       if (!cppi)
+               goto err;
+
+       max_dma_ch = usb_cppi41_info[id].max_dma_ch;
+       cppi_ch = kzalloc(sizeof(*cppi_ch) * max_dma_ch, GFP_KERNEL);
+       if (!cppi_ch)
+               goto err;
+       cppi->tx_cppi_ch = cppi_ch;
+
+       cppi_ch = kzalloc(sizeof(*cppi_ch) * max_dma_ch, GFP_KERNEL);
+       if (!cppi_ch)
+               goto err;
+       cppi->rx_cppi_ch = cppi_ch;
+
+       /* Initialize the CPPI 4.1 DMA controller structure */
+       cppi->musb  = musb;
+       cppi->controller.start = dma_controller_start;
+       cppi->controller.stop  = dma_controller_stop;
+       cppi->controller.channel_alloc = cppi41_channel_alloc;
+       cppi->controller.channel_release = cppi41_channel_release;
+       cppi->controller.channel_program = cppi41_channel_program;
+       cppi->controller.channel_abort = cppi41_channel_abort;
+       cppi->cppi_info = (struct usb_cppi41_info *)&usb_cppi41_info[id];
+       INIT_WORK(&cppi->txdma_work, txdma_completion_work);
+       INIT_WORK(&cppi->rxdma_work, rxdma_completion_work);
+       if (musb->ops->sof_handler)
+               musb->tx_isoc_sched_enable = 1;
+
+       return &cppi->controller;
+err:
+       dev_err(musb->controller, "memory allocation failed\n");
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(dma_controller_create);
+
+/**
+ * dma_controller_destroy -
+ * destroy a previously instantiated DMA controller
+ * @controller: the controller
+ */
+void dma_controller_destroy(struct dma_controller *controller)
+{
+       struct cppi41 *cppi;
+
+       cppi = container_of(controller, struct cppi41, controller);
+
+       /* Free the CPPI object */
+       kfree(cppi->tx_cppi_ch);
+       kfree(cppi->rx_cppi_ch);
+       kfree(cppi);
+}
+EXPORT_SYMBOL_GPL(dma_controller_destroy);
+
+static void usb_process_tx_queue(struct cppi41 *cppi, unsigned index)
+{
+       struct cppi41_queue_obj tx_queue_obj;
+       unsigned long pd_addr;
+       struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+       struct musb *musb = cppi->musb;
+       struct device *dev = musb->controller;
+
+       if (cppi41_queue_init(&tx_queue_obj, cppi_info->q_mgr,
+                             cppi_info->tx_comp_q[index])) {
+               pr_err("failed to init tx compl queue %d",
+                   cppi_info->tx_comp_q[index]);
+               return;
+       }
+
+       while ((pd_addr = cppi41_queue_pop(&tx_queue_obj)) != 0) {
+               struct usb_pkt_desc *curr_pd;
+               struct cppi41_channel *tx_ch;
+               u8 ch_num, ep_num, is_isoc;
+               u32 length;
+               u32 sched_work = 0;
+
+               curr_pd = usb_get_pd_ptr(cppi, pd_addr);
+               if (curr_pd == NULL) {
+                       pr_err("Invalid PD popped from Tx completion queue\n");
+                       continue;
+               }
+
+               /* Extract the data from received packet descriptor */
+               ch_num = curr_pd->ch_num;
+               ep_num = curr_pd->ep_num;
+               length = curr_pd->hw_desc.buf_len;
+
+               tx_ch = &cppi->tx_cppi_ch[ch_num];
+               tx_ch->channel.actual_len += length;
+
+               dev_dbg(dev, "Tx complete: dma channel(%d) ep%d len %d\n",
+                       ch_num, ep_num, length);
+
+               /*
+                * Return Tx PD to the software list --
+                * this is protected by critical section
+                */
+               usb_put_free_pd(cppi, curr_pd);
+
+               is_isoc = get_xfer_type(tx_ch->end_pt, 1) ==
+                               USB_ENDPOINT_XFER_ISOC;
+               if (is_isoc && cppi_info->tx_isoc_sched_enab) {
+                       if (tx_ch->xfer_state == USBREQ_DMA_INPROGRESS)
+                               tx_ch->xfer_state = USBREQ_DMA_COMPLETE;
+                       else
+                               tx_ch->xfer_state =
+                                       USBREQ_DMA_SHORTPKT_COMPLETE;
+                       dev_dbg(musb->controller, "DMAIsr isoch: state %d ep%d len %d\n",
+                                       tx_ch->xfer_state, ep_num, length);
+               } else if ((tx_ch->curr_offset < tx_ch->length) ||
+                   (tx_ch->transfer_mode && !tx_ch->zlp_queued)) {
+                       sched_work = 1;
+               } else if (tx_ch->channel.actual_len >= tx_ch->length) {
+                       void __iomem *epio;
+                       int residue, musb_completion = 0;
+                       u16 csr;
+
+                       /*
+                        * We get Tx DMA completion interrupt even when
+                        * data is still in FIFO and not moved out to
+                        * USB bus. As we program the next request we
+                        * flush out and old data in FIFO which affects
+                        * USB functionality. So far, we have obsered
+                        * failure with iperf.
+                        */
+
+                       /* wait for tx fifo empty completion interrupt
+                        * if enabled other wise use the workthread
+                        * to poll fifo empty status
+                        */
+                       epio = tx_ch->end_pt->regs;
+                       csr = musb_readw(epio, MUSB_TXCSR);
+
+                       residue = tx_ch->channel.actual_len %
+                                       tx_ch->pkt_size;
+
+                       if (is_peripheral_active(musb) &&
+                               csr & MUSB_TXCSR_TXPKTRDY) {
+                               musb_completion = 1;
+                       } else if (is_host_active(musb) &&
+                               tx_ch->pkt_size > 128 && !residue) {
+                               musb_completion = 1;
+                       } else
+                               sched_work = 1;
+
+                       if (musb_completion) {
+                               dev_dbg(dev, "dma complete ep%d csr %x\n",
+                                       ep_num, csr);
+                               tx_ch->channel.status = MUSB_DMA_STATUS_FREE;
+                               musb_dma_completion(cppi->musb, ep_num, 1);
+                       }
+               }
+
+               if (sched_work) {
+                       sched_work = 0;
+                       tx_ch->tx_complete = 1;
+                       tx_ch->count = 1;
+                       schedule_work(&cppi->txdma_work);
+               }
+       }
+}
+
+static void usb_process_rx_bd(struct cppi41 *cppi,
+               struct usb_pkt_desc *curr_pd)
+{
+       struct cppi41_channel *rx_ch;
+       struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+       u8 ch_num, ep_num;
+       struct musb *musb = cppi->musb;
+       struct device *dev = musb->controller;
+       struct platform_device *pdev = to_platform_device(dev);
+       int id = pdev->id;
+       u32 length = 0, orig_buf_len;
+
+       /* Extract the data from received packet descriptor */
+       length = curr_pd->hw_desc.desc_info & CPPI41_PKT_LEN_MASK;
+       ch_num = curr_pd->ch_num;
+       ep_num = curr_pd->ep_num;
+
+       /* the cppi41 dma will set received byte length as 1 when
+        * zero length packet is received, fix this dummy byte by
+        * setting acutal length received as zero
+        */
+       if (curr_pd->hw_desc.pkt_info & CPPI41_ZLP)
+               length = 0;
+
+       rx_ch = &cppi->rx_cppi_ch[ch_num];
+       dev_dbg(dev, "Rx complete: dma channel(%d) ep%d len %d\n",
+               ch_num, ep_num, length);
+
+       rx_ch->channel.actual_len += length;
+
+       if (curr_pd->eop) {
+               curr_pd->eop = 0;
+               /* disable the rx dma schedular */
+               if (cppi_info->sched_tbl_ctrl && !cppi_info->rx_inf_mode
+                       && is_peripheral_active(cppi->musb)) {
+                       cppi41_schedtbl_remove_dma_ch(0, 0,
+                               ch_num + 15 * id, 0);
+               }
+       }
+
+       /*
+        * Return Rx PD to the software list --
+        * this is protected by critical section
+        */
+       usb_put_free_pd(cppi, curr_pd);
+
+       orig_buf_len = curr_pd->hw_desc.orig_buf_len;
+       if (cppi_info->bd_intr_enb)
+               orig_buf_len &= ~CPPI41_PKT_INTR_FLAG;
+
+       dev_dbg(dev, "curr_pd=%p, len=%d, origlen=%d,rxch(alen/len)=%d/%d\n",
+               curr_pd, length, orig_buf_len, rx_ch->channel.actual_len,
+               rx_ch->length);
+
+       if (rx_ch->channel.actual_len >= rx_ch->length ||
+                    length < orig_buf_len) {
+
+               struct musb_hw_ep *ep;
+               u8 isoc, next_seg = 0;
+
+               /* Workaround for early rx completion of
+                * cppi41 dma in Generic RNDIS mode for ti81xx
+                * the Genric rndis mode does not work reliably
+                * on am33xx platform, so use only transparent mode
+                */
+               if (is_host_active(cppi->musb)) {
+                       u32 pkt_size = rx_ch->pkt_size;
+                       ep = cppi->musb->endpoints + ep_num;
+                       isoc = musb_readb(ep->regs, MUSB_RXTYPE);
+                       isoc = (isoc >> 4) & 0x1;
+
+                       if (!isoc
+                               && (rx_ch->dma_mode == USB_GENERIC_RNDIS_MODE)
+                               && (rx_ch->channel.actual_len < rx_ch->length)
+                               && !(rx_ch->channel.actual_len % pkt_size))
+                                       next_seg = 1;
+               }
+               if (next_seg) {
+                       rx_ch->curr_offset = rx_ch->channel.actual_len;
+                       cppi41_next_rx_segment(rx_ch);
+               } else {
+                       rx_ch->channel.status = MUSB_DMA_STATUS_FREE;
+
+                       if (rx_ch->inf_mode) {
+                               cppi41_rx_ch_set_maxbufcnt(
+                               &rx_ch->dma_ch_obj, 0);
+                               rx_ch->inf_mode = 0;
+                       }
+
+                       /* Rx completion routine callback */
+                       musb_dma_completion(cppi->musb, ep_num, 0);
+               }
+       } else {
+                       if ((rx_ch->length - rx_ch->curr_offset) > 0)
+                               cppi41_next_rx_segment(rx_ch);
+       }
+}
+
+static void rxdma_completion_work(struct work_struct *data)
+{
+       struct cppi41 *cppi = container_of(data, struct cppi41, rxdma_work);
+       struct musb *musb = cppi->musb;
+       struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+       unsigned index;
+       u8 resched = 0;
+       unsigned long flags, length;
+       struct cppi41_channel *rx_ch;
+
+       while (1) {
+               for (index = 0; index < cppi_info->max_dma_ch; index++) {
+                       rx_ch = &cppi->rx_cppi_ch[index];
+                       if (rx_ch->rx_complete) {
+                               /* Sometimes a EP can unregister from a DMA
+                                * channel while the data is still in the FIFO.
+                                * Probable reason a proper abort was not
+                                * called before taking such a step.
+                                */
+                               if (!rx_ch->curr_pd) {
+                                       pr_err("invalid curr_pd chnum%d\n",
+                                               index);
+                                       continue;
+                               }
+
+                               length = rx_ch->curr_pd->hw_desc.desc_info &
+                                               CPPI41_PKT_LEN_MASK;
+                               if (length == 0) {
+                                       resched = 1;
+                                       continue;
+                               }
+                               spin_lock_irqsave(&musb->lock, flags);
+                               usb_process_rx_bd(cppi, rx_ch->curr_pd);
+                               rx_ch->rx_complete = 0;
+                               rx_ch->curr_pd = 0;
+                               spin_unlock_irqrestore(&musb->lock, flags);
+                       }
+               }
+
+               if (resched) {
+                       resched = 0;
+                       cond_resched();
+               } else
+                       return ;
+       }
+}
+
+static void usb_process_rx_queue(struct cppi41 *cppi, unsigned index)
+{
+       struct cppi41_queue_obj rx_queue_obj;
+       unsigned long pd_addr;
+       struct usb_cppi41_info *cppi_info = cppi->cppi_info;
+       struct musb *musb = cppi->musb;
+       struct device *dev = musb->controller;
+
+       if (cppi41_queue_init(&rx_queue_obj, cppi_info->q_mgr,
+                             cppi_info->rx_comp_q[index])) {
+               dev_err(dev, "fail to init rx completion queue %d\n",
+                       cppi_info->rx_comp_q[index]);
+               return;
+       }
+
+       while ((pd_addr = cppi41_queue_pop(&rx_queue_obj)) != 0) {
+               struct usb_pkt_desc *curr_pd;
+               struct cppi41_channel *rx_ch;
+               u8 ch_num, ep_num;
+               u32 length = 0, timeout = 50;
+
+               curr_pd = usb_get_pd_ptr(cppi, pd_addr);
+               if (curr_pd == NULL) {
+                       pr_err("Invalid PD popped from Rx completion queue\n");
+                       continue;
+               }
+
+               /* This delay is required to overcome the dma race condition
+                * where software reads buffer descriptor before being updated
+                * by dma as buffer descriptor's writes by dma still pending in
+                * interconnect bridge.
+                */
+               while (timeout--) {
+                       length = curr_pd->hw_desc.desc_info &
+                                       CPPI41_PKT_LEN_MASK;
+                       if (length != 0)
+                               break;
+                       udelay(1);
+               }
+
+               /* Extract the data from received packet descriptor */
+               ch_num = curr_pd->ch_num;
+               ep_num = curr_pd->ep_num;
+
+               rx_ch = &cppi->rx_cppi_ch[ch_num];
+
+               if (length == 0) {
+                       dev_dbg(dev, "!Race: delayed rxBD update ch%d ep%d\n",
+                               ch_num, ep_num);
+                       rx_ch->rx_complete = 1;
+                       rx_ch->curr_pd = curr_pd;
+                       schedule_work(&cppi->rxdma_work);
+                       continue;
+               }
+               usb_process_rx_bd(cppi, curr_pd);
+       }
+}
+
+/*
+ * cppi41_completion - handle interrupts from the Tx/Rx completion queues
+ *
+ * NOTE: since we have to manually prod the Rx process in the transparent mode,
+ *      we certainly want to handle the Rx queues first.
+ */
+void cppi41_completion(struct musb *musb, u32 rx, u32 tx)
+{
+       struct cppi41 *cppi;
+       unsigned index;
+
+       cppi = container_of(musb->dma_controller, struct cppi41, controller);
+
+       /* Process packet descriptors from the Rx queues */
+       for (index = 0; rx != 0; rx >>= 1, index++)
+               if (rx & 1)
+                       usb_process_rx_queue(cppi, index);
+
+       /* Process packet descriptors from the Tx completion queues */
+       for (index = 0; tx != 0; tx >>= 1, index++)
+               if (tx & 1)
+                       usb_process_tx_queue(cppi, index);
+}
+EXPORT_SYMBOL_GPL(cppi41_completion);
+
+MODULE_DESCRIPTION("CPPI4.1 dma controller driver for musb");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/musb/cppi41_dma.h b/drivers/usb/musb/cppi41_dma.h
new file mode 100644 (file)
index 0000000..a5eaac9
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2005-2006 by Texas Instruments
+ * Copyright (c) 2008, MontaVista Software, Inc. <source@mvista.com>
+ *
+ * This program is free software; you can distribute 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 it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _CPPI41_DMA_H_
+#define _CPPI41_DMA_H_
+
+#define USB_GENERIC_RNDIS_EP_SIZE_REG(n) (0x80 + (((n) - 1) << 2))
+
+/* Mode register bits */
+#define USB_MODE_SHIFT(n)      ((((n) - 1) << 1))
+#define USB_MODE_MASK(n)       (3 << USB_MODE_SHIFT(n))
+#define USB_RX_MODE_SHIFT(n)   USB_MODE_SHIFT(n)
+#define USB_TX_MODE_SHIFT(n)   USB_MODE_SHIFT(n)
+#define USB_RX_MODE_MASK(n)    USB_MODE_MASK(n)
+#define USB_TX_MODE_MASK(n)    USB_MODE_MASK(n)
+#define USB_TRANSPARENT_MODE   0
+#define USB_RNDIS_MODE         1
+#define USB_CDC_MODE           2
+#define USB_GENERIC_RNDIS_MODE 3
+#define USB_INFINITE_DMAMODE   4
+#define MAX_GRNDIS_PKTSIZE     (64 * 1024)
+
+/* AutoReq register bits */
+#define USB_RX_AUTOREQ_SHIFT(n) (((n) - 1) << 1)
+#define USB_RX_AUTOREQ_MASK(n) (3 << USB_RX_AUTOREQ_SHIFT(n))
+#define USB_NO_AUTOREQ         0
+#define USB_AUTOREQ_ALL_BUT_EOP 1
+#define USB_AUTOREQ_ALWAYS     3
+
+/* Teardown register bits */
+#define USB_TX_TDOWN_SHIFT(n)  (16 + (n))
+#define USB_TX_TDOWN_MASK(n)   (1 << USB_TX_TDOWN_SHIFT(n))
+#define USB_RX_TDOWN_SHIFT(n)  (n)
+#define USB_RX_TDOWN_MASK(n)   (1 << USB_RX_TDOWN_SHIFT(n))
+
+/* CPPI 4.1 queue manager registers */
+#define QMGR_PEND0_REG         0x4090
+#define QMGR_PEND1_REG         0x4094
+#define QMGR_PEND2_REG         0x4098
+
+#define QMGR_RGN_OFFS          0x4000
+#define QMRG_DESCRGN_OFFS      0x5000
+#define QMGR_REG_OFFS          0x6000
+#define QMGR_STAT_OFFS         0x7000
+#define DMA_GLBCTRL_OFFS       0x2000
+#define DMA_CHCTRL_OFFS                0x2800
+#define DMA_SCHED_OFFS         0x3000
+#define DMA_SCHEDTBL_OFFS      0x3800
+
+#define USBSS_INTR_RX_STARV    0x00000001
+#define USBSS_INTR_PD_CMPL     0x00000004
+#define USBSS_INTR_TX_CMPL     0x00000500
+#define USBSS_INTR_RX_CMPL     0x00000A00
+#define USBSS_INTR_FLAGS       (USBSS_INTR_PD_CMPL | USBSS_INTR_TX_CMPL \
+                                       | USBSS_INTR_RX_CMPL)
+
+struct cppi41_wrapper_regs {
+       /* xmda rndis size register */
+       u16     autoreq_reg;
+       u16     teardown_reg;
+       u16     tx_mode_reg;
+       u16     rx_mode_reg;
+};
+
+/**
+ * struct usb_cppi41_info - CPPI 4.1 USB implementation details
+ * @dma_block: DMA block number
+ * @ep_dma_ch: DMA channel numbers used for EPs 1 .. Max_EP
+ * @q_mgr:     queue manager number
+ * @num_tx_comp_q: number of the Tx completion queues
+ * @num_rx_comp_q: number of the Rx queues
+ * @tx_comp_q: pointer to the list of the Tx completion queue numbers
+ * @rx_comp_q: pointer to the list of the Rx queue numbers
+ */
+struct usb_cppi41_info {
+       u8 dma_block;
+       u8 *ep_dma_ch;
+       u8 q_mgr;
+       u8 num_tx_comp_q;
+       u8 num_rx_comp_q;
+       u8 max_dma_ch;
+       u32 max_pkt_desc;
+       u16 *tx_comp_q;
+       u16 *rx_comp_q;
+       u8 bd_intr_enb;
+       u8 rx_dma_mode;
+       u8 rx_inf_mode;
+       u8 tx_isoc_sched_enab;
+       u8 sched_tbl_ctrl;
+       u32 version;
+       struct cppi41_wrapper_regs wrp;
+};
+
+extern struct usb_cppi41_info usb_cppi41_info[];
+
+/**
+ * cppi41_completion - Tx/Rx completion queue interrupt handling hook
+ * @musb:      the controller
+ * @rx:        bitmask having bit N set if Rx queue N is not empty
+ * @tx:        bitmask having bit N set if Tx completion queue N is not empty
+ */
+void cppi41_completion(struct musb *musb, u32 rx, u32 tx);
+int cppi41_isoc_schedular(struct musb *musb);
+#endif /* _CPPI41_DMA_H_ */
index 60b41cc28da446558690f0e37bff33111823f281..9ad3c39c79496bc14a2f1bb2bc323b83f6d2be12 100644 (file)
@@ -557,6 +557,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
        }
 
        if (int_usb & MUSB_INTR_VBUSERROR) {
+               struct usb_hcd *hcd = musb_to_hcd(musb);
                int     ignore = 0;
 
                /* During connection as an A-Device, we may see a short
@@ -596,6 +597,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                                musb->port1_status |=
                                          USB_PORT_STAT_OVERCURRENT
                                        | (USB_PORT_STAT_C_OVERCURRENT << 16);
+
+                               if (hcd->status_urb)
+                                       usb_hcd_poll_rh_status(hcd);
+                               else
+                                       usb_hcd_resume_root_hub(hcd);
+
                        }
                        break;
                default:
@@ -783,6 +790,10 @@ b_host:
                        /* FALLTHROUGH */
                case OTG_STATE_B_PERIPHERAL:
                case OTG_STATE_B_IDLE:
+                       pr_info("musb %s gadget disconnected.\n",
+                               musb->gadget_driver
+                               ? musb->gadget_driver->driver.name
+                               : "");
                        musb_g_disconnect(musb);
                        break;
                default:
@@ -899,6 +910,11 @@ b_host:
                }               /* end of for loop */
        }
 #endif
+       if (int_usb & MUSB_INTR_SOF) {
+               if (musb->ops->sof_handler)
+                       musb->ops->sof_handler(musb);
+               handled = IRQ_HANDLED;
+       }
 
        schedule_work(&musb->irq_work);
 
@@ -937,15 +953,19 @@ void musb_start(struct musb *musb)
        devctl = musb_readb(regs, MUSB_DEVCTL);
        devctl &= ~MUSB_DEVCTL_SESSION;
 
-       /* session started after:
-        * (a) ID-grounded irq, host mode;
-        * (b) vbus present/connect IRQ, peripheral mode;
-        * (c) peripheral initiates, using SRP
-        */
-       if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
-               musb->is_active = 1;
-       else
+       if (musb->port_mode == MUSB_PORT_MODE_HOST) {
                devctl |= MUSB_DEVCTL_SESSION;
+       } else {
+               /* session started after:
+                * (a) ID-grounded irq, host mode;
+                * (b) vbus present/connect IRQ, peripheral mode;
+                * (c) peripheral initiates, using SRP
+                */
+               if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
+                       musb->is_active = 1;
+               else
+                       devctl |= MUSB_DEVCTL_SESSION;
+       }
 
        musb_platform_enable(musb);
        musb_writeb(regs, MUSB_DEVCTL, devctl);
@@ -1007,6 +1027,9 @@ static void musb_shutdown(struct platform_device *pdev)
 
        musb_gadget_cleanup(musb);
 
+       if (musb->port_mode == MUSB_PORT_MODE_HOST)
+               usb_remove_hcd(musb_to_hcd(musb));
+
        spin_lock_irqsave(&musb->lock, flags);
        musb_platform_disable(musb);
        musb_generic_disable(musb);
@@ -1019,7 +1042,6 @@ static void musb_shutdown(struct platform_device *pdev)
        /* FIXME power down */
 }
 
-
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -1499,6 +1521,13 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
        return 0;
 }
 
+void musb_restart(struct musb *musb)
+{
+       ep_config_from_table(musb);
+       musb_start(musb);
+}
+EXPORT_SYMBOL_GPL(musb_restart);
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -1592,7 +1621,7 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
 
        if (!epnum) {
 #ifndef CONFIG_USB_TUSB_OMAP_DMA
-               if (!is_cppi_enabled()) {
+               if (!is_cppi_enabled() && !is_cppi41_enabled()) {
                        /* endpoint 0 */
                        if (devctl & MUSB_DEVCTL_HM)
                                musb_h_ep0_irq(musb);
@@ -1776,6 +1805,7 @@ static struct musb *allocate_instance(struct device *dev,
        INIT_LIST_HEAD(&musb->control);
        INIT_LIST_HEAD(&musb->in_bulk);
        INIT_LIST_HEAD(&musb->out_bulk);
+       INIT_LIST_HEAD(&musb->gb_list);
 
        hcd->uses_new_polling = 1;
        hcd->has_tt = 1;
@@ -1821,6 +1851,9 @@ static void musb_free(struct musb *musb)
 
                (void) c->stop(c);
                dma_controller_destroy(c);
+
+               if (musb->gb_queue)
+                       destroy_workqueue(musb->gb_queue);
        }
 
        usb_put_hcd(musb_to_hcd(musb));
@@ -1863,9 +1896,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
        pm_runtime_enable(musb->controller);
 
        spin_lock_init(&musb->lock);
+       spin_lock_init(&musb->gb_lock);
        musb->board_set_power = plat->set_power;
        musb->min_power = plat->min_power;
        musb->ops = plat->platform_ops;
+       musb->port_mode = plat->mode;
 
        /* The musb_platform_init() call:
         *   - adjusts musb->mregs
@@ -1936,15 +1971,16 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
        /* FIXME this handles wakeup irqs wrong */
        if (enable_irq_wake(nIrq) == 0) {
                musb->irq_wake = 1;
-               device_init_wakeup(dev, 1);
        } else {
                musb->irq_wake = 0;
        }
 
+       /* initiliaze device wakeup for musb */
+       device_init_wakeup(dev, 1);
+
        /* host side needs more setup */
        hcd = musb_to_hcd(musb);
        otg_set_host(musb->xceiv->otg, &hcd->self);
-       hcd->self.otg_port = 1;
        musb->xceiv->otg->host = &hcd->self;
        hcd->power_budget = 2 * (plat->power ? : 250);
 
@@ -1960,9 +1996,26 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
        musb->xceiv->state = OTG_STATE_B_IDLE;
 
        status = musb_gadget_setup(musb);
+       device_set_wakeup_enable(dev, 0);
 
        if (status < 0)
                goto fail3;
+       /*
+        * If the port is configured to 'host' mode only,
+        * start the HCD here.
+        */
+       if (musb->port_mode == MUSB_PORT_MODE_HOST) {
+               MUSB_HST_MODE(musb);
+               musb->xceiv->otg->default_a = 1;
+               musb->xceiv->state = OTG_STATE_A_IDLE;
+
+               status = usb_add_hcd(hcd, 0, 0);
+               if (status < 0)
+                       goto fail3;
+
+               hcd->self.uses_pio_for_control = 1;
+       } else
+               hcd->self.otg_port = 1;
 
        status = musb_init_debugfs(musb);
        if (status < 0)
@@ -1976,13 +2029,24 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
 
        pm_runtime_put(musb->controller);
 
+       musb->gb_queue = create_singlethread_workqueue(dev_name(dev));
+       if (musb->gb_queue == NULL)
+               goto fail6;
+       /* Init giveback workqueue */
+       INIT_WORK(&musb->gb_work, musb_gb_work);
+
        return 0;
 
+fail6:
+       destroy_workqueue(musb->gb_queue);
+
 fail5:
        musb_exit_debugfs(musb);
 
 fail4:
        musb_gadget_cleanup(musb);
+       if (musb->port_mode == MUSB_PORT_MODE_HOST)
+               usb_remove_hcd(hcd);
 
 fail3:
        pm_runtime_put_sync(musb->controller);
@@ -2053,14 +2117,14 @@ static int musb_remove(struct platform_device *pdev)
        iounmap(ctrl_base);
        device_init_wakeup(dev, 0);
 #ifndef CONFIG_MUSB_PIO_ONLY
-       dma_set_mask(dev, *dev->parent->dma_mask);
+       dma_set_mask(dev, *dev->dma_mask);
 #endif
        return 0;
 }
 
 #ifdef CONFIG_PM
 
-static void musb_save_context(struct musb *musb)
+void musb_save_context(struct musb *musb)
 {
        int i;
        void __iomem *musb_base = musb->mregs;
@@ -2130,8 +2194,9 @@ static void musb_save_context(struct musb *musb)
                        musb_read_rxhubport(musb_base, i);
        }
 }
+EXPORT_SYMBOL(musb_save_context);
 
-static void musb_restore_context(struct musb *musb)
+void musb_restore_context(struct musb *musb)
 {
        int i;
        void __iomem *musb_base = musb->mregs;
@@ -2207,18 +2272,26 @@ static void musb_restore_context(struct musb *musb)
        }
        musb_writeb(musb_base, MUSB_INDEX, musb->context.index);
 }
+EXPORT_SYMBOL(musb_restore_context);
 
 static int musb_suspend(struct device *dev)
 {
        struct musb     *musb = dev_to_musb(dev);
        unsigned long   flags;
+       u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+       int ret = 0;
 
        spin_lock_irqsave(&musb->lock, flags);
 
        if (is_peripheral_active(musb)) {
                /* FIXME force disconnect unless we know USB will wake
                 * the system up quickly enough to respond ...
+                * Don't allow system suspend while peripheral mode
+                * is actve and cable is connected to host.
                 */
+               if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS
+                       && (devctl & MUSB_DEVCTL_BDEVICE))
+                       ret = -EBUSY;
        } else if (is_host_active(musb)) {
                /* we know all the children are suspended; sometimes
                 * they will even be wakeup-enabled.
@@ -2226,7 +2299,7 @@ static int musb_suspend(struct device *dev)
        }
 
        spin_unlock_irqrestore(&musb->lock, flags);
-       return 0;
+       return ret;
 }
 
 static int musb_resume_noirq(struct device *dev)
index 7fb4819a6f115f54d097ef09eb131fb820994d05..bd516f406b1a0503aa282e7d9d316d4a8d57d3ba 100644 (file)
@@ -51,6 +51,10 @@ struct musb;
 struct musb_hw_ep;
 struct musb_ep;
 
+#define MUSB_PORT_MODE_HOST    1
+#define MUSB_PORT_MODE_DEV     2
+#define MUSB_PORT_MODE_OTG     3
+
 /* Helper defines for struct musb->hwvers */
 #define MUSB_HWVERS_MAJOR(x)   ((x >> 10) & 0x1f)
 #define MUSB_HWVERS_MINOR(x)   (x & 0x3ff)
@@ -213,6 +217,9 @@ struct musb_platform_ops {
        int     (*adjust_channel_params)(struct dma_channel *channel,
                                u16 packet_sz, u8 *mode,
                                dma_addr_t *dma_addr, u32 *len);
+       int     (*enable_sof)(struct musb *musb);
+       int     (*disable_sof)(struct musb *musb);
+       int     (*sof_handler)(struct musb *musb);
 };
 
 /*
@@ -263,6 +270,9 @@ struct musb_hw_ep {
        /* peripheral side */
        struct musb_ep          ep_in;                  /* TX */
        struct musb_ep          ep_out;                 /* RX */
+
+       /* save rx toggle */
+       u8                      prev_toggle;
 };
 
 static inline struct musb_request *next_in_request(struct musb_hw_ep *hw_ep)
@@ -310,6 +320,7 @@ struct musb {
 
        irqreturn_t             (*isr)(int, void *);
        struct work_struct      irq_work;
+       struct work_struct      work;
        u16                     hwvers;
 
        u16                     intrrxe;
@@ -335,7 +346,13 @@ struct musb {
        struct list_head        in_bulk;        /* of musb_qh */
        struct list_head        out_bulk;       /* of musb_qh */
 
+       struct workqueue_struct *gb_queue;
+       struct work_struct      gb_work;
+       spinlock_t              gb_lock;
+       struct list_head        gb_list;        /* of urbs */
+
        struct timer_list       otg_timer;
+       u8                      en_otg_timer;
        struct notifier_block   nb;
 
        struct dma_controller   *dma_controller;
@@ -369,6 +386,7 @@ struct musb {
        u16 epmask;
        u8 nr_endpoints;
 
+       int                     port_mode;      /* MUSB_PORT_MODE_* */
        int                     (*board_set_power)(int state);
 
        u8                      min_power;      /* vbus for periph, in mA/2 */
@@ -442,6 +460,8 @@ struct musb {
 #ifdef CONFIG_DEBUG_FS
        struct dentry           *debugfs_root;
 #endif
+       u32                     sof_enabled;
+       u8                      tx_isoc_sched_enable;
 };
 
 static inline struct musb *gadget_to_musb(struct usb_gadget *g)
@@ -531,6 +551,10 @@ extern void musb_load_testpacket(struct musb *);
 extern irqreturn_t musb_interrupt(struct musb *);
 
 extern void musb_hnp_stop(struct musb *musb);
+extern void musb_save_context(struct musb *musb);
+extern void musb_restore_context(struct musb *musb);
+extern void musb_gb_work(struct work_struct *data);
+extern void musb_restart(struct musb *musb);
 
 static inline void musb_platform_set_vbus(struct musb *musb, int is_on)
 {
@@ -589,4 +613,19 @@ static inline int musb_platform_exit(struct musb *musb)
        return musb->ops->exit(musb);
 }
 
+static inline int musb_enable_sof(struct musb *musb)
+{
+       if (!musb->ops->enable_sof)
+               return -EINVAL;
+
+       return musb->ops->enable_sof(musb);
+}
+
+static inline int musb_disable_sof(struct musb *musb)
+{
+       if (!musb->ops->disable_sof)
+               return -EINVAL;
+
+       return musb->ops->disable_sof(musb);
+}
 #endif /* __MUSB_CORE_H__ */
index 1b6b827b769f5f9fb0398f54208d425bbaafc446..d0a59528d03cc51ac561fbe02c5c5f4bafeabcb5 100644 (file)
@@ -74,6 +74,12 @@ struct musb_hw_ep;
 #define        is_cppi_enabled()       0
 #endif
 
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+#define        is_cppi41_enabled()     1
+#else
+#define        is_cppi41_enabled()     0
+#endif
+
 #ifdef CONFIG_USB_TUSB_OMAP_DMA
 #define tusb_dma_omap()                        1
 #else
index 341a4b54448fd47e81802aa2004272037b1cfeea..79d55104fd99d6375f89864fa7897078c081e86a 100644 (file)
 #include <linux/of_address.h>
 
 #include "musb_core.h"
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+#include "cppi41.h"
+#include "cppi41_dma.h"
+#endif
 
 #ifdef CONFIG_OF
 static const struct of_device_id musb_dsps_of_match[];
@@ -66,6 +70,32 @@ static inline void dsps_writeb(void __iomem *addr, unsigned offset, u8 data)
 static inline void dsps_writel(void __iomem *addr, unsigned offset, u32 data)
        { __raw_writel(data, addr + offset); }
 
+/**
+ * DSPS usbss wrapper register offset
+ * this register definition includes usbss wrapper for TI DSPS
+ */
+struct dsps_usbss_wrapper {
+       u16     revision;
+       u16     syscfg;
+       u16     irq_eoi;
+       u16     irq_status_raw;
+       u16     irq_status;
+       u16     irq_enable_set;
+       u16     irq_enable_clr;
+       u16     irq_tx0_dma_th_enb;
+       u16     irq_rx0_dma_th_enb;
+       u16     irq_tx1_dma_th_enb;
+       u16     irq_rx1_dma_th_enb;
+       u16     irq_usb0_dma_enable;
+       u16     irq_usb1_dma_enable;
+       u16     irq_tx0_frame_th_enb;
+       u16     irq_rx0_frame_th_enb;
+       u16     irq_tx1_frame_th_enb;
+       u16     irq_rx1_frame_th_enb;
+       u16     irq_usb0_frame_enable;
+       u16     irq_usb1_frame_enable;
+};
+
 /**
  * DSPS musb wrapper register offset.
  * FIXME: This should be expanded to have all the wrapper registers from TI DSPS
@@ -82,7 +112,17 @@ struct dsps_musb_wrapper {
        u16     coreintr_set;
        u16     coreintr_clear;
        u16     coreintr_status;
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       u16     tx_mode;
+       u16     rx_mode;
+       u16     g_rndis;
+       u16     auto_req;
+       u16     tear_down;
+       u16     th_xdma_idle;
+#endif
+       u16     srp_fix_time;
        u16     phy_utmi;
+       u16     mgc_utmi_lpback;
        u16     mode;
 
        /* bit positions for control */
@@ -112,8 +152,51 @@ struct dsps_musb_wrapper {
        u8              poll_seconds;
        /* number of musb instances */
        u8              instances;
+       /* usbss wrapper register */
+       struct dsps_usbss_wrapper usbss;
+};
+
+#ifdef CONFIG_PM_SLEEP
+struct dsps_usbss_regs {
+       u32     sysconfig;
+
+       u32     irq_en_set;
+
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       u32     irq_dma_th_tx0[4];
+       u32     irq_dma_th_rx0[4];
+       u32     irq_dma_th_tx1[4];
+       u32     irq_dma_th_rx1[4];
+       u32     irq_dma_en[2];
+
+       u32     irq_frame_th_tx0[4];
+       u32     irq_frame_th_rx0[4];
+       u32     irq_frame_th_tx1[4];
+       u32     irq_frame_th_rx1[4];
+       u32     irq_frame_en[2];
+#endif
 };
 
+struct dsps_usb_regs {
+       u32     control;
+
+       u32     irq_en_set[2];
+
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       u32     tx_mode;
+       u32     rx_mode;
+       u32     grndis_size[15];
+       u32     auto_req;
+       u32     teardn;
+       u32     th_xdma_idle;
+#endif
+       u32     srp_fix;
+       u32     phy_utmi;
+       u32     mgc_utmi_loopback;
+       u32     mode;
+};
+#endif
+
 /**
  * DSPS glue structure.
  */
@@ -124,6 +207,20 @@ struct dsps_glue {
        struct timer_list timer[2];     /* otg_workaround timer */
        unsigned long last_timer[2];    /* last timer data for each instance */
        u32 __iomem *usb_ctrl[2];
+       u32 __iomem *usbss_addr;
+       u8      first;                  /* ignore first call of resume */
+       u8      timer_enab[2];
+#ifdef CONFIG_PM
+       struct dsps_usbss_regs usbss_regs;
+       struct dsps_usb_regs usb_regs[2];
+#endif
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       struct cppi41_sched_tbl_t *dma_sched_table;
+       u32 __iomem *dma_addr;
+       u8 dma_irq;
+       u8 dma_init_done;
+       u8 max_dma_channel;
+#endif
 };
 
 #define        DSPS_AM33XX_CONTROL_MODULE_PHYS_0       0x44e10620
@@ -139,6 +236,445 @@ static const resource_size_t dsps_control_module_phys[] = {
 #define USBPHY_OTGVDET_EN      (1 << 19)
 #define USBPHY_OTGSESSEND_EN   (1 << 20)
 
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+static irqreturn_t cppi41dma_interrupt(int irq, void *hci);
+#define CPPI41_ADDR(offs) ((void *)((u32)glue->dma_addr + (offs - 0x2000)))
+
+/*
+ * CPPI 4.1 resources used for USB OTG controller module:
+ *
+ tx/rx completion queues for usb0 */
+static u16 tx_comp_q[] = {93, 94, 95, 96, 97,
+                               98, 99, 100, 101, 102,
+                               103, 104, 105, 106, 107 };
+
+static u16 rx_comp_q[] = {109, 110, 111, 112, 113,
+                               114, 115, 116, 117, 118,
+                               119, 120, 121, 122, 123 };
+
+/* tx/rx completion queues for usb1 */
+static u16 tx_comp_q1[] = {125, 126, 127, 128, 129,
+                                130, 131, 132, 133, 134,
+                                135, 136, 137, 138, 139 };
+
+static u16 rx_comp_q1[] = {141, 142, 143, 144, 145,
+                                146, 147, 148, 149, 150,
+                                151, 152, 153, 154, 155 };
+
+/* cppi41 dma tx channel info */
+static const struct cppi41_tx_ch tx_ch_info[] = {
+       [0] = {
+               .port_num       = 1,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 32} , {0, 33} }
+       },
+       [1] = {
+               .port_num       = 2,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 34} , {0, 35} }
+       },
+       [2] = {
+               .port_num       = 3,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 36} , {0, 37} }
+       },
+       [3] = {
+               .port_num       = 4,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 38} , {0, 39} }
+       },
+       [4] = {
+               .port_num       = 5,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 40} , {0, 41} }
+       },
+       [5] = {
+               .port_num       = 6,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 42} , {0, 43} }
+       },
+       [6] = {
+               .port_num       = 7,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 44} , {0, 45} }
+       },
+       [7] = {
+               .port_num       = 8,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 46} , {0, 47} }
+       },
+       [8] = {
+               .port_num       = 9,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 48} , {0, 49} }
+       },
+       [9] = {
+               .port_num       = 10,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 50} , {0, 51} }
+       },
+       [10] = {
+               .port_num       = 11,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 52} , {0, 53} }
+       },
+       [11] = {
+               .port_num       = 12,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 54} , {0, 55} }
+       },
+       [12] = {
+               .port_num       = 13,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 56} , {0, 57} }
+       },
+       [13] = {
+               .port_num       = 14,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 58} , {0, 59} }
+       },
+       [14] = {
+               .port_num       = 15,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 60} , {0, 61} }
+       },
+       [15] = {
+               .port_num       = 1,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 62} , {0, 63} }
+       },
+       [16] = {
+               .port_num       = 2,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 64} , {0, 65} }
+       },
+       [17] = {
+               .port_num       = 3,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 66} , {0, 67} }
+       },
+       [18] = {
+               .port_num       = 4,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 68} , {0, 69} }
+       },
+       [19] = {
+               .port_num       = 5,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 70} , {0, 71} }
+       },
+       [20] = {
+               .port_num       = 6,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 72} , {0, 73} }
+       },
+       [21] = {
+               .port_num       = 7,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 74} , {0, 75} }
+       },
+       [22] = {
+               .port_num       = 8,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 76} , {0, 77} }
+       },
+       [23] = {
+               .port_num       = 9,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 78} , {0, 79} }
+       },
+       [24] = {
+               .port_num       = 10,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 80} , {0, 81} }
+       },
+       [25] = {
+               .port_num       = 11,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 82} , {0, 83} }
+       },
+       [26] = {
+               .port_num       = 12,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 84} , {0, 85} }
+       },
+       [27] = {
+               .port_num       = 13,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 86} , {0, 87} }
+       },
+       [28] = {
+               .port_num       = 14,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 88} , {0, 89} }
+       },
+       [29] = {
+               .port_num       = 15,
+               .num_tx_queue   = 2,
+               .tx_queue       = { {0, 90} , {0, 91} }
+       }
+};
+
+/* Queues 0 to 66 are pre-assigned, others are spare */
+static const u32 assigned_queues[] = { 0xffffffff, /* queue 0..31 */
+                                       0xffffffff, /* queue 32..63 */
+                                       0xffffffff, /* queue 64..95 */
+                                       0xffffffff, /* queue 96..127 */
+                                       0x0fffffff  /* queue 128..155 */
+                                       };
+
+#define USB_CPPI41_NUM_CH      15
+#define USB_CPPI41_CH_NUM_PD   128
+#define DSPS_TX_MODE_REG       0x70    /* Transparent, CDC, [Generic] RNDIS */
+#define DSPS_RX_MODE_REG       0x74    /* Transparent, CDC, [Generic] RNDIS */
+#define DSPS_USB_AUTOREQ_REG   0xd0
+#define DSPS_USB_TEARDOWN_REG  0xd8
+#define USB_CPPI41_MAX_PD      (USB_CPPI41_CH_NUM_PD * (USB_CPPI41_NUM_CH+1))
+
+static int cppi41_init(struct dsps_glue *glue)
+{
+       struct usb_cppi41_info *cppi_info = &usb_cppi41_info[0];
+       u16 blknum = 0, order;
+       int i, id, status = 0, irq = glue->dma_irq;
+
+       if (glue->dma_init_done)
+               goto err0;
+
+       for (id = 0; id < glue->wrp->instances; ++id) {
+               cppi_info = &usb_cppi41_info[id];
+
+               cppi_info->max_dma_ch = USB_CPPI41_NUM_CH;
+               cppi_info->max_pkt_desc = USB_CPPI41_MAX_PD;
+
+               cppi_info->ep_dma_ch = kzalloc(USB_CPPI41_NUM_CH, GFP_KERNEL);
+               if (!cppi_info->ep_dma_ch) {
+                       pr_err("memory allocation failure\n");
+                       status = -ENOMEM;
+                       goto err1;
+               }
+
+               /* init cppi info structure  */
+               cppi_info->dma_block = 0;
+               for (i = 0 ; i < USB_CPPI41_NUM_CH ; i++)
+                       cppi_info->ep_dma_ch[i] = i + (15 * id);
+
+               cppi_info->q_mgr = 0;
+               cppi_info->num_tx_comp_q = 15;
+               cppi_info->num_rx_comp_q = 15;
+               cppi_info->tx_comp_q = id ? tx_comp_q1 : tx_comp_q;
+               cppi_info->rx_comp_q = id ? rx_comp_q1 : rx_comp_q;
+               cppi_info->bd_intr_enb = 1;
+               cppi_info->rx_dma_mode = USB_TRANSPARENT_MODE;
+               cppi_info->rx_inf_mode = 0;
+               cppi_info->sched_tbl_ctrl = 0;
+               cppi_info->tx_isoc_sched_enab = 1;
+
+               cppi_info->wrp.autoreq_reg = DSPS_USB_AUTOREQ_REG;
+               cppi_info->wrp.teardown_reg = DSPS_USB_TEARDOWN_REG;
+               cppi_info->wrp.tx_mode_reg = DSPS_TX_MODE_REG;
+               cppi_info->wrp.rx_mode_reg = DSPS_RX_MODE_REG;
+       }
+
+       glue->max_dma_channel = USB_CPPI41_NUM_CH * glue->wrp->instances * 2;
+       glue->dma_sched_table = kzalloc(sizeof(struct cppi41_sched_tbl_t *) *
+                                       glue->max_dma_channel, GFP_KERNEL);
+       if (!glue->dma_sched_table) {
+               pr_err("memory allocation failure\n");
+               status = -ENOMEM;
+               goto err1;
+       }
+
+       /* initialize the schedular table entries */
+       for (i = 0; i < glue->max_dma_channel; i += 2) {
+               /* add tx dmach for schedular table */
+               glue->dma_sched_table[i].dma_ch = i/2;
+               glue->dma_sched_table[i].is_tx = 1;
+               glue->dma_sched_table[i].enb = 1;
+
+               /* add rx dmach for schedular table */
+               glue->dma_sched_table[i+1].dma_ch = i/2;
+               glue->dma_sched_table[i+1].is_tx = 0;
+               glue->dma_sched_table[i+1].enb = 1;
+       }
+
+       /* Queue manager information */
+       cppi41_queue_mgr[0].num_queue = 159;
+       cppi41_queue_mgr[0].queue_types = CPPI41_FREE_DESC_BUF_QUEUE |
+                                               CPPI41_UNASSIGNED_QUEUE;
+       cppi41_queue_mgr[0].base_fdbq_num = 0;
+       cppi41_queue_mgr[0].assigned = assigned_queues;
+
+       /* init DMA block */
+       cppi41_dma_block[0].num_tx_ch = 30;
+       cppi41_dma_block[0].num_rx_ch = 30;
+       cppi41_dma_block[0].tx_ch_info = tx_ch_info;
+
+       /* initilize cppi41 dma & Qmgr address */
+       cppi41_queue_mgr[0].q_mgr_rgn_base = CPPI41_ADDR(QMGR_RGN_OFFS);
+       cppi41_queue_mgr[0].desc_mem_rgn_base = CPPI41_ADDR(QMRG_DESCRGN_OFFS);
+       cppi41_queue_mgr[0].q_mgmt_rgn_base = CPPI41_ADDR(QMGR_REG_OFFS);
+       cppi41_queue_mgr[0].q_stat_rgn_base = CPPI41_ADDR(QMGR_STAT_OFFS);
+       cppi41_dma_block[0].global_ctrl_base = CPPI41_ADDR(DMA_GLBCTRL_OFFS);
+       cppi41_dma_block[0].ch_ctrl_stat_base = CPPI41_ADDR(DMA_CHCTRL_OFFS);
+       cppi41_dma_block[0].sched_ctrl_base = CPPI41_ADDR(DMA_SCHED_OFFS);
+       cppi41_dma_block[0].sched_table_base = CPPI41_ADDR(DMA_SCHEDTBL_OFFS);
+
+       /* Initialize for Linking RAM region 0 alone */
+       status = cppi41_queue_mgr_init(cppi_info->q_mgr, 0, 0x3fff);
+       if (status < 0)
+               goto err1;
+
+       order = get_count_order(glue->max_dma_channel);
+       if (order < 5)
+               order = 5;
+
+       status = cppi41_dma_block_init(blknum, cppi_info->q_mgr, order,
+                       glue->dma_sched_table, glue->max_dma_channel);
+       if (status < 0)
+               goto err1;
+
+       /* attach to the IRQ */
+       if (request_irq(irq, cppi41dma_interrupt, 0, "usb-cppi41dma", glue)) {
+               pr_err("cppi41dma request_irq %d failed!\n", irq);
+               status = -ENODEV;
+               goto err1;
+       } else
+               pr_info("registerd cppi41-dma at IRQ %d dmabase %p\n",
+                       irq, cppi41_dma_block[0].global_ctrl_base);
+
+
+       /* enable all usbss the interrupts */
+       dsps_writel(glue->usbss_addr, glue->wrp->usbss.irq_eoi, 0);
+       dsps_writel(glue->usbss_addr, glue->wrp->usbss.irq_enable_set,
+               USBSS_INTR_FLAGS);
+       dsps_writel(glue->usbss_addr, glue->wrp->usbss.irq_usb0_dma_enable,
+               0xFFFeFFFe);
+
+       glue->dma_init_done = 1;
+       return 0;
+
+err1:
+       for (id = 0; id < glue->wrp->instances; ++id) {
+               cppi_info = &usb_cppi41_info[id];
+               kfree(cppi_info->ep_dma_ch);
+       }
+err0:
+       return status;
+}
+
+void cppi41_free(struct dsps_glue *glue)
+{
+       u32 numch, blknum, order;
+       struct usb_cppi41_info *cppi_info = &usb_cppi41_info[0];
+
+       if (!glue->dma_init_done)
+               return ;
+
+       numch =  glue->max_dma_channel;
+       /* disable the interrupts */
+       dsps_writel(glue->usbss_addr, glue->wrp->usbss.irq_eoi, 0);
+       dsps_writel(glue->usbss_addr, glue->wrp->usbss.irq_enable_set, 0);
+       dsps_writel(glue->usbss_addr, glue->wrp->usbss.irq_usb0_dma_enable, 0);
+
+       order = get_count_order(numch);
+       blknum = cppi_info->dma_block;
+
+       /* uninit cppi41 dma & queue mgr */
+       cppi41_dma_block_uninit(blknum, cppi_info->q_mgr, order,
+                       glue->dma_sched_table, numch);
+       cppi41_queue_mgr_uninit(cppi_info->q_mgr);
+
+       /* free the irq_resource */
+       free_irq(glue->dma_irq, glue);
+
+       /* free cppi resource */
+       kfree(cppi_info->ep_dma_ch);
+       kfree(glue->dma_sched_table);
+       glue->dma_init_done = 0;
+}
+
+static irqreturn_t cppi41dma_interrupt(int irq, void *hci)
+{
+       struct dsps_glue *glue = hci;
+       u32 intr_status;
+       irqreturn_t ret = IRQ_NONE;
+       u32 q_cmpl_status_0, q_cmpl_status_1, q_cmpl_status_2;
+       u32 usb0_tx_intr, usb0_rx_intr;
+       u32 usb1_tx_intr, usb1_rx_intr;
+       void *q_mgr_base = cppi41_queue_mgr[0].q_mgr_rgn_base;
+       unsigned long flags;
+       struct platform_device *pdev;
+       struct device *dev;
+       struct musb *musb;
+
+       /*
+        * CPPI 4.1 interrupts share the same IRQ and the EOI register but
+        * don't get reflected in the interrupt source/mask registers.
+        */
+       /*
+        * Check for the interrupts from Tx/Rx completion queues; they
+        * are level-triggered and will stay asserted until the queues
+        * are emptied.  We're using the queue pending register 0 as a
+        * substitute for the interrupt status register and reading it
+        * directly for speed.
+        */
+       intr_status = dsps_readl(glue->usbss_addr, glue->wrp->usbss.irq_status);
+
+       if (intr_status)
+               dsps_writel(glue->usbss_addr, glue->wrp->usbss.irq_status,
+                       intr_status);
+       else
+               pr_err("spurious usbss intr\n");
+
+       q_cmpl_status_0 = dsps_readl(q_mgr_base, CPPI41_QSTATUS_REG2);
+       q_cmpl_status_1 = dsps_readl(q_mgr_base, CPPI41_QSTATUS_REG3);
+       q_cmpl_status_2 = dsps_readl(q_mgr_base, CPPI41_QSTATUS_REG4);
+
+       /* USB0 tx/rx completion */
+       /* usb0 tx completion interrupt for ep1..15 */
+       usb0_tx_intr = (q_cmpl_status_0 >> 29) |
+                       ((q_cmpl_status_1 & 0xFFF) << 3);
+       usb0_rx_intr = ((q_cmpl_status_1 & 0x07FFe000) >> 13);
+
+       usb1_tx_intr = (q_cmpl_status_1 >> 29) |
+                       ((q_cmpl_status_2 & 0xFFF) << 3);
+       usb1_rx_intr = ((q_cmpl_status_2 & 0x0fffe000) >> 13);
+
+       /* get proper musb handle based usb0/usb1 ctrl-id */
+       pdev = glue->musb[0];
+       dev = &pdev->dev;
+       musb = (struct musb *)dev_get_drvdata(&pdev->dev);
+
+       dev_dbg(musb->controller, "CPPI 4.1 IRQ: Tx %x, Rx %x\n", usb0_tx_intr,
+                               usb0_rx_intr);
+       if (musb && (usb0_tx_intr || usb0_rx_intr)) {
+               spin_lock_irqsave(&musb->lock, flags);
+               cppi41_completion(musb, usb0_rx_intr,
+                                       usb0_tx_intr);
+               spin_unlock_irqrestore(&musb->lock, flags);
+               ret = IRQ_HANDLED;
+       }
+
+       dev_dbg(musb->controller, "CPPI 4.1 IRQ: Tx %x, Rx %x\n", usb1_tx_intr,
+               usb1_rx_intr);
+       pdev = glue->musb[1];
+       dev = &pdev->dev;
+       musb = (struct musb *)dev_get_drvdata(&pdev->dev);
+
+       if (musb && (usb1_rx_intr || usb1_tx_intr)) {
+               spin_lock_irqsave(&musb->lock, flags);
+               cppi41_completion(musb, usb1_rx_intr,
+                       usb1_tx_intr);
+               spin_unlock_irqrestore(&musb->lock, flags);
+               ret = IRQ_HANDLED;
+       }
+
+       dsps_writel(glue->usbss_addr, glue->wrp->usbss.irq_eoi, 0);
+
+       return ret;
+}
+#endif /* CONFIG_USB_TI_CPPI41_DMA */
+
 /**
  * musb_dsps_phy_control - phy on/off
  * @glue: struct dsps_glue *
@@ -151,9 +687,17 @@ static const resource_size_t dsps_control_module_phys[] = {
  * XXX: This function will be removed once we have a seperate driver for
  * control module
  */
-static void musb_dsps_phy_control(struct dsps_glue *glue, u8 id, u8 on)
+static void musb_dsps_phy_control(struct dsps_glue *glue, u8 id, u8 on,
+               bool wake_up)
 {
        u32 usbphycfg;
+       u32 wkup_val, wkup_flag;
+       void __iomem *wkup_ctrl;
+
+#define        USB0_PHY_WKUP_OFFS              28
+#define        USB1_PHY_WKUP_OFFS              20
+#define        DSPS_USB0_WKUP_CTRL_ENABLE      (1 << 0)
+#define        DSPS_USB1_WKUP_CTRL_ENABLE      (1 << 8)
 
        usbphycfg = readl(glue->usb_ctrl[id]);
 
@@ -163,8 +707,20 @@ static void musb_dsps_phy_control(struct dsps_glue *glue, u8 id, u8 on)
        } else {
                usbphycfg |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN;
        }
-
        writel(usbphycfg, glue->usb_ctrl[id]);
+
+       wkup_ctrl = glue->usb_ctrl[id] + (id ? USB1_PHY_WKUP_OFFS :
+                       USB0_PHY_WKUP_OFFS);
+       wkup_val = readl(wkup_ctrl);
+
+       wkup_flag = id ? DSPS_USB1_WKUP_CTRL_ENABLE :
+                       DSPS_USB0_WKUP_CTRL_ENABLE;
+       if (wake_up)
+               wkup_val |= wkup_flag;
+       else
+               wkup_val &= ~wkup_flag;
+
+       writel(wkup_val, wkup_ctrl);
 }
 /**
  * dsps_musb_enable - enable interrupts
@@ -294,6 +850,52 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
        mod_timer(&glue->timer[pdev->id], timeout);
 }
 
+int dsps_enable_sof(struct musb *musb)
+{
+       struct device *dev = musb->controller;
+       struct platform_device *pdev = to_platform_device(dev->parent);
+       struct dsps_glue *glue = platform_get_drvdata(pdev);
+       const struct dsps_musb_wrapper *wrp = glue->wrp;
+       void __iomem *reg_base = musb->ctrl_base;
+
+       if (musb->sof_enabled) {
+               musb->sof_enabled++;
+               return musb->sof_enabled;
+       }
+
+       musb->sof_enabled = 1;
+       musb_writeb(musb->mregs, MUSB_INTRUSBE, MUSB_INTR_SOF |
+               musb_readb(musb->mregs, MUSB_INTRUSBE));
+       musb_writel(reg_base, wrp->coreintr_set, MUSB_INTR_SOF |
+               musb_readl(reg_base, wrp->coreintr_set));
+
+       return musb->sof_enabled;
+}
+
+int dsps_disable_sof(struct musb *musb)
+{
+       struct device *dev = musb->controller;
+       struct platform_device *pdev = to_platform_device(dev->parent);
+       struct dsps_glue *glue = platform_get_drvdata(pdev);
+       const struct dsps_musb_wrapper *wrp = glue->wrp;
+       void __iomem *reg_base = musb->ctrl_base;
+       u8 intrusb;
+
+       if (musb->sof_enabled)
+               musb->sof_enabled--;
+
+       if (musb->sof_enabled)
+               return musb->sof_enabled;
+
+       intrusb = musb_readb(musb->mregs, MUSB_INTRUSBE);
+       intrusb &= ~MUSB_INTR_SOF;
+       musb_writeb(musb->mregs, MUSB_INTRUSBE, intrusb);
+       musb_writel(reg_base, wrp->coreintr_clear, MUSB_INTR_SOF);
+       musb->sof_enabled = 0;
+
+       return 0;
+}
+
 static irqreturn_t dsps_interrupt(int irq, void *hci)
 {
        struct musb  *musb = hci;
@@ -305,6 +907,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
        unsigned long flags;
        irqreturn_t ret = IRQ_NONE;
        u32 epintr, usbintr;
+       u8 is_babble;
 
        spin_lock_irqsave(&musb->lock, flags);
 
@@ -335,8 +938,12 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
         * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
         * Also, DRVVBUS pulses for SRP (but not at 5V) ...
         */
-       if (usbintr & MUSB_INTR_BABBLE)
+       is_babble = is_host_active(musb) && usbintr & MUSB_INTR_BABBLE;
+       if (is_babble) {
                pr_info("CAUTION: musb: Babble Interrupt Occurred\n");
+               musb->int_usb = MUSB_INTR_DISCONNECT;
+               is_babble = 1;
+       }
 
        if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
                int drvvbus = dsps_readl(reg_base, wrp->status);
@@ -363,7 +970,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
                                        jiffies + wrp->poll_seconds * HZ);
                        WARNING("VBUS error workaround (delay coming)\n");
                } else if (drvvbus) {
-                       musb->is_active = 1;
                        MUSB_HST_MODE(musb);
                        musb->xceiv->otg->default_a = 1;
                        musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
@@ -387,6 +993,9 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
        if (musb->int_tx || musb->int_rx || musb->int_usb)
                ret |= musb_interrupt(musb);
 
+       if (is_babble)
+               schedule_work(&musb->work);
+
  eoi:
        /* EOI needs to be written for the IRQ to be re-asserted. */
        if (ret == IRQ_HANDLED || epintr || usbintr)
@@ -402,6 +1011,42 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
        return ret;
 }
 
+static int dsps_musb_restart(struct musb *musb)
+{
+       struct device *dev = musb->controller;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct dsps_glue *glue = dev_get_drvdata(dev->parent);
+       const struct dsps_musb_wrapper *wrp = glue->wrp;
+       void __iomem *reg_base = musb->ctrl_base;
+
+       /* Reset the musb */
+       dsps_writel(reg_base, wrp->control, (1 << wrp->reset));
+       udelay(100);
+
+       /* Stop the on-chip PHY and its PLL. */
+       musb_dsps_phy_control(glue, pdev->id, 0, false);
+       udelay(100);
+
+       /* Start the on-chip PHY and its PLL. */
+       musb_dsps_phy_control(glue, pdev->id, 1, false);
+       udelay(100);
+
+       /* reinit the endpoint fifo address */
+       musb_restart(musb);
+
+       return 0;
+}
+
+static void dsps_musb_restart_work(struct work_struct *work)
+{
+       struct musb *musb = container_of(work, struct musb, work);
+       struct platform_device *pdev = to_platform_device(musb->controller);
+
+       dev_dbg(musb->controller, "restarting musb%d ...\n",
+                       pdev->id);
+       dsps_musb_restart(musb);
+}
+
 static int dsps_musb_init(struct musb *musb)
 {
        struct device *dev = musb->controller;
@@ -429,12 +1074,13 @@ static int dsps_musb_init(struct musb *musb)
        }
 
        setup_timer(&glue->timer[pdev->id], otg_timer, (unsigned long) musb);
+       INIT_WORK(&musb->work, dsps_musb_restart_work);
 
        /* Reset the musb */
        dsps_writel(reg_base, wrp->control, (1 << wrp->reset));
 
        /* Start the on-chip PHY and its PLL. */
-       musb_dsps_phy_control(glue, pdev->id, 1);
+       musb_dsps_phy_control(glue, pdev->id, 1, 0);
 
        musb->isr = dsps_interrupt;
 
@@ -462,7 +1108,7 @@ static int dsps_musb_exit(struct musb *musb)
        del_timer_sync(&glue->timer[pdev->id]);
 
        /* Shutdown the on-chip PHY and its PLL. */
-       musb_dsps_phy_control(glue, pdev->id, 0);
+       musb_dsps_phy_control(glue, pdev->id, 0, 0);
 
        /* NOP driver needs change if supporting dual instance */
        usb_put_phy(musb->xceiv);
@@ -479,6 +1125,11 @@ static struct musb_platform_ops dsps_ops = {
        .disable        = dsps_musb_disable,
 
        .try_idle       = dsps_musb_try_idle,
+       .enable_sof     = dsps_enable_sof,
+       .disable_sof    = dsps_disable_sof,
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       .sof_handler    = cppi41_isoc_schedular,
+#endif
 };
 
 static u64 musb_dmamask = DMA_BIT_MASK(32);
@@ -630,6 +1281,13 @@ static int dsps_probe(struct platform_device *pdev)
        }
 
        glue->dev = &pdev->dev;
+       iomem[0].flags = IORESOURCE_MEM;
+       glue->usbss_addr = devm_request_and_ioremap(&pdev->dev, iomem);
+       if (glue->usbss_addr == NULL) {
+               dev_err(&pdev->dev, "Failed to obtain usbss_addr memory\n");
+               ret = -ENODEV;
+               goto err1;
+       }
 
        glue->wrp = kmemdup(wrp, sizeof(*wrp), GFP_KERNEL);
        if (!glue->wrp) {
@@ -638,6 +1296,7 @@ static int dsps_probe(struct platform_device *pdev)
                goto err1;
        }
        platform_set_drvdata(pdev, glue);
+       glue->first = 1;
 
        /* enable the usbss clocks */
        pm_runtime_enable(&pdev->dev);
@@ -648,6 +1307,45 @@ static int dsps_probe(struct platform_device *pdev)
                goto err2;
        }
 
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       /* get memory resource */
+       iomem = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+       if (!iomem) {
+               dev_err(&pdev->dev, "failed to get usbss mem resourse\n");
+               ret = -ENODEV;
+               goto err1;
+       }
+
+       iomem[0].flags = IORESOURCE_MEM;
+       glue->dma_addr = devm_request_and_ioremap(&pdev->dev, iomem);
+       if (glue->dma_addr == NULL) {
+               dev_err(&pdev->dev, "Failed to obtain usbss_addr memory\n");
+               ret = -ENODEV;
+               goto err1;
+       }
+
+       /* first resource is for usbss, so start index from 1 */
+       iomem = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!iomem) {
+               dev_err(&pdev->dev, "failed to get irq for instance %d\n", 0);
+               ret = -ENODEV;
+               goto err0;
+       }
+       glue->dma_irq = iomem->start;
+
+       /* clear any USBSS interrupts */
+       dsps_writel(glue->usbss_addr, glue->wrp->usbss.irq_eoi, 0);
+       dsps_writel(glue->usbss_addr, glue->wrp->usbss.irq_status,
+               dsps_readl(glue->usbss_addr, glue->wrp->usbss.irq_status));
+
+       /* initialize the cppi41dma init */
+       ret = cppi41_init(glue);
+       if (ret) {
+               dev_err(&pdev->dev, "cppi4.1 dma init failed\n");
+               return ret;
+       }
+#endif
+
        /* create the child platform device for all instances of musb */
        for (i = 0; i < wrp->instances ; i++) {
                ret = dsps_create_musb_pdev(glue, i);
@@ -660,6 +1358,7 @@ static int dsps_probe(struct platform_device *pdev)
                }
        }
 
+
        return 0;
 
 err3:
@@ -685,21 +1384,219 @@ static int dsps_remove(struct platform_device *pdev)
        /* disable usbss clocks */
        pm_runtime_put(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
+
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       cppi41_free(glue);
+#endif
        kfree(glue->wrp);
        kfree(glue);
        return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
+static void dsps_save_context(struct dsps_glue *glue)
+{
+       struct dsps_usbss_regs *usbss = &glue->usbss_regs;
+       const struct dsps_musb_wrapper *wrp = glue->wrp;
+       u8 i, j;
+
+       /* save USBSS register */
+       usbss->irq_en_set = dsps_readl(glue->usbss_addr,
+                               wrp->epintr_set);
+
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       for (i = 0 ; i < 4 ; i++) {
+               usbss->irq_dma_th_tx0[i] = dsps_readl(glue->usbss_addr,
+                       wrp->usbss.irq_tx0_dma_th_enb + (4 * i));
+               usbss->irq_dma_th_rx0[i] = dsps_readl(glue->usbss_addr,
+                       wrp->usbss.irq_rx0_dma_th_enb + (4 * i));
+               usbss->irq_dma_th_tx1[i] = dsps_readl(glue->usbss_addr,
+                       wrp->usbss.irq_tx1_dma_th_enb + (4 * i));
+               usbss->irq_dma_th_rx1[i] = dsps_readl(glue->usbss_addr,
+                       wrp->usbss.irq_rx1_dma_th_enb + (4 * i));
+
+               usbss->irq_frame_th_tx0[i] = dsps_readl(glue->usbss_addr,
+                       wrp->usbss.irq_tx0_frame_th_enb + (4 * i));
+               usbss->irq_frame_th_rx0[i] = dsps_readl(glue->usbss_addr,
+                       wrp->usbss.irq_rx0_frame_th_enb + (4 * i));
+               usbss->irq_frame_th_tx1[i] = dsps_readl(glue->usbss_addr,
+                       wrp->usbss.irq_tx1_frame_th_enb + (4 * i));
+               usbss->irq_frame_th_rx1[i] = dsps_readl(glue->usbss_addr,
+                       wrp->usbss.irq_rx1_frame_th_enb + (4 * i));
+       }
+       for (i = 0 ; i < 2 ; i++) {
+               usbss->irq_dma_en[i] = dsps_readl(glue->usbss_addr,
+                               wrp->usbss.irq_usb0_dma_enable + (4 * i));
+               usbss->irq_frame_en[i] = dsps_readl(glue->usbss_addr,
+                               wrp->usbss.irq_usb0_frame_enable + (4 * i));
+       }
+#endif
+       /* save usbX register */
+       for (i = 0 ; i < wrp->instances ; i++) {
+               struct dsps_usb_regs *usb = &glue->usb_regs[i];
+               const struct dsps_musb_wrapper *wrp = glue->wrp;
+               struct musb *musb = platform_get_drvdata(glue->musb[i]);
+               struct device *dev = musb->controller;
+               struct platform_device *pdev = to_platform_device(dev);
+               void __iomem *cbase = musb->ctrl_base;
+
+               /* disable the timers */
+               if (timer_pending(&glue->timer[pdev->id]) &&
+                                       is_host_active(musb)) {
+                       del_timer_sync(&glue->timer[pdev->id]);
+                       glue->timer_enab[pdev->id] = 1;
+               }
+
+               if (timer_pending(&musb->otg_timer)) {
+                       del_timer_sync(&musb->otg_timer);
+                       musb->en_otg_timer = 1;
+               }
+
+               musb_save_context(musb);
+               usb->control = musb_readl(cbase, wrp->control);
+
+               for (j = 0 ; j < 2 ; j++)
+                       usb->irq_en_set[j] = musb_readl(cbase,
+                                               wrp->epintr_set + (4 * j));
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+               usb->tx_mode = musb_readl(cbase, wrp->tx_mode);
+               usb->rx_mode = musb_readl(cbase, wrp->rx_mode);
+
+               for (j = 0 ; j < 15 ; j++)
+                       usb->grndis_size[j] = musb_readl(cbase,
+                                               wrp->g_rndis + (j << 2));
+
+               usb->auto_req = musb_readl(cbase, wrp->auto_req);
+               usb->teardn = musb_readl(cbase, wrp->tear_down);
+               usb->th_xdma_idle = musb_readl(cbase, wrp->th_xdma_idle);
+#endif
+               usb->srp_fix = musb_readl(cbase, wrp->srp_fix_time);
+               usb->phy_utmi = musb_readl(cbase, wrp->phy_utmi);
+               usb->mgc_utmi_loopback = musb_readl(cbase,
+                                               wrp->mgc_utmi_lpback);
+               usb->mode = musb_readl(cbase, wrp->mode);
+       }
+       /* save CPPI4.1 DMA register */
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       /* save CPPI4.1 DMA register for dma block 0 */
+       cppi41_save_context(0);
+#endif
+}
+
+static void dsps_restore_context(struct dsps_glue *glue)
+{
+       struct dsps_usbss_regs *usbss = &glue->usbss_regs;
+       const struct dsps_musb_wrapper *wrp = glue->wrp;
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       void __iomem *usbss_addr = glue->usbss_addr;
+       struct dsps_usbss_wrapper *usbsswrp;
+#endif
+       u8 i, j;
+
+       /* restore USBSS register */
+       dsps_writel(glue->usbss_addr, wrp->epintr_set,
+                       usbss->irq_en_set);
+
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       usbsswrp = (struct dsps_usbss_wrapper *)&wrp->usbss;
+       for (i = 0 ; i < 4 ; i++) {
+
+               dsps_writel(usbss_addr, usbsswrp->irq_tx0_dma_th_enb + (4 * i),
+                               usbss->irq_dma_th_tx0[i]);
+               dsps_writel(usbss_addr, usbsswrp->irq_rx0_dma_th_enb + (4 * i),
+                               usbss->irq_dma_th_rx0[i]);
+               dsps_writel(usbss_addr, usbsswrp->irq_tx1_dma_th_enb + (4 * i),
+                               usbss->irq_dma_th_tx1[i]);
+               dsps_writel(usbss_addr, usbsswrp->irq_rx1_dma_th_enb + (4 * i),
+                               usbss->irq_dma_th_rx1[i]);
+
+               dsps_writel(usbss_addr, usbsswrp->irq_tx0_frame_th_enb + (4 * i)
+                                       , usbss->irq_frame_th_tx0[i]);
+               dsps_writel(usbss_addr, usbsswrp->irq_rx0_frame_th_enb + (4 * i)
+                                       , usbss->irq_frame_th_rx0[i]);
+               dsps_writel(usbss_addr, usbsswrp->irq_tx1_frame_th_enb + (4 * i)
+                                       , usbss->irq_frame_th_tx1[i]);
+               dsps_writel(usbss_addr, usbsswrp->irq_rx1_frame_th_enb + (4 * i)
+                                       , usbss->irq_frame_th_rx1[i]);
+       }
+       for (i = 0 ; i < 2 ; i++) {
+               dsps_writel(usbss_addr, usbsswrp->irq_usb0_dma_enable + (4 * i),
+                               usbss->irq_dma_en[i]);
+               dsps_writel(usbss_addr,
+                       usbsswrp->irq_usb0_frame_enable + (4 * i),
+                       usbss->irq_frame_en[i]);
+       }
+#endif
+       /* restore usbX register */
+       for (i = 0 ; i < 2 ; i++) {
+               struct dsps_usb_regs *usb = &glue->usb_regs[i];
+               const struct dsps_musb_wrapper *wrp = glue->wrp;
+               struct musb *musb = platform_get_drvdata(glue->musb[i]);
+               struct device *dev = musb->controller;
+               struct platform_device *pdev = to_platform_device(dev);
+               void __iomem *cbase = musb->ctrl_base;
+
+               musb_restore_context(musb);
+               musb_writel(cbase, wrp->control, usb->control);
+
+               for (j = 0 ; j < 2 ; j++)
+                       musb_writel(cbase, wrp->epintr_set + (4 * j),
+                                       usb->irq_en_set[j]);
+
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+               musb_writel(cbase, wrp->tx_mode, usb->tx_mode);
+               musb_writel(cbase, wrp->rx_mode, usb->rx_mode);
+
+               for (j = 0 ; j < 15 ; j++)
+                       musb_writel(cbase, wrp->g_rndis + (j << 2),
+                                       usb->grndis_size[j]);
+
+               musb_writel(cbase, wrp->auto_req, usb->auto_req);
+               musb_writel(cbase, wrp->tear_down, usb->teardn);
+               musb_writel(cbase, wrp->th_xdma_idle, usb->th_xdma_idle);
+#endif
+               musb_writel(cbase, wrp->srp_fix_time, usb->srp_fix);
+               musb_writel(cbase, wrp->phy_utmi, usb->phy_utmi);
+               musb_writel(cbase, wrp->mgc_utmi_lpback,
+                               usb->mgc_utmi_loopback);
+               musb_writel(cbase, wrp->mode, usb->mode);
+
+               /* reenable the timers */
+               if (glue->timer_enab[pdev->id] && is_host_active(musb)) {
+                       mod_timer(&glue->timer[pdev->id],
+                                       jiffies + wrp->poll_seconds * HZ);
+                       glue->timer_enab[pdev->id] = 0;
+               }
+               if (musb->en_otg_timer) {
+                       mod_timer(&musb->otg_timer,
+                                       jiffies + wrp->poll_seconds * HZ);
+                       musb->en_otg_timer = 0;
+               }
+       }
+       /* restore CPPI4.1 DMA register */
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       /* save CPPI4.1 DMA register for dma block 0 */
+       cppi41_restore_context(0);
+
+       /* controller needs 200ms delay to resume */
+       msleep(200);
+#endif
+}
+
 static int dsps_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev->parent);
        struct dsps_glue *glue = platform_get_drvdata(pdev);
        const struct dsps_musb_wrapper *wrp = glue->wrp;
-       int i;
+       int i, is_wkup;
 
-       for (i = 0; i < wrp->instances; i++)
-               musb_dsps_phy_control(glue, i, 0);
+       /* save wrappers and cppi4.1 dma register */
+       dsps_save_context(glue);
+
+       for (i = 0; i < wrp->instances; i++) {
+               is_wkup = device_may_wakeup(&glue->musb[i]->dev) ? true : false;
+               musb_dsps_phy_control(glue, i, 0, is_wkup);
+       }
 
        return 0;
 }
@@ -711,8 +1608,20 @@ static int dsps_resume(struct device *dev)
        const struct dsps_musb_wrapper *wrp = glue->wrp;
        int i;
 
+       /*
+        * ignore first call of resume as all registers are not yet
+        * initialized
+        */
+       if (glue->first) {
+               glue->first = 0;
+               return 0;
+       }
+
        for (i = 0; i < wrp->instances; i++)
-               musb_dsps_phy_control(glue, i, 1);
+               musb_dsps_phy_control(glue, i, 1, false);
+
+       /* restore wrappers and cppi4.1 dma register */
+       dsps_restore_context(glue);
 
        return 0;
 }
@@ -731,7 +1640,17 @@ static const struct dsps_musb_wrapper ti81xx_driver_data = {
        .coreintr_set           = 0x3c,
        .coreintr_clear         = 0x44,
        .coreintr_status        = 0x34,
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+       .tx_mode                = 0x70,
+       .rx_mode                = 0x74,
+       .g_rndis                = 0x80,
+       .auto_req               = 0xd0,
+       .tear_down              = 0xd8,
+       .th_xdma_idle           = 0xdc,
+#endif
+       .srp_fix_time           = 0xd4,
        .phy_utmi               = 0xe0,
+       .mgc_utmi_lpback        = 0xe4,
        .mode                   = 0xe8,
        .reset                  = 0,
        .otg_disable            = 21,
@@ -748,7 +1667,28 @@ static const struct dsps_musb_wrapper ti81xx_driver_data = {
        .rxep_bitmap            = (0xfffe << 16),
        .musb_core_offset       = 0x400,
        .poll_seconds           = 2,
-       .instances              = 1,
+       .instances              = 2,
+       .usbss                  =  {
+               .revision       = 0x00,
+               .syscfg         = 0x10,
+               .irq_eoi        = 0x20,
+               .irq_status_raw = 0x24,
+               .irq_status     = 0x28,
+               .irq_enable_set = 0x2c,
+               .irq_enable_clr = 0x30,
+               .irq_tx0_dma_th_enb = 0x100,
+               .irq_tx0_dma_th_enb = 0x110,
+               .irq_tx1_dma_th_enb = 0x120,
+               .irq_tx1_dma_th_enb = 0x130,
+               .irq_usb0_dma_enable = 0x140,
+               .irq_usb1_dma_enable = 0x144,
+               .irq_tx0_frame_th_enb = 0x200,
+               .irq_tx0_frame_th_enb = 0x210,
+               .irq_tx1_frame_th_enb = 0x220,
+               .irq_tx1_frame_th_enb = 0x230,
+               .irq_usb0_frame_enable = 0x240,
+               .irq_usb1_frame_enable = 0x244,
+       }
 };
 
 static const struct platform_device_id musb_dsps_id_table[] = {
index 876787438c2f06bc53552f0f3be189fcf688a33b..33e7832ff2f666614329c7f48f461e9fcba7a25e 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/slab.h>
 
 #include "musb_core.h"
+#include "musb_dma.h"
 
 
 /* MUSB PERIPHERAL status 3-mar-2006:
@@ -417,11 +418,24 @@ static void txstate(struct musb *musb, struct musb_request *req)
                        }
                }
 
-#elif defined(CONFIG_USB_TI_CPPI_DMA)
+#elif defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA)
                /* program endpoint CSR first, then setup DMA */
                csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY);
-               csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE |
-                      MUSB_TXCSR_MODE;
+
+               /* use pio mode for zero byte transfer */
+               if (!request_size) {
+                       csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_TXPKTRDY);
+                       musb_writew(epio, MUSB_TXCSR, csr);
+
+                       csr &= ~(MUSB_TXCSR_DMAMODE);
+                       use_dma = 0;
+               } else {
+                       if (!musb->tx_isoc_sched_enable ||
+                               musb_ep->type != USB_ENDPOINT_XFER_ISOC)
+                               csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE |
+                                       MUSB_TXCSR_MODE;
+               }
+
                musb_writew(epio, MUSB_TXCSR,
                        (MUSB_TXCSR_P_WZC_BITS & ~MUSB_TXCSR_P_UNDERRUN)
                                | csr);
@@ -676,7 +690,8 @@ static void rxstate(struct musb *musb, struct musb_request *req)
                return;
        }
 
-       if (is_cppi_enabled() && is_buffer_mapped(req)) {
+       if ((is_cppi_enabled() || is_cppi41_enabled())
+                       && is_buffer_mapped(req)) {
                struct dma_controller   *c = musb->dma_controller;
                struct dma_channel      *channel = musb_ep->dma;
 
@@ -1932,6 +1947,11 @@ static int musb_gadget_start(struct usb_gadget *g,
                goto err;
        }
 
+       if (musb->port_mode == MUSB_PORT_MODE_HOST) {
+               retval = -EINVAL;
+               goto err;
+       }
+
        pm_runtime_get_sync(musb->controller);
 
        dev_dbg(musb->controller, "registering driver %s\n", driver->function);
index c9c1ac4e075f758ae01eaf0a79b738ee41da0611..2af45a0c8930b35ca645834c157291198c383aca 100644 (file)
@@ -505,8 +505,10 @@ static void ep0_rxstate(struct musb *musb)
                        req->status = -EOVERFLOW;
                        count = len;
                }
-               musb_read_fifo(&musb->endpoints[0], count, buf);
-               req->actual += count;
+               if (count > 0) {
+                       musb_read_fifo(&musb->endpoints[0], count, buf);
+                       req->actual += count;
+               }
                csr = MUSB_CSR0_P_SVDRXPKTRDY;
                if (count < 64 || req->actual == req->length) {
                        musb->ep0_state = MUSB_EP0_STAGE_STATUSIN;
index e9f0fd9ddd2d05284283ee3448785984cc760c10..112900b289e7b53499c9a4a49d2ed75f7bb08630 100644 (file)
@@ -101,6 +101,30 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
                        struct urb *urb, int is_out,
                        u8 *buf, u32 offset, u32 len);
 
+void push_queue(struct musb *musb, struct urb *urb)
+{
+       spin_lock(&musb->gb_lock);
+       list_add_tail(&urb->giveback_list, &musb->gb_list);
+       spin_unlock(&musb->gb_lock);
+}
+
+struct urb *pop_queue(struct musb *musb)
+{
+       struct urb *urb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&musb->gb_lock, flags);
+       if (list_empty(&musb->gb_list)) {
+               spin_unlock_irqrestore(&musb->gb_lock, flags);
+               return NULL;
+       }
+       urb = list_entry(musb->gb_list.next, struct urb, giveback_list);
+       list_del(&urb->giveback_list);
+       spin_unlock_irqrestore(&musb->gb_lock, flags);
+
+       return urb;
+}
+
 /*
  * Clear TX fifo. Needed to avoid BABBLE errors.
  */
@@ -178,7 +202,7 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep)
        /* NOTE: no locks here; caller should lock and select EP */
        txcsr = musb_readw(ep->regs, MUSB_TXCSR);
        txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS;
-       if (is_cppi_enabled())
+       if (is_cppi_enabled() || is_cppi41_enabled())
                txcsr |= MUSB_TXCSR_DMAMODE;
        musb_writew(ep->regs, MUSB_TXCSR, txcsr);
 }
@@ -292,15 +316,17 @@ start:
 
                if (!hw_ep->tx_channel)
                        musb_h_tx_start(hw_ep);
-               else if (is_cppi_enabled() || tusb_dma_omap())
-                       musb_h_tx_dma_start(hw_ep);
+               else if (is_cppi_enabled() || is_cppi41_enabled()
+                               || tusb_dma_omap()) {
+                       if (!musb->tx_isoc_sched_enable ||
+                               qh->type != USB_ENDPOINT_XFER_ISOC)
+                               musb_h_tx_dma_start(hw_ep);
+               }
        }
 }
 
 /* Context: caller owns controller lock, IRQs are blocked */
 static void musb_giveback(struct musb *musb, struct urb *urb, int status)
-__releases(musb->lock)
-__acquires(musb->lock)
 {
        dev_dbg(musb->controller,
                        "complete %p %pF (%d), dev%d ep%d%s, %d/%d\n",
@@ -311,17 +337,16 @@ __acquires(musb->lock)
                        urb->actual_length, urb->transfer_buffer_length
                        );
 
-       usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb);
-       spin_unlock(&musb->lock);
        usb_hcd_giveback_urb(musb_to_hcd(musb), urb, status);
-       spin_lock(&musb->lock);
 }
 
 /* For bulk/interrupt endpoints only */
 static inline void musb_save_toggle(struct musb_qh *qh, int is_in,
                                    struct urb *urb)
 {
+       struct musb             *musb = qh->hw_ep->musb;
        void __iomem            *epio = qh->hw_ep->regs;
+       u8                      curr_toggle;
        u16                     csr;
 
        /*
@@ -329,13 +354,41 @@ static inline void musb_save_toggle(struct musb_qh *qh, int is_in,
         * problems getting toggle correct.
         */
 
-       if (is_in)
+       if (is_in) {
                csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE;
-       else
+               curr_toggle = csr ? 1 : 0;
+
+               /* check if data toggle has gone out of sync */
+               if (is_cppi41_enabled() && qh->hw_ep->rx_channel &&
+                       curr_toggle == qh->hw_ep->prev_toggle) {
+                       dev_dbg(musb->controller,
+                               "Data toggle same as previous (=%d) on ep%d\n",
+                                       curr_toggle, qh->hw_ep->epnum);
+
+                       csr = musb_readw(epio, MUSB_RXCSR);
+                       csr |= MUSB_RXCSR_H_DATATOGGLE |
+                                       MUSB_RXCSR_H_WR_DATATOGGLE;
+                       musb_writew(epio, MUSB_RXCSR, csr);
+                       csr = 1;
+               }
+       } else
                csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE;
 
        usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0);
 }
+/* Used to complete urb giveback */
+void musb_gb_work(struct work_struct *data)
+{
+       struct musb *musb = container_of(data, struct musb, gb_work);
+       struct urb *urb;
+
+       while ((urb = pop_queue(musb)) != 0) {
+               if (urb->status == -EINPROGRESS)
+                       musb_giveback(musb, urb, 0);
+               else
+                       musb_giveback(musb, urb, urb->status);
+       }
+}
 
 /*
  * Advance this hardware endpoint's queue, completing the specified URB and
@@ -349,7 +402,6 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb,
 {
        struct musb_qh          *qh = musb_ep_get_qh(hw_ep, is_in);
        struct musb_hw_ep       *ep = qh->hw_ep;
-       int                     ready = qh->is_ready;
        int                     status;
 
        status = (urb->status == -EINPROGRESS) ? 0 : urb->status;
@@ -366,9 +418,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb,
                break;
        }
 
-       qh->is_ready = 0;
-       musb_giveback(musb, urb, status);
-       qh->is_ready = ready;
+       usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb);
 
        /* reclaim resources (and bandwidth) ASAP; deschedule it, and
         * invalidate qh as soon as list_empty(&hep->urb_list)
@@ -427,6 +477,10 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb,
                    hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh));
                musb_start_urb(musb, is_in, qh);
        }
+
+       /* if URB is successfully completed then giveback in workqueue */
+       push_queue(musb, urb);
+       queue_work(musb->gb_queue, &musb->gb_work);
 }
 
 static u16 musb_h_flush_rxfifo(struct musb_hw_ep *hw_ep, u16 csr)
@@ -435,7 +489,8 @@ static u16 musb_h_flush_rxfifo(struct musb_hw_ep *hw_ep, u16 csr)
         * ignore dma (various models),
         * leave toggle alone (may not have been saved yet)
         */
-       csr |= MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_RXPKTRDY;
+       if (csr & MUSB_RXCSR_RXPKTRDY)
+               csr |= MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_RXPKTRDY;
        csr &= ~(MUSB_RXCSR_H_REQPKT
                | MUSB_RXCSR_H_AUTOREQ
                | MUSB_RXCSR_AUTOCLEAR);
@@ -644,7 +699,8 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
        channel->desired_mode = mode;
        musb_writew(epio, MUSB_TXCSR, csr);
 #else
-       if (!is_cppi_enabled() && !tusb_dma_omap())
+       if (!is_cppi_enabled() && !is_cppi41_enabled()
+                       && !tusb_dma_omap())
                return false;
 
        channel->actual_len = 0;
@@ -664,7 +720,8 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
         */
        wmb();
 
-       if (!dma->channel_program(channel, pkt_size, mode,
+       if (!dma->channel_program(channel, pkt_size |
+                       (qh->hb_mult << 11), mode,
                        urb->transfer_dma + offset, length)) {
                dma->channel_release(channel);
                hw_ep->tx_channel = NULL;
@@ -867,8 +924,8 @@ finish:
                } else {
                        csr = musb_readw(hw_ep->regs, MUSB_RXCSR);
 
-                       if (csr & (MUSB_RXCSR_RXPKTRDY
-                                       | MUSB_RXCSR_DMAENAB
+                       if (csr & (MUSB_RXCSR_RXPKTRDY |
+                               (is_cppi41_enabled() ? 0 : MUSB_RXCSR_DMAENAB)
                                        | MUSB_RXCSR_H_REQPKT))
                                ERR("broken !rx_reinit, ep%d csr %04x\n",
                                                hw_ep->epnum, csr);
@@ -879,7 +936,8 @@ finish:
 
                /* kick things off */
 
-               if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) {
+               if ((is_cppi_enabled() || is_cppi41_enabled()
+                       || tusb_dma_omap()) && dma_channel) {
                        /* Candidate for DMA */
                        dma_channel->actual_len = 0L;
                        qh->segsize = len;
@@ -888,13 +946,21 @@ finish:
                        musb_writew(hw_ep->regs, MUSB_RXCSR, csr);
                        csr = musb_readw(hw_ep->regs, MUSB_RXCSR);
 
+                       /*
+                        * Save the data toggle value which can be compared
+                        * later to see if data toggle goes out of sync
+                        */
+                       hw_ep->prev_toggle = (csr &
+                               MUSB_RXCSR_H_DATATOGGLE) ? 1 : 0;
+
                        /*
                         * Unless caller treats short RX transfers as
                         * errors, we dare not queue multiple transfers.
                         */
                        dma_ok = dma_controller->channel_program(dma_channel,
-                                       packet_sz, !(urb->transfer_flags &
-                                                    URB_SHORT_NOT_OK),
+                                       packet_sz | (qh->hb_mult << 11),
+                                       !(urb->transfer_flags &
+                                               URB_SHORT_NOT_OK),
                                        urb->transfer_dma + offset,
                                        qh->segsize);
                        if (!dma_ok) {
@@ -1417,8 +1483,12 @@ done:
        } else if ((usb_pipeisoc(pipe) || transfer_pending) && dma) {
                if (musb_tx_dma_program(musb->dma_controller, hw_ep, qh, urb,
                                offset, length)) {
-                       if (is_cppi_enabled() || tusb_dma_omap())
-                               musb_h_tx_dma_start(hw_ep);
+                       if (is_cppi_enabled() || is_cppi41_enabled()
+                               || tusb_dma_omap()) {
+                               if (!musb->tx_isoc_sched_enable ||
+                                       qh->type != USB_ENDPOINT_XFER_ISOC)
+                                       musb_h_tx_dma_start(hw_ep);
+                       }
                        return;
                }
        } else  if (tx_csr & MUSB_TXCSR_DMAENAB) {
@@ -1669,9 +1739,13 @@ void musb_host_rx(struct musb *musb, u8 epnum)
                        | MUSB_RXCSR_H_AUTOREQ
                        | MUSB_RXCSR_AUTOCLEAR
                        | MUSB_RXCSR_RXPKTRDY);
+
+               if (is_cppi41_enabled() && usb_pipeisoc(pipe))
+                       val |= MUSB_RXCSR_DMAENAB;
+
                musb_writew(hw_ep->regs, MUSB_RXCSR, val);
 
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA)
                if (usb_pipeisoc(pipe)) {
                        struct usb_iso_packet_descriptor *d;
 
@@ -1684,11 +1758,20 @@ void musb_host_rx(struct musb *musb, u8 epnum)
                        if (d->status != -EILSEQ && d->status != -EOVERFLOW)
                                d->status = 0;
 
+                       done = false;
                        if (++qh->iso_idx >= urb->number_of_packets)
                                done = true;
-                       else
-                               done = false;
-
+                       else if (is_cppi41_enabled()) {
+                               struct dma_controller   *c;
+                               int ret, idx = qh->iso_idx;
+
+                               c = musb->dma_controller;
+                               ret = c->channel_program(dma, qh->maxpacket |
+                                       (qh->hb_mult << 11), 0,
+                                       (u32)urb->transfer_dma +
+                                       urb->iso_frame_desc[idx].offset,
+                                       urb->iso_frame_desc[idx].length);
+                       }
                } else  {
                /* done if urb buffer is full or short packet is recd */
                done = (urb->actual_length + xfer_len >=
@@ -1831,7 +1914,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
                         * adjusted first...
                         */
                        ret = c->channel_program(
-                               dma, qh->maxpacket,
+                               dma, qh->maxpacket | ((qh->hb_mult - 1) << 11),
                                dma->desired_mode, buf, length);
 
                        if (!ret) {
@@ -1893,6 +1976,12 @@ finish:
        urb->actual_length += xfer_len;
        qh->offset += xfer_len;
        if (done) {
+               /* Reset this dma ->actual_len after transfer complete,
+                * if not subsequence dequeue request will take this
+                */
+               if (dma)
+                       dma->actual_len = 0;
+
                if (use_sg)
                        use_sg = false;
 
@@ -2046,6 +2135,8 @@ static int musb_urb_enqueue(
        qh = ret ? NULL : hep->hcpriv;
        if (qh)
                urb->hcpriv = qh;
+
+       INIT_LIST_HEAD(&urb->giveback_list);
        spin_unlock_irqrestore(&musb->lock, flags);
 
        /* DMA mapping was already done, if needed, and this urb is on
@@ -2309,8 +2400,12 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
                        || musb_ep_get_qh(qh->hw_ep, is_in) != qh) {
                int     ready = qh->is_ready;
 
+               usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb);
+
                qh->is_ready = 0;
+               spin_unlock(&musb->lock);
                musb_giveback(musb, urb, 0);
+               spin_lock(&musb->lock);
                qh->is_ready = ready;
 
                /* If nothing else (usually musb_giveback) is using it
@@ -2371,8 +2466,13 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
                 * other transfers, and since !qh->is_ready nothing
                 * will activate any of these as it advances.
                 */
-               while (!list_empty(&hep->urb_list))
-                       musb_giveback(musb, next_urb(qh), -ESHUTDOWN);
+               while (!list_empty(&hep->urb_list)) {
+                       urb = next_urb(qh);
+                       usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb);
+                       spin_unlock(&musb->lock);
+                       musb_giveback(musb, urb, -ESHUTDOWN);
+                       spin_lock(&musb->lock);
+               }
 
                hep->hcpriv = NULL;
                list_del(&qh->ring);
index d7772856bf4af6fb2f00dc2002df188f5f306739..9215ae3da9dcccf3fdbdbb4e6d9b92df2c89d7ae 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/usb/musb-omap.h>
+#include <linux/usb/omap_control_usb.h>
 
 #include "musb_core.h"
 #include "omap2430.h"
@@ -46,7 +47,7 @@ struct omap2430_glue {
        struct platform_device  *musb;
        enum omap_musb_vbus_id_status status;
        struct work_struct      omap_musb_mailbox_work;
-       u32 __iomem             *control_otghs;
+       struct device           *control_otghs;
 };
 #define glue_to_musb(g)                platform_get_drvdata(g->musb)
 
@@ -54,26 +55,6 @@ struct omap2430_glue         *_glue;
 
 static struct timer_list musb_idle_timer;
 
-/**
- * omap4_usb_phy_mailbox - write to usb otg mailbox
- * @glue: struct omap2430_glue *
- * @val: the value to be written to the mailbox
- *
- * On detection of a device (ID pin is grounded), this API should be called
- * to set AVALID, VBUSVALID and ID pin is grounded.
- *
- * When OMAP is connected to a host (OMAP in device mode), this API
- * is called to set AVALID, VBUSVALID and ID pin in high impedance.
- *
- * XXX: This function will be removed once we have a seperate driver for
- * control module
- */
-static void omap4_usb_phy_mailbox(struct omap2430_glue *glue, u32 val)
-{
-       if (glue->control_otghs)
-               writel(val, glue->control_otghs);
-}
-
 static void musb_do_idle(unsigned long _musb)
 {
        struct musb     *musb = (void *)_musb;
@@ -269,7 +250,6 @@ EXPORT_SYMBOL_GPL(omap_musb_mailbox);
 
 static void omap_musb_set_mailbox(struct omap2430_glue *glue)
 {
-       u32 val;
        struct musb *musb = glue_to_musb(glue);
        struct device *dev = musb->controller;
        struct musb_hdrc_platform_data *pdata = dev->platform_data;
@@ -285,8 +265,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
                musb->xceiv->last_event = USB_EVENT_ID;
                if (musb->gadget_driver) {
                        pm_runtime_get_sync(dev);
-                       val = AVALID | VBUSVALID;
-                       omap4_usb_phy_mailbox(glue, val);
+                       omap_control_usb_set_mode(glue->control_otghs,
+                               USB_MODE_HOST);
                        omap2430_musb_set_vbus(musb, 1);
                }
                break;
@@ -299,8 +279,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
                musb->xceiv->last_event = USB_EVENT_VBUS;
                if (musb->gadget_driver)
                        pm_runtime_get_sync(dev);
-               val = IDDIG | AVALID | VBUSVALID;
-               omap4_usb_phy_mailbox(glue, val);
+               omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE);
                break;
 
        case OMAP_MUSB_ID_FLOAT:
@@ -317,8 +296,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
                        if (musb->xceiv->otg->set_vbus)
                                otg_set_vbus(musb->xceiv->otg, 0);
                }
-               val = SESSEND | IDDIG;
-               omap4_usb_phy_mailbox(glue, val);
+               omap_control_usb_set_mode(glue->control_otghs,
+                       USB_MODE_DISCONNECT);
                break;
        default:
                dev_dbg(dev, "ID float\n");
@@ -366,7 +345,12 @@ 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.
         */
-       musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+       if (dev->parent->of_node)
+               musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent,
+                   "usb-phy", 0);
+       else
+               musb->xceiv = devm_usb_get_phy_dev(dev, 0);
+
        if (IS_ERR_OR_NULL(musb->xceiv)) {
                pr_err("HS USB OTG: no transceiver configured\n");
                return -EPROBE_DEFER;
@@ -415,7 +399,6 @@ err1:
 static void omap2430_musb_enable(struct musb *musb)
 {
        u8              devctl;
-       u32             val;
        unsigned long timeout = jiffies + msecs_to_jiffies(1000);
        struct device *dev = musb->controller;
        struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
@@ -425,8 +408,7 @@ static void omap2430_musb_enable(struct musb *musb)
        switch (glue->status) {
 
        case OMAP_MUSB_ID_GROUND:
-               val = AVALID | VBUSVALID;
-               omap4_usb_phy_mailbox(glue, val);
+               omap_control_usb_set_mode(glue->control_otghs, USB_MODE_HOST);
                if (data->interface_type != MUSB_INTERFACE_UTMI)
                        break;
                devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
@@ -445,8 +427,7 @@ static void omap2430_musb_enable(struct musb *musb)
                break;
 
        case OMAP_MUSB_VBUS_VALID:
-               val = IDDIG | AVALID | VBUSVALID;
-               omap4_usb_phy_mailbox(glue, val);
+               omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE);
                break;
 
        default:
@@ -456,14 +437,12 @@ static void omap2430_musb_enable(struct musb *musb)
 
 static void omap2430_musb_disable(struct musb *musb)
 {
-       u32 val;
        struct device *dev = musb->controller;
        struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
 
-       if (glue->status != OMAP_MUSB_UNKNOWN) {
-               val = SESSEND | IDDIG;
-               omap4_usb_phy_mailbox(glue, val);
-       }
+       if (glue->status != OMAP_MUSB_UNKNOWN)
+               omap_control_usb_set_mode(glue->control_otghs,
+                       USB_MODE_DISCONNECT);
 }
 
 static int omap2430_musb_exit(struct musb *musb)
@@ -498,7 +477,6 @@ static int omap2430_probe(struct platform_device *pdev)
        struct omap2430_glue            *glue;
        struct device_node              *np = pdev->dev.of_node;
        struct musb_hdrc_config         *config;
-       struct resource                 *res;
        int                             ret = -ENOMEM;
 
        glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
@@ -521,12 +499,6 @@ static int omap2430_probe(struct platform_device *pdev)
        glue->musb                      = musb;
        glue->status                    = OMAP_MUSB_UNKNOWN;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-
-       glue->control_otghs = devm_request_and_ioremap(&pdev->dev, res);
-       if (glue->control_otghs == NULL)
-               dev_dbg(&pdev->dev, "Failed to obtain control memory\n");
-
        if (np) {
                pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
                if (!pdata) {
@@ -552,17 +524,28 @@ static int omap2430_probe(struct platform_device *pdev)
                }
 
                of_property_read_u32(np, "mode", (u32 *)&pdata->mode);
-               of_property_read_u32(np, "interface_type",
+               of_property_read_u32(np, "interface-type",
                                                (u32 *)&data->interface_type);
-               of_property_read_u32(np, "num_eps", (u32 *)&config->num_eps);
-               of_property_read_u32(np, "ram_bits", (u32 *)&config->ram_bits);
+               of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps);
+               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");
+                       return -ENODEV;
+               }
+       } else {
+               glue->control_otghs = ERR_PTR(-ENODEV);
+       }
        pdata->platform_ops             = &omap2430_ops;
 
        platform_set_drvdata(pdev, glue);
index 8ef656659fcbdbf9d3d98854ce76c880e1414fb1..1b5e83a9840ec4beb35d995ebd082231f44cbba4 100644 (file)
 #define OTG_FORCESTDBY         0x414
 #      define  ENABLEFORCE             (1 << 0)
 
-/*
- * Control Module bit definitions
- * XXX: Will be removed once we have a driver for control module.
- */
-#define        AVALID                          BIT(0)
-#define        BVALID                          BIT(1)
-#define        VBUSVALID                       BIT(2)
-#define        SESSEND                         BIT(3)
-#define        IDDIG                           BIT(4)
 #endif /* __MUSB_OMAP243X_H__ */
index 37962c99ff1e9ace79bd89fdadfe4103649909e6..0442fb4f0efd45ca28892bf3a499706beebc08aa 100644 (file)
@@ -138,4 +138,11 @@ config USB_MV_OTG
 
          To compile this driver as a module, choose M here.
 
+config PALMAS_USB
+       tristate "Palmas USB Transceiver Driver"
+       depends on MFD_PALMAS
+       select USB_OTG_UTILS
+       help
+         Enable this to support the Palmas OTG transceiver
+
 endif # USB || OTG
index a844b8d35d14c2c12be670b92cc9b49f410e9425..7ae90ba8b003680441756620a310ec92a8a14cae 100644 (file)
@@ -22,3 +22,4 @@ fsl_usb2_otg-objs             := fsl_otg.o otg_fsm.o
 obj-$(CONFIG_FSL_USB2_OTG)     += fsl_usb2_otg.o
 obj-$(CONFIG_USB_MXS_PHY)      += mxs-phy.o
 obj-$(CONFIG_USB_MV_OTG)       += mv_otg.o
+obj-$(CONFIG_PALMAS_USB)       += palmas-usb.o
index a3ce24b94a73b1ee4bd43a1cbbcb2483c5bddeaf..7b0e8a8683c71e3b3eb75253190968c11820bb38 100644 (file)
 #include <linux/usb/otg.h>
 #include <linux/usb/nop-usb-xceiv.h>
 #include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
 
 struct nop_usb_xceiv {
-       struct usb_phy          phy;
-       struct device           *dev;
+       struct usb_phy phy;
+       struct device *dev;
+       struct clk *clk;
+       struct regulator *vcc;
+       struct regulator *reset;
+       u32 clk_rate;
+       unsigned int needs_vcc:1;
+       unsigned int needs_reset:1;
 };
 
 static struct platform_device *pd;
@@ -64,6 +73,46 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
        return 0;
 }
 
+static int nop_init(struct usb_phy *phy)
+{
+       struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev);
+
+       if (!IS_ERR(nop->vcc)) {
+               if (regulator_enable(nop->vcc))
+                       dev_err(phy->dev, "Failed to enable power\n");
+       }
+
+       if (!IS_ERR(nop->clk))
+               clk_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");
+       }
+
+       return 0;
+}
+
+static void nop_shutdown(struct usb_phy *phy)
+{
+       struct nop_usb_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");
+       }
+
+       if (!IS_ERR(nop->clk))
+               clk_disable(nop->clk);
+
+       if (!IS_ERR(nop->vcc)) {
+               if (regulator_disable(nop->vcc))
+                       dev_err(phy->dev, "Failed to disable power\n");
+       }
+}
+
 static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
 {
        if (!otg)
@@ -95,39 +144,93 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
 
 static int nop_usb_xceiv_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data;
        struct nop_usb_xceiv    *nop;
        enum usb_phy_type       type = USB_PHY_TYPE_USB2;
        int err;
 
-       nop = kzalloc(sizeof *nop, GFP_KERNEL);
+       nop = devm_kzalloc(&pdev->dev, sizeof *nop, GFP_KERNEL);
        if (!nop)
                return -ENOMEM;
 
-       nop->phy.otg = kzalloc(sizeof *nop->phy.otg, GFP_KERNEL);
-       if (!nop->phy.otg) {
-               kfree(nop);
+       nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof *nop->phy.otg, GFP_KERNEL);
+       if (!nop->phy.otg)
                return -ENOMEM;
-       }
 
-       if (pdata)
+       if (dev->of_node) {
+               struct device_node *node = dev->of_node;
+               u32 clk_rate;
+
+               if (!of_property_read_u32(node, "clock-frequency", &clk_rate))
+                       nop->clk_rate = clk_rate;
+
+               nop->needs_vcc = of_property_read_bool(node, "vcc-supply");
+               nop->needs_reset = of_property_read_bool(node, "reset-supply");
+
+       } else if (pdata) {
                type = pdata->type;
+               nop->clk_rate = pdata->clk_rate;
+               nop->needs_vcc = pdata->needs_vcc;
+               nop->needs_reset = pdata->needs_reset;
+       }
+
+       nop->clk = devm_clk_get(&pdev->dev, "main_clk");
+       if (IS_ERR(nop->clk)) {
+               dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n",
+                                       PTR_ERR(nop->clk));
+       }
+
+       if (!IS_ERR(nop->clk) && nop->clk_rate) {
+               err = clk_set_rate(nop->clk, nop->clk_rate);
+               if (err) {
+                       dev_err(&pdev->dev, "Error setting clock rate\n");
+                       return err;
+               }
+       }
+
+       if (!IS_ERR(nop->clk)) {
+               err = clk_prepare(nop->clk);
+               if (err) {
+                       dev_err(&pdev->dev, "Error preparing clock\n");
+                       return err;
+               }
+       }
+
+       nop->vcc = devm_regulator_get(&pdev->dev, "vcc");
+       if (IS_ERR(nop->vcc)) {
+               dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n",
+                                       PTR_ERR(nop->vcc));
+               if (nop->needs_vcc)
+                       return -EPROBE_DEFER;
+       }
+
+       nop->reset = devm_regulator_get(&pdev->dev, "reset");
+       if (IS_ERR(nop->reset)) {
+               dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n",
+                                       PTR_ERR(nop->reset));
+               if (nop->needs_reset)
+                       return -EPROBE_DEFER;
+       }
 
        nop->dev                = &pdev->dev;
        nop->phy.dev            = nop->dev;
        nop->phy.label          = "nop-xceiv";
        nop->phy.set_suspend    = nop_set_suspend;
+       nop->phy.init           = nop_init;
+       nop->phy.shutdown       = nop_shutdown;
        nop->phy.state          = OTG_STATE_UNDEFINED;
+       nop->phy.type           = type;
 
        nop->phy.otg->phy               = &nop->phy;
        nop->phy.otg->set_host          = nop_set_host;
        nop->phy.otg->set_peripheral    = nop_set_peripheral;
 
-       err = usb_add_phy(&nop->phy, type);
+       err = usb_add_phy_dev(&nop->phy);
        if (err) {
                dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
                        err);
-               goto exit;
+               goto err_add;
        }
 
        platform_set_drvdata(pdev, nop);
@@ -135,9 +238,10 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev)
        ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier);
 
        return 0;
-exit:
-       kfree(nop->phy.otg);
-       kfree(nop);
+
+err_add:
+       if (!IS_ERR(nop->clk))
+               clk_unprepare(nop->clk);
        return err;
 }
 
@@ -145,21 +249,30 @@ static int nop_usb_xceiv_remove(struct platform_device *pdev)
 {
        struct nop_usb_xceiv *nop = platform_get_drvdata(pdev);
 
+       if (!IS_ERR(nop->clk))
+               clk_unprepare(nop->clk);
+
        usb_remove_phy(&nop->phy);
 
        platform_set_drvdata(pdev, NULL);
-       kfree(nop->phy.otg);
-       kfree(nop);
 
        return 0;
 }
 
+static const struct of_device_id nop_xceiv_dt_ids[] = {
+       { .compatible = "usb-nop-xceiv" },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids);
+
 static struct platform_driver nop_usb_xceiv_driver = {
        .probe          = nop_usb_xceiv_probe,
        .remove         = nop_usb_xceiv_remove,
        .driver         = {
                .name   = "nop_usb_xceiv",
                .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(nop_xceiv_dt_ids),
        },
 };
 
index a30c04115115046e9c1c43e4e1bbced59b73e441..e1814397ca3ab9631872a481d0a60c2b77b89afc 100644 (file)
 #include <linux/export.h>
 #include <linux/err.h>
 #include <linux/device.h>
+#include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 #include <linux/usb/otg.h>
 
 static LIST_HEAD(phy_list);
+static LIST_HEAD(phy_bind_list);
 static DEFINE_SPINLOCK(phy_lock);
 
 static struct usb_phy *__usb_find_phy(struct list_head *list,
@@ -35,6 +38,38 @@ static struct usb_phy *__usb_find_phy(struct list_head *list,
        return ERR_PTR(-ENODEV);
 }
 
+static struct usb_phy *__usb_find_phy_dev(struct device *dev,
+       struct list_head *list, u8 index)
+{
+       struct usb_phy_bind *phy_bind = NULL;
+
+       list_for_each_entry(phy_bind, list, list) {
+               if (!(strcmp(phy_bind->dev_name, dev_name(dev))) &&
+                               phy_bind->index == index) {
+                       if (phy_bind->phy)
+                               return phy_bind->phy;
+                       else
+                               return ERR_PTR(-EPROBE_DEFER);
+               }
+       }
+
+       return ERR_PTR(-ENODEV);
+}
+
+static struct usb_phy *__of_usb_find_phy(struct device_node *node)
+{
+       struct usb_phy  *phy;
+
+       list_for_each_entry(phy, &phy_list, head) {
+               if (node != phy->dev->of_node)
+                       continue;
+
+               return phy;
+       }
+
+       return ERR_PTR(-ENODEV);
+}
+
 static void devm_usb_phy_release(struct device *dev, void *res)
 {
        struct usb_phy *phy = *(struct usb_phy **)res;
@@ -110,6 +145,133 @@ err0:
 }
 EXPORT_SYMBOL(usb_get_phy);
 
+ /**
+ * devm_usb_get_phy_by_phandle - find the USB PHY by phandle
+ * @dev - device that requests this phy
+ * @phandle - name of the property holding the phy phandle value
+ * @index - the index of the phy
+ *
+ * Returns the phy driver associated with the given phandle value,
+ * after getting a refcount to it, -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. 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.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
+       const char *phandle, u8 index)
+{
+       struct usb_phy  *phy = ERR_PTR(-ENOMEM), **ptr;
+       unsigned long   flags;
+       struct device_node *node;
+
+       if (!dev->of_node) {
+               dev_dbg(dev, "device does not have a device node entry\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       node = of_parse_phandle(dev->of_node, phandle, index);
+       if (!node) {
+               dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
+                       dev->of_node->full_name);
+               return ERR_PTR(-ENODEV);
+       }
+
+       ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr) {
+               dev_dbg(dev, "failed to allocate memory for devres\n");
+               goto err0;
+       }
+
+       spin_lock_irqsave(&phy_lock, flags);
+
+       phy = __of_usb_find_phy(node);
+       if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) {
+               phy = ERR_PTR(-EPROBE_DEFER);
+               devres_free(ptr);
+               goto err1;
+       }
+
+       *ptr = phy;
+       devres_add(dev, ptr);
+
+       get_device(phy->dev);
+
+err1:
+       spin_unlock_irqrestore(&phy_lock, flags);
+
+err0:
+       of_node_put(node);
+
+       return phy;
+}
+EXPORT_SYMBOL(devm_usb_get_phy_by_phandle);
+
+/**
+ * usb_get_phy_dev - find the USB PHY
+ * @dev - device that requests this phy
+ * @index - the index of the phy
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy.  The caller is responsible for
+ * calling usb_put_phy() to release that count.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index)
+{
+       struct usb_phy  *phy = NULL;
+       unsigned long   flags;
+
+       spin_lock_irqsave(&phy_lock, flags);
+
+       phy = __usb_find_phy_dev(dev, &phy_bind_list, index);
+       if (IS_ERR(phy)) {
+               pr_err("unable to find transceiver\n");
+               goto err0;
+       }
+
+       get_device(phy->dev);
+
+err0:
+       spin_unlock_irqrestore(&phy_lock, flags);
+
+       return phy;
+}
+EXPORT_SYMBOL(usb_get_phy_dev);
+
+/**
+ * devm_usb_get_phy_dev - find the USB PHY using device ptr and index
+ * @dev - device that requests this phy
+ * @index - the index of the phy
+ *
+ * Gets the phy using usb_get_phy_dev(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index)
+{
+       struct usb_phy **ptr, *phy;
+
+       ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return NULL;
+
+       phy = usb_get_phy_dev(dev, index);
+       if (!IS_ERR(phy)) {
+               *ptr = phy;
+               devres_add(dev, ptr);
+       } else
+               devres_free(ptr);
+
+       return phy;
+}
+EXPORT_SYMBOL(devm_usb_get_phy_dev);
+
 /**
  * devm_usb_put_phy - release the USB PHY
  * @dev - device that wants to release this phy
@@ -184,6 +346,36 @@ out:
 }
 EXPORT_SYMBOL(usb_add_phy);
 
+/**
+ * usb_add_phy_dev - declare the USB PHY
+ * @x: the USB phy to be used; or NULL
+ *
+ * This call is exclusively for use by phy drivers, which
+ * coordinate the activities of drivers for host and peripheral
+ * controllers, and in some cases for VBUS current regulation.
+ */
+int usb_add_phy_dev(struct usb_phy *x)
+{
+       struct usb_phy_bind *phy_bind;
+       unsigned long flags;
+
+       if (!x->dev) {
+               dev_err(x->dev, "no device provided for PHY\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&phy_lock, flags);
+       list_for_each_entry(phy_bind, &phy_bind_list, list)
+               if (!(strcmp(phy_bind->phy_dev_name, dev_name(x->dev))))
+                       phy_bind->phy = x;
+
+       list_add_tail(&x->head, &phy_list);
+
+       spin_unlock_irqrestore(&phy_lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(usb_add_phy_dev);
+
 /**
  * usb_remove_phy - remove the OTG PHY
  * @x: the USB OTG PHY to be removed;
@@ -193,14 +385,55 @@ EXPORT_SYMBOL(usb_add_phy);
 void usb_remove_phy(struct usb_phy *x)
 {
        unsigned long   flags;
+       struct usb_phy_bind *phy_bind;
 
        spin_lock_irqsave(&phy_lock, flags);
-       if (x)
+       if (x) {
+               list_for_each_entry(phy_bind, &phy_bind_list, list)
+                       if (phy_bind->phy == x)
+                               phy_bind->phy = NULL;
                list_del(&x->head);
+       }
        spin_unlock_irqrestore(&phy_lock, flags);
 }
 EXPORT_SYMBOL(usb_remove_phy);
 
+/**
+ * usb_bind_phy - bind the phy and the controller that uses the phy
+ * @dev_name: the device name of the device that will bind to the phy
+ * @index: index to specify the port number
+ * @phy_dev_name: the device name of the phy
+ *
+ * Fills the phy_bind structure with the dev_name and phy_dev_name. This will
+ * be used when the phy driver registers the phy and when the controller
+ * requests this phy.
+ *
+ * To be used by platform specific initialization code.
+ */
+int __init usb_bind_phy(const char *dev_name, u8 index,
+                               const char *phy_dev_name)
+{
+       struct usb_phy_bind *phy_bind;
+       unsigned long flags;
+
+       phy_bind = kzalloc(sizeof(*phy_bind), GFP_KERNEL);
+       if (!phy_bind) {
+               pr_err("phy_bind(): No memory for phy_bind");
+               return -ENOMEM;
+       }
+
+       phy_bind->dev_name = dev_name;
+       phy_bind->phy_dev_name = phy_dev_name;
+       phy_bind->index = index;
+
+       spin_lock_irqsave(&phy_lock, flags);
+       list_add_tail(&phy_bind->list, &phy_bind_list);
+       spin_unlock_irqrestore(&phy_lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(usb_bind_phy);
+
 const char *otg_state_string(enum usb_otg_state state)
 {
        switch (state) {
diff --git a/drivers/usb/otg/palmas-usb.c b/drivers/usb/otg/palmas-usb.c
new file mode 100644 (file)
index 0000000..7592e2d
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Palmas USB transceiver driver
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * Based on twl6030_usb.c
+ *
+ * Author: Hema HK <hemahk@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/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/usb/omap_usb.h>
+#include <linux/usb/dwc3-omap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mfd/palmas.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+static int palmas_usb_read(struct palmas *palmas, unsigned int reg,
+               unsigned int *dest)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_USB_OTG_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_USB_OTG_BASE, reg);
+
+       return regmap_read(palmas->regmap[slave], addr, dest);
+}
+
+static int palmas_usb_write(struct palmas *palmas, unsigned int reg,
+               unsigned int data)
+{
+       unsigned int addr;
+       int slave;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_USB_OTG_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_USB_OTG_BASE, reg);
+
+       return regmap_write(palmas->regmap[slave], addr, data);
+}
+
+static void palmas_usb_wakeup(struct palmas *palmas, int enable)
+{
+       if (enable)
+               palmas_usb_write(palmas, PALMAS_USB_WAKEUP,
+                               PALMAS_USB_WAKEUP_ID_WK_UP_COMP);
+       else
+               palmas_usb_write(palmas, PALMAS_USB_WAKEUP, 0);
+}
+
+static ssize_t palmas_usb_vbus_show(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       unsigned long           flags;
+       int                     ret = -EINVAL;
+       struct palmas_usb       *palmas_usb = dev_get_drvdata(dev);
+
+       spin_lock_irqsave(&palmas_usb->lock, flags);
+
+       switch (palmas_usb->linkstat) {
+       case OMAP_DWC3_VBUS_VALID:
+              ret = snprintf(buf, PAGE_SIZE, "vbus\n");
+              break;
+       case OMAP_DWC3_ID_GROUND:
+              ret = snprintf(buf, PAGE_SIZE, "id\n");
+              break;
+       case OMAP_DWC3_ID_FLOAT:
+       case OMAP_DWC3_VBUS_OFF:
+              ret = snprintf(buf, PAGE_SIZE, "none\n");
+              break;
+       default:
+              ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n");
+       }
+       spin_unlock_irqrestore(&palmas_usb->lock, flags);
+
+       return ret;
+}
+static DEVICE_ATTR(vbus, 0444, palmas_usb_vbus_show, NULL);
+
+static irqreturn_t palmas_vbus_wakeup_irq(int irq, void *_palmas_usb)
+{
+       struct palmas_usb *palmas_usb = _palmas_usb;
+       enum omap_dwc3_vbus_id_status status = OMAP_DWC3_UNKNOWN;
+       int slave;
+       unsigned int vbus_line_state, addr;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_INTERRUPT_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_INTERRUPT_BASE,
+                       PALMAS_INT3_LINE_STATE);
+
+       regmap_read(palmas_usb->palmas->regmap[slave], addr, &vbus_line_state);
+
+       if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
+               if (palmas_usb->linkstat != OMAP_DWC3_VBUS_VALID) {
+                       if (!IS_ERR_OR_NULL(palmas_usb->vbus_reg))
+                               regulator_enable(palmas_usb->vbus_reg);
+                       status = OMAP_DWC3_VBUS_VALID;
+               } else {
+                       dev_dbg(palmas_usb->dev,
+                               "Spurious connect event detected\n");
+               }
+       } else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
+               if (palmas_usb->linkstat == OMAP_DWC3_VBUS_VALID) {
+                       if (!IS_ERR_OR_NULL(palmas_usb->vbus_reg))
+                               regulator_disable(palmas_usb->vbus_reg);
+                       status = OMAP_DWC3_VBUS_OFF;
+               } else {
+                       dev_dbg(palmas_usb->dev,
+                               "Spurious disconnect event detected\n");
+               }
+       }
+
+       palmas_usb->linkstat = status;
+       if (status != OMAP_DWC3_UNKNOWN) {
+               return dwc3_omap_mailbox(status);
+       }
+
+       return IRQ_NONE;
+}
+
+static irqreturn_t palmas_id_wakeup_irq(int irq, void *_palmas_usb)
+{
+       enum omap_dwc3_vbus_id_status status = OMAP_DWC3_UNKNOWN;
+       unsigned int            set;
+       struct palmas_usb       *palmas_usb = _palmas_usb;
+
+       palmas_usb_read(palmas_usb->palmas, PALMAS_USB_ID_INT_LATCH_SET, &set);
+
+       if (set & PALMAS_USB_ID_INT_SRC_ID_GND) {
+               if (!IS_ERR_OR_NULL(palmas_usb->vbus_reg))
+                       regulator_enable(palmas_usb->vbus_reg);
+               palmas_usb_write(palmas_usb->palmas,
+                                       PALMAS_USB_ID_INT_EN_HI_SET,
+                                       PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT);
+               palmas_usb_write(palmas_usb->palmas,
+                                       PALMAS_USB_ID_INT_EN_HI_CLR,
+                                       PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
+               status = OMAP_DWC3_ID_GROUND;
+       } else if (set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) {
+               palmas_usb_write(palmas_usb->palmas,
+                                       PALMAS_USB_ID_INT_EN_HI_SET,
+                                       PALMAS_USB_ID_INT_EN_HI_SET_ID_GND);
+               palmas_usb_write(palmas_usb->palmas,
+                                       PALMAS_USB_ID_INT_EN_HI_CLR,
+                                       PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
+               if (!IS_ERR_OR_NULL(palmas_usb->vbus_reg))
+                       regulator_disable(palmas_usb->vbus_reg);
+               status = OMAP_DWC3_ID_FLOAT;
+       }
+
+       palmas_usb->linkstat = status;
+       if (status != OMAP_DWC3_UNKNOWN) {
+               return dwc3_omap_mailbox(status);
+       }
+
+       return IRQ_NONE;
+}
+
+static int palmas_enable_irq(struct palmas_usb *palmas_usb)
+{
+       int ret;
+
+       palmas_usb_write(palmas_usb->palmas, PALMAS_USB_VBUS_CTRL_SET,
+                       PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP);
+
+       palmas_usb_write(palmas_usb->palmas, PALMAS_USB_ID_CTRL_SET,
+                       PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP);
+
+       palmas_usb_write(palmas_usb->palmas, PALMAS_USB_ID_INT_EN_HI_SET,
+                       PALMAS_USB_ID_INT_EN_HI_SET_ID_GND);
+
+       ret = palmas_vbus_wakeup_irq(palmas_usb->irq4, palmas_usb);
+
+       if (palmas_usb->linkstat == OMAP_DWC3_UNKNOWN)
+               ret = palmas_id_wakeup_irq(palmas_usb->irq2, palmas_usb);
+
+       return ret;
+}
+
+static void palmas_set_vbus_work(struct work_struct *data)
+{
+       struct palmas_usb *palmas_usb = container_of(data, struct palmas_usb,
+                                                               set_vbus_work);
+
+       if (IS_ERR_OR_NULL(palmas_usb->vbus_reg)) {
+               dev_err(palmas_usb->dev, "invalid regulator\n");
+               return;
+       }
+
+       /*
+        * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1
+        * register. This enables boost mode.
+        */
+
+       if (palmas_usb->vbus_enable)
+               regulator_enable(palmas_usb->vbus_reg);
+       else
+               regulator_disable(palmas_usb->vbus_reg);
+}
+
+static int palmas_set_vbus(struct phy_companion *comparator, bool enabled)
+{
+       struct palmas_usb *palmas_usb = comparator_to_palmas(comparator);
+
+       palmas_usb->vbus_enable = enabled;
+       schedule_work(&palmas_usb->set_vbus_work);
+
+       return 0;
+}
+
+static int palmas_start_srp(struct phy_companion *comparator)
+{
+       struct palmas_usb *palmas_usb = comparator_to_palmas(comparator);
+
+       palmas_usb_write(palmas_usb->palmas, PALMAS_USB_VBUS_CTRL_SET,
+                       PALMAS_USB_VBUS_CTRL_SET_VBUS_DISCHRG |
+                       PALMAS_USB_VBUS_CTRL_SET_VBUS_IADP_SINK);
+       palmas_usb_write(palmas_usb->palmas, PALMAS_USB_VBUS_CTRL_SET,
+                       PALMAS_USB_VBUS_CTRL_SET_VBUS_CHRG_VSYS |
+                       PALMAS_USB_VBUS_CTRL_SET_VBUS_IADP_SINK);
+
+       mdelay(100);
+
+       palmas_usb_write(palmas_usb->palmas, PALMAS_USB_VBUS_CTRL_CLR,
+                       PALMAS_USB_VBUS_CTRL_SET_VBUS_CHRG_VSYS |
+                       PALMAS_USB_VBUS_CTRL_SET_VBUS_CHRG_VSYS);
+
+       return 0;
+}
+
+static void palmas_dt_to_pdata(struct device_node *node,
+               struct palmas_usb_platform_data *pdata)
+{
+       pdata->no_control_vbus = of_property_read_bool(node,
+                                       "ti,no_control_vbus");
+       pdata->wakeup = of_property_read_bool(node, "ti,wakeup");
+}
+
+static int palmas_usb_probe(struct platform_device *pdev)
+{
+       u32 ret;
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+       struct palmas_usb_platform_data *pdata = pdev->dev.platform_data;
+       struct device_node *node = pdev->dev.of_node;
+       struct palmas_usb *palmas_usb;
+       int status;
+
+       if(node && !pdata) {
+               pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+
+               if (!pdata)
+                       return -ENOMEM;
+
+               palmas_dt_to_pdata(node, pdata);
+       }
+
+       if (!pdata)
+               return -EINVAL;
+
+       palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL);
+       if (!palmas_usb)
+               return -ENOMEM;
+
+       palmas->usb             = palmas_usb;
+       palmas_usb->palmas      = palmas;
+
+       palmas_usb->dev         = &pdev->dev;
+
+       palmas_usb->irq1 = regmap_irq_get_virq(palmas->irq_data,
+                                               PALMAS_ID_OTG_IRQ);
+       palmas_usb->irq2 = regmap_irq_get_virq(palmas->irq_data,
+                                               PALMAS_ID_IRQ);
+       palmas_usb->irq3 = regmap_irq_get_virq(palmas->irq_data,
+                                               PALMAS_VBUS_OTG_IRQ);
+       palmas_usb->irq4 = regmap_irq_get_virq(palmas->irq_data,
+                                               PALMAS_VBUS_IRQ);
+
+       palmas_usb->comparator.set_vbus = palmas_set_vbus;
+       palmas_usb->comparator.start_srp = palmas_start_srp;
+
+       ret = omap_usb2_set_comparator(&palmas_usb->comparator);
+       if (ret == -ENODEV) {
+               dev_dbg(&pdev->dev, "phy not ready, deferring probe");
+               return -EPROBE_DEFER;
+       }
+
+       palmas_usb_wakeup(palmas, pdata->wakeup);
+
+       /* init spinlock for workqueue */
+       spin_lock_init(&palmas_usb->lock);
+
+       if (!pdata->no_control_vbus) {
+               palmas_usb->vbus_reg = devm_regulator_get(&pdev->dev, "vbus");
+               if (IS_ERR(palmas_usb->vbus_reg)) {
+                       dev_err(&pdev->dev, "vbus init failed\n");
+                       return PTR_ERR(palmas_usb->vbus_reg);
+               }
+       }
+
+       platform_set_drvdata(pdev, palmas_usb);
+
+       if (device_create_file(&pdev->dev, &dev_attr_vbus))
+               dev_warn(&pdev->dev, "could not create sysfs file\n");
+
+       /* init spinlock for workqueue */
+       spin_lock_init(&palmas_usb->lock);
+
+       INIT_WORK(&palmas_usb->set_vbus_work, palmas_set_vbus_work);
+
+       status = devm_request_threaded_irq(palmas_usb->dev, palmas_usb->irq2,
+                       NULL, palmas_id_wakeup_irq,
+                       IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+                       "palmas_usb", palmas_usb);
+       if (status < 0) {
+               dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
+                                       palmas_usb->irq2, status);
+               goto fail_irq;
+       }
+
+       status = devm_request_threaded_irq(palmas_usb->dev, palmas_usb->irq4,
+                       NULL, palmas_vbus_wakeup_irq,
+                       IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+                       "palmas_usb", palmas_usb);
+       if (status < 0) {
+               dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
+                                       palmas_usb->irq4, status);
+               goto fail_irq;
+       }
+
+       dev_info(&pdev->dev, "Initialized Palmas USB module\n");
+
+       status = palmas_enable_irq(palmas_usb);
+       if (status < 0) {
+               dev_dbg(&pdev->dev, "enable irq failed\n");
+               goto fail_irq;
+       }
+
+       return 0;
+
+fail_irq:
+       cancel_work_sync(&palmas_usb->set_vbus_work);
+       device_remove_file(palmas_usb->dev, &dev_attr_vbus);
+
+       return status;
+}
+
+static int palmas_usb_remove(struct platform_device *pdev)
+{
+       struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
+
+       device_remove_file(palmas_usb->dev, &dev_attr_vbus);
+       cancel_work_sync(&palmas_usb->set_vbus_work);
+
+       return 0;
+}
+
+static struct of_device_id of_palmas_match_tbl[] = {
+       { .compatible = "ti,palmas-usb", },
+       { /* end */ }
+};
+
+static struct platform_driver palmas_usb_driver = {
+       .probe = palmas_usb_probe,
+       .remove = palmas_usb_remove,
+       .driver = {
+               .name = "palmas-usb",
+               .of_match_table = of_palmas_match_tbl,
+               .owner = THIS_MODULE,
+       },
+};
+
+module_platform_driver(palmas_usb_driver);
+
+MODULE_ALIAS("platform:palmas-usb");
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("Palmas USB transceiver driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
index 0a701938ab535baa75d58d193a8c6441e1a5afde..a994715a3101978a916bea3f4d3d1e1c38d72f7d 100644 (file)
@@ -610,6 +610,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
        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;
 
        otg->phy                = &twl->phy;
@@ -624,7 +625,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "ldo init failed\n");
                return err;
        }
-       usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2);
+       usb_add_phy_dev(&twl->phy);
 
        platform_set_drvdata(pdev, twl);
        if (device_create_file(&pdev->dev, &dev_attr_vbus))
index 5de6e7f39f9cf4fd41faaf93bdb722b6420e9d3c..6566eaea004473600e4d381747533f3ed0816c64 100644 (file)
@@ -8,12 +8,33 @@ config OMAP_USB2
        tristate "OMAP USB2 PHY Driver"
        depends on ARCH_OMAP2PLUS
        select USB_OTG_UTILS
+       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 OMAP_USB3
+       tristate "OMAP USB3 PHY Driver"
+       select USB_OTG_UTILS
+       select OMAP_CONTROL_USB
+       help
+         Enable this to support the USB3 PHY that is part of SOC. This
+         driver takes care of all the PHY functionality apart from comparator.
+         This driver interacts with the "OMAP Control USB Driver" to power
+         on/off the PHY.
+
+config OMAP_CONTROL_USB
+       tristate "OMAP CONTROL USB Driver"
+       depends on ARCH_OMAP2PLUS
+       help
+         Enable this to add support for the USB part present in the control
+         module. This driver has API to power on the USB2 PHY and to write to
+         the mailbox. The mailbox is present only in omap4 and the register to
+         power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
+         additional register to power on USB3 PHY.
+
 config USB_ISP1301
        tristate "NXP ISP1301 USB transceiver support"
        depends on USB || USB_GADGET
index 1a579a860a037b3b9226839c0d5fa7981a5d0687..6a072986b9bd935d1e05627e2fa4504815cc26a5 100644 (file)
@@ -5,6 +5,8 @@
 ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG
 
 obj-$(CONFIG_OMAP_USB2)                        += omap-usb2.o
+obj-$(CONFIG_OMAP_USB3)                        += omap-usb3.o
+obj-$(CONFIG_OMAP_CONTROL_USB)         += omap-control-usb.o
 obj-$(CONFIG_USB_ISP1301)              += isp1301.o
 obj-$(CONFIG_MV_U3D_PHY)               += mv_u3d_phy.o
 obj-$(CONFIG_USB_EHCI_TEGRA)   += tegra_usb_phy.o
diff --git a/drivers/usb/phy/omap-control-usb.c b/drivers/usb/phy/omap-control-usb.c
new file mode 100644 (file)
index 0000000..5323b71
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * omap-control-usb.c - The USB part of control module.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/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
+ * @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.
+ */
+void omap_control_usb3_phy_power(struct device *dev, bool on)
+{
+       u32 val;
+       unsigned long rate;
+       struct omap_control_usb *control_usb = dev_get_drvdata(dev);
+
+       if (control_usb->type != OMAP_CTRL_DEV_TYPE2)
+               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;
+       }
+
+       writel(val, control_usb->phy_power);
+}
+EXPORT_SYMBOL_GPL(omap_control_usb3_phy_power);
+
+/**
+ * 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->dev_conf);
+
+       if (on)
+               val &= ~OMAP_CTRL_DEV_PHY_PD;
+       else
+               val |= OMAP_CTRL_DEV_PHY_PD;
+
+       writel(val, control_usb->dev_conf);
+}
+EXPORT_SYMBOL_GPL(omap_control_usb_phy_power);
+
+/**
+ * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
+ * @ctrl_usb: struct omap_control_usb *
+ *
+ * Writes to the mailbox register to notify the usb core that a usb
+ * device has been connected.
+ */
+static void omap_control_usb_host_mode(struct omap_control_usb *ctrl_usb)
+{
+       u32 val;
+
+       val = readl(ctrl_usb->otghs_control);
+       val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND);
+       val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;
+       writel(val, ctrl_usb->otghs_control);
+}
+
+/**
+ * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high
+ * impedance
+ * @ctrl_usb: struct omap_control_usb *
+ *
+ * Writes to the mailbox register to notify the usb core that it has been
+ * connected to a usb host.
+ */
+static void omap_control_usb_device_mode(struct omap_control_usb *ctrl_usb)
+{
+       u32 val;
+
+       val = readl(ctrl_usb->otghs_control);
+       val &= ~OMAP_CTRL_DEV_SESSEND;
+       val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID |
+               OMAP_CTRL_DEV_VBUSVALID;
+       writel(val, ctrl_usb->otghs_control);
+}
+
+/**
+ * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
+ * impedance
+ * @ctrl_usb: struct omap_control_usb *
+ *
+ * Writes to the mailbox register to notify the usb core it's now in
+ * disconnected state.
+ */
+static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb)
+{
+       u32 val;
+
+       val = readl(ctrl_usb->otghs_control);
+       val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID);
+       val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND;
+       writel(val, ctrl_usb->otghs_control);
+}
+
+/**
+ * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode
+ * or device mode or to denote disconnected state
+ * @dev: the control module device
+ * @mode: The mode to which usb should be configured
+ *
+ * This is an API to write to the mailbox register to notify the usb core that
+ * a usb device has been connected.
+ */
+void omap_control_usb_set_mode(struct device *dev,
+       enum omap_control_usb_mode mode)
+{
+       struct omap_control_usb *ctrl_usb;
+
+       if (IS_ERR(dev) || control_usb->type != OMAP_CTRL_DEV_TYPE1)
+               return;
+
+       ctrl_usb = dev_get_drvdata(dev);
+
+       switch (mode) {
+       case USB_MODE_HOST:
+               omap_control_usb_host_mode(ctrl_usb);
+               break;
+       case USB_MODE_DEVICE:
+               omap_control_usb_device_mode(ctrl_usb);
+               break;
+       case USB_MODE_DISCONNECT:
+               omap_control_usb_set_sessionend(ctrl_usb);
+               break;
+       default:
+               dev_vdbg(dev, "invalid omap control usb mode\n");
+       }
+}
+EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
+
+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 = pdev->dev.platform_data;
+
+       control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),
+               GFP_KERNEL);
+       if (!control_usb) {
+               dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
+               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_request_and_ioremap(&pdev->dev, res);
+       if (!control_usb->dev_conf) {
+               dev_err(&pdev->dev, "Failed to obtain io memory\n");
+               return -EADDRNOTAVAIL;
+       }
+
+       if (control_usb->type == OMAP_CTRL_DEV_TYPE1) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                       "otghs_control");
+               control_usb->otghs_control = devm_request_and_ioremap(
+                       &pdev->dev, res);
+               if (!control_usb->otghs_control) {
+                       dev_err(&pdev->dev, "Failed to obtain io memory\n");
+                       return -EADDRNOTAVAIL;
+               }
+       }
+
+       if (control_usb->type == OMAP_CTRL_DEV_TYPE2) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                       "phy_power_usb");
+               control_usb->phy_power = devm_request_and_ioremap(
+                       &pdev->dev, res);
+               if (!control_usb->phy_power) {
+                       dev_dbg(&pdev->dev, "Failed to obtain io memory\n");
+                       return -EADDRNOTAVAIL;
+               }
+
+               control_usb->sys_clk = devm_clk_get(control_usb->dev,
+                       "sys_clkin");
+               if (IS_ERR(control_usb->sys_clk)) {
+                       pr_err("%s: unable to get sys_clkin\n", __func__);
+                       return -EINVAL;
+               }
+       }
+
+
+       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         = {
+               .name   = "omap-control-usb",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(omap_control_usb_id_table),
+       },
+};
+
+static int __init omap_control_usb_init(void)
+{
+       return platform_driver_register(&omap_control_usb_driver);
+}
+subsys_initcall(omap_control_usb_init);
+
+static void __exit omap_control_usb_exit(void)
+{
+       platform_driver_unregister(&omap_control_usb_driver);
+}
+module_exit(omap_control_usb_exit);
+
+MODULE_ALIAS("platform: omap_control_usb");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP Control Module USB Driver");
+MODULE_LICENSE("GPL v2");
index 26ae8f49225c9b2803fe6624f1ec1bfa488d3018..844ab68f08d04d6600594fb74fc56a3225b079b2 100644 (file)
@@ -27,6 +27,7 @@
 #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
@@ -52,29 +53,6 @@ int omap_usb2_set_comparator(struct phy_companion *comparator)
 }
 EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
 
-/**
- * omap_usb_phy_power - power on/off the phy using control module reg
- * @phy: struct omap_usb *
- * @on: 0 or 1, based on powering on or off the PHY
- *
- * XXX: Remove this function once control module driver gets merged
- */
-static void omap_usb_phy_power(struct omap_usb *phy, int on)
-{
-       u32 val;
-
-       if (on) {
-               val = readl(phy->control_dev);
-               if (val & PHY_PD) {
-                       writel(~PHY_PD, phy->control_dev);
-                       /* XXX: add proper documentation for this delay */
-                       mdelay(200);
-               }
-       } else {
-               writel(PHY_PD, phy->control_dev);
-       }
-}
-
 static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
 {
        struct omap_usb *phy = phy_to_omapusb(otg->phy);
@@ -124,7 +102,7 @@ static int omap_usb2_suspend(struct usb_phy *x, int suspend)
        struct omap_usb *phy = phy_to_omapusb(x);
 
        if (suspend && !phy->is_suspended) {
-               omap_usb_phy_power(phy, 0);
+               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) {
@@ -134,7 +112,7 @@ static int omap_usb2_suspend(struct usb_phy *x, int suspend)
                                                                        ret);
                        return ret;
                }
-               omap_usb_phy_power(phy, 1);
+               omap_control_usb_phy_power(phy->control_dev, 1);
                phy->is_suspended = 0;
        }
 
@@ -145,7 +123,6 @@ static int omap_usb2_probe(struct platform_device *pdev)
 {
        struct omap_usb                 *phy;
        struct usb_otg                  *otg;
-       struct resource                 *res;
 
        phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
        if (!phy) {
@@ -165,17 +142,16 @@ static int omap_usb2_probe(struct platform_device *pdev)
        phy->phy.label          = "omap-usb2";
        phy->phy.set_suspend    = omap_usb2_suspend;
        phy->phy.otg            = otg;
+       phy->phy.type           = USB_PHY_TYPE_USB2;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-
-       phy->control_dev = devm_request_and_ioremap(&pdev->dev, res);
-       if (phy->control_dev == NULL) {
-               dev_err(&pdev->dev, "Failed to obtain io memory\n");
-               return -ENXIO;
+       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_usb_phy_power(phy, 0);
+       omap_control_usb_phy_power(phy->control_dev, 0);
 
        otg->set_host           = omap_usb_set_host;
        otg->set_peripheral     = omap_usb_set_peripheral;
@@ -190,7 +166,13 @@ static int omap_usb2_probe(struct platform_device *pdev)
        }
        clk_prepare(phy->wkupclk);
 
-       usb_add_phy(&phy->phy, USB_PHY_TYPE_USB2);
+       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);
 
@@ -204,6 +186,8 @@ 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;
@@ -217,6 +201,8 @@ static int omap_usb2_runtime_suspend(struct 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;
 }
@@ -228,9 +214,25 @@ static int omap_usb2_runtime_resume(struct device *dev)
        struct omap_usb *phy = platform_get_drvdata(pdev);
 
        ret = clk_enable(phy->wkupclk);
-       if (ret < 0)
+       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;
 }
 
diff --git a/drivers/usb/phy/omap-usb3.c b/drivers/usb/phy/omap-usb3.c
new file mode 100644 (file)
index 0000000..fadc0c2
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * omap-usb3 - USB PHY, talking to dwc3 controller in OMAP.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/usb/omap_usb.h>
+#include <linux/of.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>
+
+#define        NUM_SYS_CLKS            5
+#define        PLL_STATUS              0x00000004
+#define        PLL_GO                  0x00000008
+#define        PLL_CONFIGURATION1      0x0000000C
+#define        PLL_CONFIGURATION2      0x00000010
+#define        PLL_CONFIGURATION3      0x00000014
+#define        PLL_CONFIGURATION4      0x00000020
+
+#define        PLL_REGM_MASK           0x001FFE00
+#define        PLL_REGM_SHIFT          0x9
+#define        PLL_REGM_F_MASK         0x0003FFFF
+#define        PLL_REGM_F_SHIFT        0x0
+#define        PLL_REGN_MASK           0x000001FE
+#define        PLL_REGN_SHIFT          0x1
+#define        PLL_SELFREQDCO_MASK     0x0000000E
+#define        PLL_SELFREQDCO_SHIFT    0x1
+#define        PLL_SD_MASK             0x0003FC00
+#define        PLL_SD_SHIFT            0x9
+#define        SET_PLL_GO              0x1
+#define        PLL_TICOPWDN            0x10000
+#define        PLL_LOCK                0x2
+#define        PLL_IDLE                0x1
+
+/*
+ * This is an Empirical value that works, need to confirm the actual
+ * value required for the USB3PHY_PLL_CONFIGURATION2.PLL_IDLE status
+ * to be correctly reflected in the USB3PHY_PLL_STATUS register.
+ */
+# define PLL_IDLE_TIME  100;
+
+enum sys_clk_rate {
+       CLK_RATE_UNDEFINED = -1,
+       CLK_RATE_12MHZ,
+       CLK_RATE_16MHZ,
+       CLK_RATE_19MHZ,
+       CLK_RATE_26MHZ,
+       CLK_RATE_38MHZ
+};
+
+static struct usb_dpll_params omap_usb3_dpll_params[NUM_SYS_CLKS] = {
+       {1250, 5, 4, 20, 0},            /* 12 MHz */
+       {3125, 20, 4, 20, 0},           /* 16.8 MHz */
+       {1172, 8, 4, 20, 65537},        /* 19.2 MHz */
+       {1250, 12, 4, 20, 0},           /* 26 MHz */
+       {3125, 47, 4, 20, 92843},       /* 38.4 MHz */
+};
+
+static int omap_usb3_suspend(struct usb_phy *x, int suspend)
+{
+       struct omap_usb *phy = phy_to_omapusb(x);
+       int     val;
+       int timeout = PLL_IDLE_TIME;
+
+       if (suspend && !phy->is_suspended) {
+               val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
+               val |= PLL_IDLE;
+               omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
+
+               do {
+                       val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
+                       if (val & PLL_TICOPWDN)
+                               break;
+                       udelay(1);
+               } while (--timeout);
+
+               omap_control_usb3_phy_power(phy->control_dev, 0);
+
+               phy->is_suspended       = 1;
+       } else if (!suspend && phy->is_suspended) {
+               phy->is_suspended       = 0;
+
+               val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
+               val &= ~PLL_IDLE;
+               omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
+
+               do {
+                       val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
+                       if (!(val & PLL_TICOPWDN))
+                               break;
+                       udelay(1);
+               } while (--timeout);
+       }
+
+       return 0;
+}
+
+static inline enum sys_clk_rate __get_sys_clk_index(unsigned long rate)
+{
+       switch (rate) {
+       case 12000000:
+               return CLK_RATE_12MHZ;
+       case 16800000:
+               return CLK_RATE_16MHZ;
+       case 19200000:
+               return CLK_RATE_19MHZ;
+       case 26000000:
+               return CLK_RATE_26MHZ;
+       case 38400000:
+               return CLK_RATE_38MHZ;
+       default:
+               return CLK_RATE_UNDEFINED;
+       }
+}
+
+static void omap_usb_dpll_relock(struct omap_usb *phy)
+{
+       u32             val;
+       unsigned long   timeout;
+
+       omap_usb_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
+
+       timeout = jiffies + msecs_to_jiffies(20);
+       do {
+               val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
+               if (val & PLL_LOCK)
+                       break;
+       } while (!WARN_ON(time_after(jiffies, timeout)));
+}
+
+static int omap_usb_dpll_lock(struct omap_usb *phy)
+{
+       u32                     val;
+       unsigned long           rate;
+       enum sys_clk_rate       clk_index;
+
+       rate            = clk_get_rate(phy->sys_clk);
+       clk_index       = __get_sys_clk_index(rate);
+
+       if (clk_index == CLK_RATE_UNDEFINED) {
+               pr_err("dpll cannot be locked for sys clk freq:%luHz\n", rate);
+               return -EINVAL;
+       }
+
+       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
+       val &= ~PLL_REGN_MASK;
+       val |= omap_usb3_dpll_params[clk_index].n << PLL_REGN_SHIFT;
+       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
+
+       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
+       val &= ~PLL_SELFREQDCO_MASK;
+       val |= omap_usb3_dpll_params[clk_index].freq << PLL_SELFREQDCO_SHIFT;
+       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
+
+       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
+       val &= ~PLL_REGM_MASK;
+       val |= omap_usb3_dpll_params[clk_index].m << PLL_REGM_SHIFT;
+       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
+
+       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
+       val &= ~PLL_REGM_F_MASK;
+       val |= omap_usb3_dpll_params[clk_index].mf << PLL_REGM_F_SHIFT;
+       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
+
+       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
+       val &= ~PLL_SD_MASK;
+       val |= omap_usb3_dpll_params[clk_index].sd << PLL_SD_SHIFT;
+       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
+
+       omap_usb_dpll_relock(phy);
+
+       return 0;
+}
+
+static int omap_usb3_init(struct usb_phy *x)
+{
+       struct omap_usb *phy = phy_to_omapusb(x);
+
+       omap_usb_dpll_lock(phy);
+       omap_control_usb3_phy_power(phy->control_dev, 1);
+
+       return 0;
+}
+
+static int omap_usb3_probe(struct platform_device *pdev)
+{
+       struct omap_usb                 *phy;
+       struct resource                 *res;
+
+       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy) {
+               dev_err(&pdev->dev, "unable to alloc mem for OMAP USB3 PHY\n");
+               return -ENOMEM;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl");
+       phy->pll_ctrl_base = devm_request_and_ioremap(&pdev->dev, res);
+       if (!phy->pll_ctrl_base) {
+               dev_err(&pdev->dev, "ioremap of pll_ctrl failed\n");
+               return -ENOMEM;
+       }
+
+       phy->dev                = &pdev->dev;
+
+       phy->phy.dev            = phy->dev;
+       phy->phy.label          = "omap-usb3";
+       phy->phy.init           = omap_usb3_init;
+       phy->phy.set_suspend    = omap_usb3_suspend;
+       phy->phy.type           = USB_PHY_TYPE_USB3;
+
+       phy->is_suspended       = 1;
+       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_err(&pdev->dev, "unable to get usb_otg_ss_refclk960m\n");
+               return PTR_ERR(phy->optclk);
+       }
+       clk_prepare(phy->optclk);
+
+       phy->sys_clk = devm_clk_get(phy->dev, "sys_clkin");
+       if (IS_ERR(phy->sys_clk)) {
+               pr_err("%s: unable to get sys_clkin\n", __func__);
+               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;
+       }
+
+       omap_control_usb3_phy_power(phy->control_dev, 0);
+       usb_add_phy_dev(&phy->phy);
+
+       platform_set_drvdata(pdev, phy);
+
+       pm_runtime_enable(phy->dev);
+       pm_runtime_get(&pdev->dev);
+
+       return 0;
+}
+
+static int omap_usb3_remove(struct platform_device *pdev)
+{
+       struct omap_usb *phy = platform_get_drvdata(pdev);
+
+       clk_unprepare(phy->wkupclk);
+       clk_unprepare(phy->optclk);
+       usb_remove_phy(&phy->phy);
+       if (!pm_runtime_suspended(&pdev->dev))
+               pm_runtime_put(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int omap_usb3_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);
+       clk_disable(phy->optclk);
+
+       return 0;
+}
+
+static int omap_usb3_runtime_resume(struct device *dev)
+{
+       u32 ret = 0;
+       struct platform_device  *pdev = to_platform_device(dev);
+       struct omap_usb *phy = platform_get_drvdata(pdev);
+
+       ret = clk_enable(phy->optclk);
+       if (ret) {
+               dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
+               goto err1;
+       }
+
+       ret = clk_enable(phy->wkupclk);
+       if (ret) {
+               dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
+               goto err2;
+       }
+
+       return 0;
+
+err2:
+       clk_disable(phy->optclk);
+
+err1:
+       return ret;
+}
+
+static const struct dev_pm_ops omap_usb3_pm_ops = {
+       SET_RUNTIME_PM_OPS(omap_usb3_runtime_suspend, omap_usb3_runtime_resume,
+               NULL)
+};
+
+#define DEV_PM_OPS     (&omap_usb3_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id omap_usb3_id_table[] = {
+       { .compatible = "ti,omap-usb3" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, omap_usb3_id_table);
+#endif
+
+static struct platform_driver omap_usb3_driver = {
+       .probe          = omap_usb3_probe,
+       .remove         = omap_usb3_remove,
+       .driver         = {
+               .name   = "omap-usb3",
+               .owner  = THIS_MODULE,
+               .pm     = DEV_PM_OPS,
+               .of_match_table = of_match_ptr(omap_usb3_id_table),
+       },
+};
+
+module_platform_driver(omap_usb3_driver);
+
+MODULE_ALIAS("platform: omap_usb3");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP USB3 phy driver");
+MODULE_LICENSE("GPL v2");
index c3853c92279b70ba1058724363113686a80b9c0d..2535345b7cd3a2835eff85ff5f76d1fd0f93bb5f 100644 (file)
@@ -53,6 +53,13 @@ config PANEL_TAAL
         help
           Taal DSI command mode panel from TPO.
 
+config PANEL_LG4591
+       tristate "LG 4591 Panel"
+       depends on OMAP2_DSS_DSI
+       depends on BACKLIGHT_CLASS_DEVICE
+       help
+               LG 4591 DSI Video mode Panel on OMAP5 sevm.
+
 config PANEL_TPO_TD043MTEA1
         tristate "TPO TD043MTEA1 LCD Panel"
         depends on OMAP2_DSS_DPI && SPI
index 58a5176b07b0fb769c22692354390be027923ec5..f866c6024c7a731f92bd05b4acdecd7918dd44c5 100644 (file)
@@ -5,6 +5,7 @@ obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
 obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o
 
 obj-$(CONFIG_PANEL_TAAL) += panel-taal.o
+obj-$(CONFIG_PANEL_LG4591) += panel-lg4591.o
 obj-$(CONFIG_PANEL_PICODLP) +=  panel-picodlp.o
 obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
 obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o
diff --git a/drivers/video/omap2/displays/panel-lg4591.c b/drivers/video/omap2/displays/panel-lg4591.c
new file mode 100644 (file)
index 0000000..256e59a
--- /dev/null
@@ -0,0 +1,681 @@
+/*
+ * LG-4591 panel driver
+ *
+ * Copyright 2011 Texas Instruments, Inc.
+ * Author: Archit Taneja <archit@ti.com>
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * based on d2l panel driver by Jerry Alexander <x0135174@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+
+#include <video/omapdss.h>
+#include <video/mipi_display.h>
+
+#define DISPLAY_CTRL1  0xb5
+#define DISPLAY_CTRL2  0xb6
+#define OSCSET         0xc0
+#define PWRCTL1                0xc1
+#define PWRCTL2                0xc2
+#define PWRCTL3                0xc3
+#define PWRCTL4                0xc4
+#define RGAMMAP                0xd0
+#define RGAMMAN                0xd1
+#define GGAMMAP                0xd2
+#define GGAMMAN                0xd3
+#define BGAMMAP                0xd4
+#define BGAMMAN                0xd5
+#define DSI_CFG                0xe0
+#define OTP2           0xf9
+
+#define WRDISBV                0x51
+#define RDDISBV                0x52
+#define WRCTRLD                0x53
+#define RDCTRLD                0x54
+
+static struct omap_video_timings lg4591_timings = {
+       .x_res          = 720,
+       .y_res          = 1280,
+       .pixel_clock    = 80000,
+       .hfp            = 1,
+       .hsw            = 1,
+       .hbp            = 306,
+       .vfp            = 1,
+       .vsw            = 1,
+       .vbp            = 15,
+};
+
+static struct omap_dss_dsi_videomode_timings vm_data = {
+       .hsa                    = 1,
+       .hfp                    = 1,
+       .hbp                    = 227,
+       .vsa                    = 1,
+       .vfp                    = 1,
+       .vbp                    = 15,
+
+       .blanking_mode          = 0,
+       .hsa_blanking_mode      = 1,
+       .hfp_blanking_mode      = 1,
+       .hbp_blanking_mode      = 1,
+
+       .ddr_clk_always_on      = true,
+
+       .window_sync            = 4,
+};
+
+struct lg4591_data {
+       struct mutex lock;
+
+       int     reset_gpio;
+       struct omap_dsi_pin_config pin_config;
+
+       struct omap_dss_device *dssdev;
+       struct backlight_device *bldev;
+       bool enabled;
+       int bl;
+
+       int config_channel;
+       int pixel_channel;
+
+       struct omap_video_timings *timings;
+};
+
+struct lg4591_reg {
+       /* Address and register value */
+       u8 data[10];
+       int len;
+};
+
+static struct lg4591_reg init_seq1[] = {
+       { { DSI_CFG, 0x43, 0x00, 0x80, 0x00, 0x00, }, 6, },
+       { { DISPLAY_CTRL1, 0x29, 0x20, 0x40, 0x00, 0x00 }, 6, },
+       { { DISPLAY_CTRL2, 0x01, 0x14, 0x0f, 0x16, 0x13 }, 6, },
+       { { RGAMMAP, 0x10, 0x31, 0x67, 0x34, 0x18, 0x06, 0x60, 0x32, 0x02 },
+               10, },
+       { { RGAMMAN, 0x10, 0x14, 0x64, 0x34, 0x00, 0x05, 0x60, 0x33, 0x04 },
+               10, },
+       { { GGAMMAP, 0x10, 0x31, 0x67, 0x34, 0x18, 0x06, 0x60, 0x32, 0x02 },
+               10, },
+       { { GGAMMAN, 0x10, 0x14, 0x64, 0x34, 0x00, 0x05, 0x60, 0x33, 0x04 },
+               10, },
+       { { BGAMMAP, 0x10, 0x31, 0x67, 0x34, 0x18, 0x06, 0x60, 0x32, 0x02 },
+               10, },
+       { { BGAMMAN, 0x10, 0x14, 0x64, 0x34, 0x00, 0x05, 0x60, 0x33, 0x04 },
+               10, },
+       { { OSCSET, 0x01, 0x04, }, 3 },
+       { { PWRCTL3, 0x00, 0x09, 0x10, 0x12, 0x00, 0x66, 0x00, 0x32, 0x00 },
+               10, },
+       { { PWRCTL4, 0x22, 0x24, 0x18, 0x18, 0x47 }, 6, },
+       { { OTP2, 0x00, }, 2 },
+       { { PWRCTL1, 0x00, }, 2 },
+       { { PWRCTL2, 0x02, }, 2 },
+};
+
+static struct lg4591_reg init_seq2[] = {
+       { { PWRCTL2, 0x06, }, 2 },
+};
+
+static struct lg4591_reg init_seq3[] = {
+       { { PWRCTL2, 0x4E, }, 2 },
+};
+
+static struct lg4591_reg sleep_out[] = {
+       { { MIPI_DCS_EXIT_SLEEP_MODE, }, 1},
+};
+
+static struct lg4591_reg init_seq4[] = {
+       { { OTP2, 0x80}, 2 },
+};
+
+static struct lg4591_reg brightness_ctrl[] = {
+       { { WRCTRLD, 0x24}, 2 },
+};
+
+static struct lg4591_reg display_on[] = {
+       { { MIPI_DCS_SET_DISPLAY_ON, }, 1},
+};
+
+static int lg4591_write(struct omap_dss_device *dssdev, u8 *buf, int len)
+{
+       struct lg4591_data *lg_d = dev_get_drvdata(&dssdev->dev);
+       int r;
+
+       r = dsi_vc_dcs_write_nosync(dssdev, lg_d->config_channel, buf, len);
+       if (r)
+               dev_err(&dssdev->dev, "write cmd/reg(%x) failed: %d\n",
+                               buf[0], r);
+
+       return r;
+}
+
+static int lg4591_write_sequence(struct omap_dss_device *dssdev,
+               struct lg4591_reg *seq, int len)
+{
+       int r, i;
+
+       for (i = 0; i < len; i++) {
+               r = lg4591_write(dssdev, seq[i].data, seq[i].len);
+               if (r) {
+                       dev_err(&dssdev->dev, "sequence failed: %d\n", i);
+                       return -EINVAL;
+               }
+
+               /* TODO: Figure out why this is needed for OMAP5 */
+               msleep(1);
+       }
+
+       return 0;
+}
+
+static void lg4591_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       *timings = dssdev->panel.timings;
+}
+
+static void lg4591_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       dssdev->panel.timings.x_res = timings->x_res;
+       dssdev->panel.timings.y_res = timings->y_res;
+       dssdev->panel.timings.pixel_clock = timings->pixel_clock;
+       dssdev->panel.timings.hsw = timings->hsw;
+       dssdev->panel.timings.hfp = timings->hfp;
+       dssdev->panel.timings.hbp = timings->hbp;
+       dssdev->panel.timings.vsw = timings->vsw;
+       dssdev->panel.timings.vfp = timings->vfp;
+       dssdev->panel.timings.vbp = timings->vbp;
+}
+
+static int lg4591_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       return 0;
+}
+
+static void lg4591_get_resolution(struct omap_dss_device *dssdev,
+               u16 *xres, u16 *yres)
+{
+       *xres = dssdev->panel.timings.x_res;
+       *yres = dssdev->panel.timings.y_res;
+}
+
+static int lg4591_hw_reset(struct omap_dss_device *dssdev)
+{
+       struct lg4591_data *lg_d = dev_get_drvdata(&dssdev->dev);
+
+       gpio_set_value(lg_d->reset_gpio, 1);
+       msleep(20);
+       gpio_set_value(lg_d->reset_gpio, 0);
+       msleep(25);
+       gpio_set_value(lg_d->reset_gpio, 1);
+       msleep(20);
+
+       return 0;
+}
+
+static int lg4591_update_brightness(struct omap_dss_device *dssdev, int level)
+{
+       int r;
+       u8 buf[2];
+
+       buf[0] = WRDISBV;
+       buf[1] = level;
+
+       r = lg4591_write(dssdev, buf, sizeof(buf));
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static int lg4591_set_brightness(struct backlight_device *bd)
+{
+       struct omap_dss_device *dssdev = dev_get_drvdata(&bd->dev);
+       struct lg4591_data *lg_d = dev_get_drvdata(&dssdev->dev);
+       int bl = bd->props.brightness;
+       int r = 0;
+
+       if (bl == lg_d->bl)
+               return 0;
+
+       mutex_lock(&lg_d->lock);
+
+       if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+               dsi_bus_lock(dssdev);
+
+               r = lg4591_update_brightness(dssdev, bl);
+               if (!r)
+                       lg_d->bl = bl;
+
+               dsi_bus_unlock(dssdev);
+       }
+
+       mutex_unlock(&lg_d->lock);
+
+       return r;
+}
+
+static int lg4591_get_brightness(struct backlight_device *bd)
+{
+       return bd->props.brightness;
+}
+
+static const struct backlight_ops lg4591_backlight_ops  = {
+       .get_brightness = lg4591_get_brightness,
+       .update_status = lg4591_set_brightness,
+};
+
+static int lg4591_probe_of(struct omap_dss_device *dssdev,
+               struct lg4591_data *lg_d)
+{
+       struct device_node *node = dssdev->dev.of_node;
+       struct property *prop;
+       u32 lane_arr[10];
+       int len, i, num_pins, r;
+
+       r = of_get_gpio(node, 0);
+       if (!gpio_is_valid(r)) {
+               dev_err(&dssdev->dev, "failed to parse reset gpio\n");
+               return r;
+       }
+       lg_d->reset_gpio = r;
+
+       prop = of_find_property(node, "lanes", &len);
+       if (prop == NULL) {
+               dev_err(&dssdev->dev, "failed to find lane data\n");
+               return -EINVAL;
+       }
+
+       num_pins = len / sizeof(u32);
+
+       if (num_pins < 4 || num_pins % 2 != 0
+                       || num_pins > ARRAY_SIZE(lane_arr)) {
+               dev_err(&dssdev->dev, "bad number of lanes\n");
+               return -EINVAL;
+       }
+
+       r = of_property_read_u32_array(node, "lanes", lane_arr, num_pins);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to read lane data\n");
+               return r;
+       }
+
+       lg_d->pin_config.num_pins = num_pins;
+       for (i = 0; i < num_pins; ++i)
+               lg_d->pin_config.pins[i] = (int)lane_arr[i];
+
+       return 0;
+}
+
+static int lg4591_probe(struct omap_dss_device *dssdev)
+{
+       struct device_node *node = dssdev->dev.of_node;
+       struct backlight_properties props;
+       struct lg4591_data *lg_d;
+       int r;
+
+       dev_dbg(&dssdev->dev, "lg4591_probe\n");
+
+       if (node == NULL) {
+               dev_err(&dssdev->dev, "no device tree data!\n");
+               return -EINVAL;
+       }
+
+       lg_d = devm_kzalloc(&dssdev->dev, sizeof(*lg_d), GFP_KERNEL);
+       if (!lg_d)
+               return -ENOMEM;
+
+       dev_set_drvdata(&dssdev->dev, lg_d);
+       lg_d->dssdev = dssdev;
+
+       r = lg4591_probe_of(dssdev, lg_d);
+       if (r)
+               return r;
+
+       dssdev->caps = 0;
+       dssdev->panel.timings = lg4591_timings;
+       dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
+
+       mutex_init(&lg_d->lock);
+
+       r = devm_gpio_request_one(&dssdev->dev, lg_d->reset_gpio,
+                       GPIOF_DIR_OUT, "lcd reset");
+       if (r) {
+               dev_err(&dssdev->dev, "failed to request reset gpio\n");
+               return r;
+       }
+
+       /* Register DSI backlight control */
+       memset(&props, 0, sizeof(struct backlight_properties));
+
+       props.type = BACKLIGHT_RAW;
+       props.max_brightness = 255;
+       props.brightness = 255;
+
+       lg_d->bl = props.brightness;
+
+       lg_d->bldev = backlight_device_register("lg4591", &dssdev->dev, dssdev,
+                       &lg4591_backlight_ops, &props);
+       if (IS_ERR(lg_d->bldev))
+               return PTR_ERR(lg_d->bldev);
+
+       r = omap_dsi_request_vc(dssdev, &lg_d->pixel_channel);
+       if (r) {
+               dev_err(&dssdev->dev,
+                               "failed to request pixel update channel\n");
+               goto err_req_pix_vc;
+       }
+
+       r = omap_dsi_set_vc_id(dssdev, lg_d->pixel_channel, 0);
+       if (r) {
+               dev_err(&dssdev->dev,
+                               "failed to set VC_ID for pixel data virtual channel\n");
+               goto err_set_pix_vc_id;
+       }
+
+       r = omap_dsi_request_vc(dssdev, &lg_d->config_channel);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to request config channel\n");
+               goto err_req_cfg_vc;
+       }
+
+       r = omap_dsi_set_vc_id(dssdev, lg_d->config_channel, 0);
+       if (r) {
+               dev_err(&dssdev->dev,
+                               "failed to set VC_ID for config virtual channel\n");
+               goto err_set_cfg_vc_id;
+       }
+
+       return 0;
+
+err_set_cfg_vc_id:
+       omap_dsi_release_vc(dssdev, lg_d->config_channel);
+err_req_cfg_vc:
+err_set_pix_vc_id:
+       omap_dsi_release_vc(dssdev, lg_d->pixel_channel);
+err_req_pix_vc:
+       backlight_device_unregister(lg_d->bldev);
+
+       return r;
+}
+
+static void lg4591_remove(struct omap_dss_device *dssdev)
+{
+       struct lg4591_data *lg_d = dev_get_drvdata(&dssdev->dev);
+
+       omap_dsi_release_vc(dssdev, lg_d->config_channel);
+       omap_dsi_release_vc(dssdev, lg_d->pixel_channel);
+
+       backlight_device_unregister(lg_d->bldev);
+}
+
+/**
+ * lg4591_config - Configure lg4591
+ *
+ * Initial configuration for lg4591 configuration registers
+ */
+static int lg4591_config(struct omap_dss_device *dssdev)
+{
+       struct lg4591_data *lg_d = dev_get_drvdata(&dssdev->dev);
+       int r;
+
+       r = lg4591_write_sequence(dssdev, init_seq1, ARRAY_SIZE(init_seq1));
+       if (r)
+               return r;
+
+       msleep(20);
+
+       r = lg4591_write_sequence(dssdev, init_seq2, ARRAY_SIZE(init_seq2));
+       if (r)
+               return r;
+
+       msleep(20);
+
+       r = lg4591_write_sequence(dssdev, init_seq3, ARRAY_SIZE(init_seq3));
+       if (r)
+               return r;
+
+       msleep(20);
+
+       r = lg4591_write_sequence(dssdev, sleep_out, ARRAY_SIZE(sleep_out));
+       if (r)
+               return r;
+
+       msleep(20);
+
+       r = lg4591_write_sequence(dssdev, init_seq4, ARRAY_SIZE(init_seq4));
+       if (r)
+               return r;
+
+       msleep(20);
+
+       r = lg4591_write_sequence(dssdev, display_on, ARRAY_SIZE(display_on));
+       if (r)
+               return r;
+
+       msleep(20);
+
+       r = lg4591_write_sequence(dssdev, brightness_ctrl,
+                       ARRAY_SIZE(brightness_ctrl));
+       if (r)
+               return r;
+
+       r = dsi_vc_set_max_rx_packet_size(dssdev, lg_d->config_channel, 0xff);
+       if (r) {
+               dev_err(&dssdev->dev, "can't set max rx packet size\n");
+               return r;
+       }
+
+       r = lg4591_update_brightness(dssdev, lg_d->bl);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static int lg4591_power_on(struct omap_dss_device *dssdev)
+{
+       struct lg4591_data *lg_d = dev_get_drvdata(&dssdev->dev);
+       int r;
+
+       r = omapdss_dsi_configure_pins(dssdev, &lg_d->pin_config);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to configure DSI pins\n");
+               goto err0;
+       }
+
+       omapdss_dsi_set_pixel_format(dssdev, OMAP_DSS_DSI_FMT_RGB888);
+       omapdss_dsi_set_timings(dssdev, &lg4591_timings);
+       omapdss_dsi_set_videomode_timings(dssdev, &vm_data);
+       omapdss_dsi_set_operation_mode(dssdev, OMAP_DSS_DSI_VIDEO_MODE);
+
+       r = omapdss_dsi_set_clocks(dssdev, 240000000, 10000000);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to set HS and LP clocks\n");
+               goto err0;
+       }
+
+       r = omapdss_dsi_display_enable(dssdev);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to enable DSI\n");
+               goto err0;
+       }
+
+       lg4591_hw_reset(dssdev);
+
+       omapdss_dsi_vc_enable_hs(dssdev, lg_d->pixel_channel, true);
+
+       /* XXX */
+       msleep(100);
+
+       r = lg4591_config(dssdev);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to configure panel\n");
+               goto err;
+       }
+
+       dsi_enable_video_output(dssdev, lg_d->pixel_channel);
+
+       lg_d->enabled = true;
+
+       return r;
+err:
+       dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
+
+       lg4591_hw_reset(dssdev);
+
+       omapdss_dsi_display_disable(dssdev, false, false);
+err0:
+       return r;
+}
+
+static void lg4591_power_off(struct omap_dss_device *dssdev)
+{
+       struct lg4591_data *lg_d = dev_get_drvdata(&dssdev->dev);
+
+       gpio_set_value(lg_d->reset_gpio, 0);
+
+       msleep(20);
+
+       lg_d->enabled = 0;
+       dsi_disable_video_output(dssdev, lg_d->pixel_channel);
+       omapdss_dsi_display_disable(dssdev, false, false);
+}
+
+static int lg4591_start(struct omap_dss_device *dssdev)
+{
+       struct lg4591_data *lg_d = dev_get_drvdata(&dssdev->dev);
+       int r = 0;
+
+       mutex_lock(&lg_d->lock);
+
+       dsi_bus_lock(dssdev);
+
+       r = lg4591_power_on(dssdev);
+
+       dsi_bus_unlock(dssdev);
+
+       if (r)
+               dev_err(&dssdev->dev, "enable failed\n");
+       else
+               dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       mutex_unlock(&lg_d->lock);
+
+       return r;
+}
+
+static void lg4591_stop(struct omap_dss_device *dssdev)
+{
+       struct lg4591_data *lg_d = dev_get_drvdata(&dssdev->dev);
+
+       mutex_lock(&lg_d->lock);
+
+       dsi_bus_lock(dssdev);
+
+       lg4591_power_off(dssdev);
+
+       dsi_bus_unlock(dssdev);
+
+       mutex_unlock(&lg_d->lock);
+}
+
+static void lg4591_disable(struct omap_dss_device *dssdev)
+{
+       dev_dbg(&dssdev->dev, "disable\n");
+
+       if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+               lg4591_stop(dssdev);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static int lg4591_enable(struct omap_dss_device *dssdev)
+{
+       dev_dbg(&dssdev->dev, "enable\n");
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
+               return -EINVAL;
+
+       return lg4591_start(dssdev);
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id lg4591_of_match[] = {
+       {
+               .compatible = "ti,lg4591",
+       },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, lg4591_of_match);
+#else
+#define dss_of_match NULL
+#endif
+
+static struct omap_dss_driver lg4591_driver = {
+       .probe = lg4591_probe,
+       .remove = lg4591_remove,
+
+       .enable = lg4591_enable,
+       .disable = lg4591_disable,
+
+       .get_resolution = lg4591_get_resolution,
+       .get_recommended_bpp = omapdss_default_get_recommended_bpp,
+
+       .get_timings = lg4591_get_timings,
+       .set_timings = lg4591_set_timings,
+       .check_timings = lg4591_check_timings,
+
+       .driver = {
+               .name = "lg4591",
+               .owner = THIS_MODULE,
+               .of_match_table = lg4591_of_match,
+       },
+};
+
+static int __init lg4591_init(void)
+{
+       omap_dss_register_driver(&lg4591_driver);
+       return 0;
+}
+
+static void __exit lg4591_exit(void)
+{
+       omap_dss_unregister_driver(&lg4591_driver);
+}
+
+module_init(lg4591_init);
+module_exit(lg4591_exit);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("LG4591 driver");
+MODULE_LICENSE("GPL");
index a32407a5735af59b123c8865c6dcf2ec3220db7a..a906eeeb5e81cc3af105ed5dadd7dcd4f038b530 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/workqueue.h>
 #include <linux/slab.h>
 #include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 
 #include <video/omapdss.h>
 #include <video/omap-panel-nokia-dsi.h>
@@ -876,6 +878,67 @@ static void taal_probe_pdata(struct taal_data *td,
        td->pin_config = pdata->pin_config;
 }
 
+static int taal_probe_of(struct omap_dss_device *dssdev, struct taal_data *td)
+{
+       struct device_node *node = dssdev->dev.of_node;
+       struct property *prop;
+       u32 lane_arr[10];
+       int gpio, len, num_pins;
+       int r, i;
+
+       gpio = of_get_gpio(node, 0);
+
+       if (gpio_is_valid(gpio)) {
+               td->reset_gpio = gpio;
+       } else {
+               dev_err(&dssdev->dev, "failed to parse reset gpio\n");
+               return gpio;
+       }
+
+       if (of_gpio_count(node) > 1) {
+               gpio = of_get_gpio(node, 1);
+
+               if (gpio_is_valid(gpio)) {
+                       td->ext_te_gpio = gpio;
+               } else if (gpio == -ENOENT) {
+                       td->ext_te_gpio = -1;
+               } else {
+                       dev_err(&dssdev->dev, "failed to parse TE gpio\n");
+                       return gpio;
+               }
+       } else {
+               td->ext_te_gpio = -1;
+       }
+
+       prop = of_find_property(node, "lanes", &len);
+       if (prop == NULL) {
+               dev_err(&dssdev->dev, "failed to find lane data\n");
+               return -EINVAL;
+       }
+
+       num_pins = len / sizeof(u32);
+
+       if (num_pins < 4 || num_pins % 2 != 0
+                       || num_pins > ARRAY_SIZE(lane_arr)) {
+               dev_err(&dssdev->dev, "bad number of lanes\n");
+               return -EINVAL;
+       }
+
+       r = of_property_read_u32_array(node, "lanes", lane_arr, num_pins);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to read lane data\n");
+               return r;
+       }
+
+       td->pin_config.num_pins = num_pins;
+       for (i = 0; i < num_pins; ++i)
+               td->pin_config.pins[i] = (int)lane_arr[i];
+
+       return 0;
+}
+
+static const struct of_device_id taal_of_match[];
+
 static int taal_probe(struct omap_dss_device *dssdev)
 {
        struct backlight_properties props;
@@ -899,6 +962,20 @@ static int taal_probe(struct omap_dss_device *dssdev)
                taal_probe_pdata(td, pdata);
 
                panel_name = pdata->name;
+       } else if (dssdev->dev.of_node) {
+               const struct of_device_id *of_dev_id;
+
+               of_dev_id = of_match_device(taal_of_match, &dssdev->dev);
+               if (of_dev_id == NULL)
+                       return -ENODEV;
+
+               panel_name = (const char*)of_dev_id->data;
+
+               r = taal_probe_of(dssdev, td);
+               if (r) {
+                       dev_err(&dssdev->dev, "failed to parse OF data\n");
+                       return r;
+               }
        } else {
                return -ENODEV;
        }
@@ -1742,6 +1819,20 @@ err:
        mutex_unlock(&td->lock);
 }
 
+#if defined(CONFIG_OF)
+static const struct of_device_id taal_of_match[] = {
+       {
+               .compatible = "tpo,taal",
+               .data = "taal",
+       },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, taal_of_match);
+#else
+#define dss_of_match NULL
+#endif
+
 static struct omap_dss_driver taal_driver = {
        .probe          = taal_probe,
        .remove         = __exit_p(taal_remove),
@@ -1768,6 +1859,7 @@ static struct omap_dss_driver taal_driver = {
        .driver         = {
                .name   = "taal",
                .owner  = THIS_MODULE,
+               .of_match_table = taal_of_match,
        },
 };
 
index 8281baafe1efd39127a628c70ecb3b720034d6f1..d38b71a2640cd468100a361fe76ad1b165eb7f68 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/i2c.h>
 #include <linux/gpio.h>
 #include <drm/drm_edid.h>
+#include <linux/of_gpio.h>
+#include <linux/of_i2c.h>
 
 #include <video/omap-panel-tfp410.h>
 
@@ -93,11 +95,51 @@ static void tfp410_power_off(struct omap_dss_device *dssdev)
        omapdss_dpi_display_disable(dssdev);
 }
 
+static int tfp410_probe_of(struct omap_dss_device *dssdev,
+               struct panel_drv_data *ddata)
+{
+       struct device_node *node = dssdev->dev.of_node;
+       struct device_node *adapter_node;
+       struct i2c_adapter *adapter;
+       int r, gpio, datalines;
+
+       gpio = of_get_gpio(node, 0);
+
+       if (gpio_is_valid(gpio)) {
+               ddata->pd_gpio = gpio;
+       } else if (gpio == -ENOENT) {
+               ddata->pd_gpio = -1;
+       } else {
+               dev_err(&dssdev->dev, "failed to parse PD gpio\n");
+               return gpio;
+       }
+
+       r = of_property_read_u32(node, "data-lines", &datalines);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to parse datalines");
+               return r;
+       }
+
+       dssdev->phy.dpi.data_lines = datalines;
+
+       adapter_node = of_parse_phandle(node, "i2c-bus", 0);
+       if (adapter_node) {
+               adapter = of_find_i2c_adapter_by_node(adapter_node);
+               if (adapter == NULL) {
+                       dev_err(&dssdev->dev, "failed to parse i2c-bus\n");
+                       return -EINVAL;
+               }
+
+               ddata->i2c_adapter = adapter;
+       }
+
+       return 0;
+}
+
 static int tfp410_probe(struct omap_dss_device *dssdev)
 {
        struct panel_drv_data *ddata;
        int r;
-       int i2c_bus_num;
 
        ddata = devm_kzalloc(&dssdev->dev, sizeof(*ddata), GFP_KERNEL);
        if (!ddata)
@@ -108,14 +150,29 @@ static int tfp410_probe(struct omap_dss_device *dssdev)
        ddata->dssdev = dssdev;
        mutex_init(&ddata->lock);
 
-       if (dssdev->data) {
+       if (dssdev->dev.of_node) {
+               r = tfp410_probe_of(dssdev, ddata);
+               if (r)
+                       return r;
+       }
+       else if (dssdev->data) {
                struct tfp410_platform_data *pdata = dssdev->data;
+               struct i2c_adapter *adapter;
+               int i2c_bus_num;
 
                ddata->pd_gpio = pdata->power_down_gpio;
                i2c_bus_num = pdata->i2c_bus_num;
+
+               adapter = i2c_get_adapter(i2c_bus_num);
+               if (!adapter) {
+                       dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n",
+                                       i2c_bus_num);
+                       return -EINVAL;
+               }
+
+               ddata->i2c_adapter = adapter;
        } else {
-               ddata->pd_gpio = -1;
-               i2c_bus_num = -1;
+               return -EINVAL;
        }
 
        if (gpio_is_valid(ddata->pd_gpio)) {
@@ -128,19 +185,6 @@ static int tfp410_probe(struct omap_dss_device *dssdev)
                }
        }
 
-       if (i2c_bus_num != -1) {
-               struct i2c_adapter *adapter;
-
-               adapter = i2c_get_adapter(i2c_bus_num);
-               if (!adapter) {
-                       dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n",
-                                       i2c_bus_num);
-                       return -EINVAL;
-               }
-
-               ddata->i2c_adapter = adapter;
-       }
-
        dev_set_drvdata(&dssdev->dev, ddata);
 
        return 0;
@@ -318,6 +362,19 @@ out:
        return true;
 }
 
+#if defined(CONFIG_OF)
+static const struct of_device_id tfp410_of_match[] = {
+       {
+               .compatible = "ti,tfp410",
+       },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, tfp410_of_match);
+#else
+#define dss_of_match NULL
+#endif
+
 static struct omap_dss_driver tfp410_driver = {
        .probe          = tfp410_probe,
        .remove         = __exit_p(tfp410_remove),
@@ -335,6 +392,7 @@ static struct omap_dss_driver tfp410_driver = {
        .driver         = {
                .name   = "tfp410",
                .owner  = THIS_MODULE,
+               .of_match_table = tfp410_of_match,
        },
 };
 
index cb0f145c7077897721dce128aa0369ce4f6953c8..28159e2d96ef0f95427a9ebac39c85da25187a5e 100644 (file)
@@ -58,7 +58,7 @@ config OMAP2_DSS_VENC
          OMAP Video Encoder support for S-Video and composite TV-out.
 
 config OMAP4_DSS_HDMI
-       bool "HDMI support"
+       bool "OMAP4 HDMI support"
         default y
        help
          HDMI Interface. This adds the High Definition Multimedia Interface.
@@ -67,6 +67,22 @@ config OMAP4_DSS_HDMI
 config OMAP4_DSS_HDMI_AUDIO
        bool
 
+config OMAP5_DSS_HDMI_AUDIO
+       bool
+       depends on OMAP5_DSS_HDMI
+
+config OMAP5_DSS_HDMI
+       bool "OMAP5 HDMI support"
+       depends on SOC_OMAP5
+       depends on OMAP4_DSS_HDMI
+       default y
+       select PINCTRL_SINGLE
+       select GPIO_PCA953X
+       select MFD_PALMAS
+       select REGULATOR_PALMAS
+       help
+           HDMI Interface.This add the High Deinition Multimedia Interface.
+           See http://wwww.hdmi.org/ for HDMI specification.
 config OMAP2_DSS_SDI
        bool "SDI support"
         default n
index 61949ff7940cf4257d7d2c489eb6fe4960436b15..8abdc8165341a1b029f44da7e6e744517bf64b32 100644 (file)
@@ -12,4 +12,6 @@ omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
 omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
 omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o \
                                    hdmi_panel.o ti_hdmi_4xxx_ip.o
+omapdss-$(CONFIG_OMAP5_DSS_HDMI) += hdmi.o \
+                               hdmi_panel.o ti_hdmi_4xxx_ip.o ti_hdmi_5xxx_ip.o
 ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG
index f8779d4750ba63f98908492f2b11b9bfe7cf4ea8..0ddaae61a0e48d3cbe139e025e6f4426af063de7 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/suspend.h>
 #include <linux/slab.h>
+#include <linux/of_device.h>
 
 #include <video/omapdss.h>
 
@@ -282,7 +283,16 @@ static int dss_bus_match(struct device *dev, struct device_driver *driver)
        DSSDBG("bus_match. dev %s/%s, drv %s\n",
                        dev_name(dev), dssdev->driver_name, driver->name);
 
-       return strcmp(dssdev->driver_name, driver->name) == 0;
+       /* Attempt an OF style match first... */
+       if (of_driver_match_device(dev, driver))
+               return 1;
+
+       /* Then with old style */
+       if (dssdev->driver_name && driver->name &&
+                       strcmp(dssdev->driver_name, driver->name) == 0)
+               return 1;
+
+       return 0;
 }
 
 static ssize_t device_name_show(struct device *dev,
index 05ff2b91d9e8a49ee1e11c3c957b634eb4698393..4e8b6c8c67b9815a0cff74ec97ea2b0378246206 100644 (file)
@@ -85,6 +85,9 @@ struct dispc_features {
 
        /* no DISPC_IRQ_FRAMEDONETV on this SoC */
        bool no_framedone_tv:1;
+
+       /* revert to the OMAP4 mechanism of DISPC Smart Standby operation */
+       bool standby_workaround:1;
 };
 
 #define DISPC_MAX_NR_FIFOS 5
@@ -1584,6 +1587,7 @@ static void dispc_ovl_set_scaling(enum omap_plane plane,
 }
 
 static void dispc_ovl_set_rotation_attrs(enum omap_plane plane, u8 rotation,
+               enum omap_dss_rotation_type rotation_type,
                bool mirroring, enum omap_color_mode color_mode)
 {
        bool row_repeat = false;
@@ -1634,6 +1638,15 @@ static void dispc_ovl_set_rotation_attrs(enum omap_plane plane, u8 rotation,
        if (dss_has_feature(FEAT_ROWREPEATENABLE))
                REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane),
                        row_repeat ? 1 : 0, 18, 18);
+
+       if (color_mode == OMAP_DSS_COLOR_NV12) {
+               bool doublestride = (rotation_type == OMAP_DSS_ROT_TILER) &&
+                                       (rotation == OMAP_DSS_ROT_0 ||
+                                       rotation == OMAP_DSS_ROT_180);
+               /* DOUBLESTRIDE */
+               REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), doublestride, 22, 22);
+       }
+
 }
 
 static int color_mode_to_bpp(enum omap_color_mode color_mode)
@@ -2512,7 +2525,8 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
                dispc_ovl_set_vid_color_conv(plane, cconv);
        }
 
-       dispc_ovl_set_rotation_attrs(plane, rotation, mirror, color_mode);
+       dispc_ovl_set_rotation_attrs(plane, rotation, rotation_type, mirror,
+                       color_mode);
 
        dispc_ovl_set_zorder(plane, caps, zorder);
        dispc_ovl_set_pre_mult_alpha(plane, caps, pre_mult_alpha);
@@ -3466,6 +3480,9 @@ static void _omap_dispc_initial_config(void)
        dispc_configure_burst_sizes();
 
        dispc_ovl_enable_zorder_planes();
+
+       if (dispc.feat->standby_workaround)
+               REG_FLD_MOD(DISPC_MSTANDBY_CTRL, 1, 0, 0);
 }
 
 static const struct dispc_features omap24xx_dispc_feats __initconst = {
@@ -3551,6 +3568,7 @@ static const struct dispc_features omap54xx_dispc_feats __initconst = {
        .calc_core_clk          =       calc_core_clk_44xx,
        .num_fifos              =       5,
        .gfx_fifo_workaround    =       true,
+       .standby_workaround     =       true,
 };
 
 static int __init dispc_init_features(struct platform_device *pdev)
@@ -3693,12 +3711,24 @@ static const struct dev_pm_ops dispc_pm_ops = {
        .runtime_resume = dispc_runtime_resume,
 };
 
+#if defined(CONFIG_OF)
+static const struct of_device_id dispc_of_match[] = {
+       {
+               .compatible = "ti,omap4-dispc",
+       },
+       {},
+};
+#else
+#define dispc_of_match NULL
+#endif
+
 static struct platform_driver omap_dispchw_driver = {
        .remove         = __exit_p(omap_dispchw_remove),
        .driver         = {
                .name   = "omapdss_dispc",
                .owner  = THIS_MODULE,
                .pm     = &dispc_pm_ops,
+               .of_match_table = dispc_of_match,
        },
 };
 
index 222363c6e623f794402f68b98d419f5fc0d1f869..de4863d21ab78b7683d490628ef9bc5a7f1f335b 100644 (file)
@@ -39,6 +39,7 @@
 #define DISPC_GLOBAL_BUFFER            0x0800
 #define DISPC_CONTROL3                  0x0848
 #define DISPC_CONFIG3                   0x084C
+#define DISPC_MSTANDBY_CTRL            0x0858
 
 /* DISPC overlay registers */
 #define DISPC_OVL_BA0(n)               (DISPC_OVL_BASE(n) + \
index 4af136a04e53ce4fafc67a3435a0121062e53674..d06b8c66ca83db952958a17c7fbce39fc653b918 100644 (file)
@@ -30,6 +30,8 @@
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/of.h>
 
 #include <video/omapdss.h>
 
@@ -510,6 +512,60 @@ static void __init dpi_probe_pdata(struct platform_device *dpidev)
        }
 }
 
+static void __init dpi_probe_of(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *child;
+       struct omap_dss_device *dssdev;
+       enum omap_channel channel;
+       u32 v;
+       int r;
+
+       child = of_get_next_available_child(node, NULL);
+
+       if (!child)
+               return;
+
+       r = of_property_read_u32(node, "video-source", &v);
+       if (r) {
+               DSSERR("parsing channel failed\n");
+               return;
+       }
+
+       channel = v;
+
+       dssdev = dss_alloc_and_init_device(&pdev->dev);
+       if (!dssdev)
+               return;
+
+       dssdev->dev.of_node = child;
+       dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+       dssdev->name = child->name;
+       dssdev->channel = channel;
+
+       r = dpi_init_display(dssdev);
+       if (r) {
+               DSSERR("device %s init failed: %d\n", dssdev->name, r);
+               dss_put_device(dssdev);
+               return;
+       }
+
+       r = omapdss_output_set_device(&dpi.output, dssdev);
+       if (r) {
+               DSSERR("failed to connect output to new device: %s\n",
+                               dssdev->name);
+               dss_put_device(dssdev);
+               return;
+       }
+
+       r = dss_add_device(dssdev);
+       if (r) {
+               DSSERR("dss_add_device failed %d\n", r);
+               dss_put_device(dssdev);
+               return;
+       }
+}
+
 static void __init dpi_init_output(struct platform_device *pdev)
 {
        struct omap_dss_output *out = &dpi.output;
@@ -534,7 +590,10 @@ static int __init omap_dpi_probe(struct platform_device *pdev)
 
        dpi_init_output(pdev);
 
-       dpi_probe_pdata(pdev);
+       if (pdev->dev.of_node)
+               dpi_probe_of(pdev);
+       else if (pdev->dev.platform_data)
+               dpi_probe_pdata(pdev);
 
        return 0;
 }
@@ -548,11 +607,23 @@ static int __exit omap_dpi_remove(struct platform_device *pdev)
        return 0;
 }
 
+#if defined(CONFIG_OF)
+static const struct of_device_id dpi_of_match[] = {
+       {
+               .compatible = "ti,omap4-dpi",
+       },
+       {},
+};
+#else
+#define dpi_of_match NULL
+#endif
+
 static struct platform_driver omap_dpi_driver = {
        .remove         = __exit_p(omap_dpi_remove),
        .driver         = {
                .name   = "omapdss_dpi",
                .owner  = THIS_MODULE,
+               .of_match_table = dpi_of_match,
        },
 };
 
index 28d41d16b7be45aa10d646ba8983cd57e418e363..ba45909f052997b5b1ec81b02200412765c93903 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/slab.h>
 #include <linux/debugfs.h>
 #include <linux/pm_runtime.h>
+#include <linux/of.h>
 
 #include <video/omapdss.h>
 #include <video/mipi_display.h>
@@ -5178,6 +5179,62 @@ static void __init dsi_probe_pdata(struct platform_device *dsidev)
        }
 }
 
+static void __init dsi_probe_of(struct platform_device *pdev)
+{
+       struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *child;
+       struct omap_dss_device *dssdev;
+       u32 v;
+       enum omap_channel channel;
+       int r;
+
+       child = of_get_next_available_child(node, NULL);
+
+       if (!child)
+               return;
+
+       r = of_property_read_u32(node, "video-source", &v);
+       if (r) {
+               DSSERR("parsing channel failed\n");
+               return;
+       }
+
+       channel = v;
+
+       dssdev = dss_alloc_and_init_device(&pdev->dev);
+       if (!dssdev)
+               return;
+
+       dssdev->dev.of_node = child;
+       dssdev->type = OMAP_DISPLAY_TYPE_DSI;
+       dssdev->name = child->name;
+       dssdev->channel = channel;
+       dssdev->phy.dsi.module = dsi->module_id;
+
+       r = dsi_init_display(dssdev);
+       if (r) {
+               DSSERR("device %s init failed: %d\n", dssdev->name, r);
+               dss_put_device(dssdev);
+               return;
+       }
+
+       r = omapdss_output_set_device(&dsi->output, dssdev);
+       if (r) {
+               DSSERR("failed to connect output to new device: %s\n",
+                               dssdev->name);
+               dss_put_device(dssdev);
+               return;
+       }
+
+       r = dss_add_device(dssdev);
+       if (r) {
+               DSSERR("dss_add_device failed %d\n", r);
+               dss_put_device(dssdev);
+               return;
+       }
+}
+
 static void __init dsi_init_output(struct platform_device *dsidev)
 {
        struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
@@ -5212,7 +5269,19 @@ static int __init omap_dsihw_probe(struct platform_device *dsidev)
        if (!dsi)
                return -ENOMEM;
 
-       dsi->module_id = dsidev->id;
+       if (dsidev->dev.of_node) {
+               u32 id;
+               r = of_property_read_u32(dsidev->dev.of_node, "reg", &id);
+               if (r) {
+                       DSSERR("failed to read DSI module ID\n");
+                       return r;
+               }
+
+               dsi->module_id = id;
+       } else {
+               dsi->module_id = dsidev->id;
+       }
+
        dsi->pdev = dsidev;
        dev_set_drvdata(&dsidev->dev, dsi);
 
@@ -5295,8 +5364,6 @@ static int __init omap_dsihw_probe(struct platform_device *dsidev)
 
        dsi_init_output(dsidev);
 
-       dsi_probe_pdata(dsidev);
-
        dsi_runtime_put(dsidev);
 
        if (dsi->module_id == 0)
@@ -5310,6 +5377,12 @@ static int __init omap_dsihw_probe(struct platform_device *dsidev)
        else if (dsi->module_id == 1)
                dss_debugfs_create_file("dsi2_irqs", dsi2_dump_irqs);
 #endif
+
+       if (dsidev->dev.of_node)
+               dsi_probe_of(dsidev);
+       else if (dsidev->dev.platform_data)
+                dsi_probe_pdata(dsidev);
+
        return 0;
 
 err_runtime_get:
@@ -5368,12 +5441,24 @@ static const struct dev_pm_ops dsi_pm_ops = {
        .runtime_resume = dsi_runtime_resume,
 };
 
+#if defined(CONFIG_OF)
+static const struct of_device_id dsi_of_match[] = {
+       {
+               .compatible = "ti,omap4-dsi",
+       },
+       {},
+};
+#else
+#define dsi_of_match NULL
+#endif
+
 static struct platform_driver omap_dsihw_driver = {
        .remove         = __exit_p(omap_dsihw_remove),
        .driver         = {
                .name   = "omapdss_dsi",
                .owner  = THIS_MODULE,
                .pm     = &dsi_pm_ops,
+               .of_match_table = dsi_of_match,
        },
 };
 
index 054c2a22b3f1c650aca01037bd6d125d3a55235f..3cafa87e6cb814698466e89fbffa6ec92d4a5ccd 100644 (file)
@@ -157,7 +157,8 @@ static void dss_restore_context(void)
 
 int dss_get_ctx_loss_count(void)
 {
-       struct omap_dss_board_info *board_data = dss.pdev->dev.platform_data;
+       struct platform_device *core_pdev = dss_get_core_pdev();
+       struct omap_dss_board_info *board_data = core_pdev->dev.platform_data;
        int cnt;
 
        if (!board_data->get_context_loss_count)
@@ -1037,12 +1038,24 @@ static const struct dev_pm_ops dss_pm_ops = {
        .runtime_resume = dss_runtime_resume,
 };
 
+#if defined(CONFIG_OF)
+static const struct of_device_id dss_of_match[] = {
+       {
+               .compatible = "ti,omap4-dss",
+       },
+       {},
+};
+#else
+#define dss_of_match NULL
+#endif
+
 static struct platform_driver omap_dsshw_driver = {
        .remove         = __exit_p(omap_dsshw_remove),
        .driver         = {
                .name   = "omapdss_dss",
                .owner  = THIS_MODULE,
                .pm     = &dss_pm_ops,
+               .of_match_table = dss_of_match,
        },
 };
 
index 610c8e563daa2033750c3eec12f3328ad7db38e4..2d4c1fde79e6f0f2c4ae4b83420d992a46511970 100644 (file)
@@ -435,7 +435,7 @@ int venc_panel_init(void);
 void venc_panel_exit(void);
 
 /* HDMI */
-#ifdef CONFIG_OMAP4_DSS_HDMI
+#if defined(CONFIG_OMAP4_DSS_HDMI) || defined(CONFIG_OMAP5_DSS_HDMI)
 int hdmi_init_platform_driver(void) __init;
 void hdmi_uninit_platform_driver(void) __exit;
 unsigned long hdmi_get_pixel_clock(void);
@@ -456,9 +456,17 @@ int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev,
                                        struct omap_video_timings *timings);
 int omapdss_hdmi_read_edid(u8 *buf, int len);
 bool omapdss_hdmi_detect(void);
+int omapdss_hdmi_get_range(void);
+int omapdss_hdmi_set_range(int range);
+int omapdss_hdmi_get_deepcolor(void);
+int omapdss_hdmi_set_deepcolor(struct omap_dss_device *dssdev, int val,
+               bool hdmi_restart);
+int omapdss_hdmi_display_3d_enable(struct omap_dss_device *dssdev,
+                                       struct s3d_disp_info *info, int code);
 int hdmi_panel_init(void);
 void hdmi_panel_exit(void);
-#ifdef CONFIG_OMAP4_DSS_HDMI_AUDIO
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
 int hdmi_audio_enable(void);
 void hdmi_audio_disable(void);
 int hdmi_audio_start(void);
index d7d66ef5cb58098c6f80268651c0a277957fc2e0..a29f40168852c6ddf2110587802eb77d546d15d6 100644 (file)
@@ -791,7 +791,7 @@ static const struct omap_dss_features omap5_dss_features = {
        .burst_size_unit = 16,
 };
 
-#if defined(CONFIG_OMAP4_DSS_HDMI)
+#if defined(CONFIG_OMAP4_DSS_HDMI) || defined(CONFIG_OMAP5_DSS_HDMI)
 /* HDMI OMAP4 Functions*/
 static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
 
@@ -808,6 +808,8 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
        .dump_core              =       ti_hdmi_4xxx_core_dump,
        .dump_pll               =       ti_hdmi_4xxx_pll_dump,
        .dump_phy               =       ti_hdmi_4xxx_phy_dump,
+       .irq_handler            =       ti_hdmi_4xxx_irq_handler,
+       .irq_core_handler       =       ti_hdmi_4xxx_core_irq_handler,
 #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
        .audio_enable           =       ti_hdmi_4xxx_wp_audio_enable,
        .audio_disable          =       ti_hdmi_4xxx_wp_audio_disable,
@@ -819,6 +821,36 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
 
 };
 
+/* HDMI OMAP5 Functions*/
+static const struct ti_hdmi_ip_ops omap5_hdmi_functions = {
+#if defined(CONFIG_OMAP5_DSS_HDMI)
+       .video_configure        =       ti_hdmi_5xxx_basic_configure,
+       .read_edid              =       ti_hdmi_5xxx_read_edid,
+       .irq_core_handler       =       ti_hdmi_5xxx_core_irq_handler,
+       .dump_core              =       ti_hdmi_5xxx_core_dump,
+       .configure_range        =       ti_hdmi_5xxx_configure_range,
+#endif
+       .phy_enable             =       ti_hdmi_4xxx_phy_enable,
+       .phy_disable            =       ti_hdmi_4xxx_phy_disable,
+       .detect                 =       ti_hdmi_4xxx_detect,
+       .pll_enable             =       ti_hdmi_4xxx_pll_enable,
+       .pll_disable            =       ti_hdmi_4xxx_pll_disable,
+       .video_enable           =       ti_hdmi_4xxx_wp_video_start,
+       .video_disable          =       ti_hdmi_4xxx_wp_video_stop,
+       .dump_wrapper           =       ti_hdmi_4xxx_wp_dump,
+       .dump_pll               =       ti_hdmi_4xxx_pll_dump,
+       .dump_phy               =       ti_hdmi_4xxx_phy_dump,
+       .irq_handler            =       ti_hdmi_4xxx_irq_handler,
+#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+       .audio_enable           =       ti_hdmi_4xxx_wp_audio_enable,
+       .audio_disable          =       ti_hdmi_4xxx_wp_audio_disable,
+       .audio_start            =       ti_hdmi_5xxx_audio_start,
+       .audio_stop             =       ti_hdmi_5xxx_audio_stop,
+       .audio_config           =       ti_hdmi_5xxx_audio_config,
+       .audio_get_dma_port     =       ti_hdmi_4xxx_audio_get_dma_port,
+#endif
+};
+
 void dss_init_hdmi_ip_ops(struct hdmi_ip_data *ip_data,
                enum omapdss_version version)
 {
@@ -828,6 +860,9 @@ void dss_init_hdmi_ip_ops(struct hdmi_ip_data *ip_data,
        case OMAPDSS_VER_OMAP4:
                ip_data->ops = &omap4_hdmi_functions;
                break;
+       case OMAPDSS_VER_OMAP5:
+               ip_data->ops = &omap5_hdmi_functions;
+               break;
        default:
                ip_data->ops = NULL;
        }
index 489b9bec4a6d5ceebbc0cc01caaf9b96b7583673..188ceececf5082a21e261f533948367c14d5542c 100644 (file)
@@ -20,7 +20,7 @@
 #ifndef __OMAP2_DSS_FEATURES_H
 #define __OMAP2_DSS_FEATURES_H
 
-#if defined(CONFIG_OMAP4_DSS_HDMI)
+#if defined(CONFIG_OMAP4_DSS_HDMI) || defined(CONFIG_OMAP5_DSS_HDMI)
 #include "ti_hdmi.h"
 #endif
 
@@ -117,7 +117,7 @@ bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type);
 bool dss_has_feature(enum dss_feat_id id);
 void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end);
 void dss_features_init(enum omapdss_version version);
-#if defined(CONFIG_OMAP4_DSS_HDMI)
+#if defined(CONFIG_OMAP4_DSS_HDMI) || defined(CONFIG_OMAP5_DSS_HDMI)
 void dss_init_hdmi_ip_ops(struct hdmi_ip_data *ip_data,
                enum omapdss_version version);
 #endif
index 769d0828581ccfec1f350b49b8d1d791732e9b04..1ad294ad52f493e8072c1902378d404c6a76f0bf 100644 (file)
 #include <linux/clk.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/of_gpio.h>
 #include <video/omapdss.h>
 
 #include "ti_hdmi.h"
 #include "dss.h"
 #include "dss_features.h"
 
-#define HDMI_WP                        0x0
-#define HDMI_CORE_SYS          0x400
-#define HDMI_CORE_AV           0x900
-#define HDMI_PLLCTRL           0x200
-#define HDMI_PHY               0x300
-
 /* HDMI EDID Length move this */
 #define HDMI_EDID_MAX_LENGTH                   256
 #define EDID_TIMING_DESCRIPTOR_SIZE            0x12
 static struct {
        struct mutex lock;
        struct platform_device *pdev;
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+       struct platform_device *audio_pdev;
+#endif
 
        struct hdmi_ip_data ip_data;
+       int hdmi_irq;
 
        struct clk *sys_clk;
        struct regulator *vdda_hdmi_dac_reg;
@@ -304,6 +305,27 @@ static const struct hdmi_config vesa_timings[] = {
        },
 };
 
+static const struct hdmi_config s3d_timings[] = {
+       {
+               { 1280, 1470, 148500, 40, 110, 220, 5, 5, 20,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 4, HDMI_HDMI },
+       },
+       {
+               { 1280, 1470, 148500, 40, 440, 220, 5, 5, 20,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 19, HDMI_HDMI },
+       },
+       {
+               { 1920, 2205, 148500, 44, 638, 148, 5, 4, 36,
+                       OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+                       false, },
+               { 32, HDMI_HDMI },
+       },
+};
+
 static int hdmi_runtime_get(void)
 {
        int r;
@@ -340,8 +362,6 @@ static int __init hdmi_init_display(struct omap_dss_device *dssdev)
 
        DSSDBG("init_display\n");
 
-       dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version());
-
        if (hdmi.vdda_hdmi_dac_reg == NULL) {
                struct regulator *reg;
 
@@ -393,15 +413,20 @@ static const struct hdmi_config *hdmi_get_timings(void)
        const struct hdmi_config *arr;
        int len;
 
-       if (hdmi.ip_data.cfg.cm.mode == HDMI_DVI) {
-               arr = vesa_timings;
-               len = ARRAY_SIZE(vesa_timings);
-       } else {
-               arr = cea_timings;
-               len = ARRAY_SIZE(cea_timings);
-       }
+       if (!hdmi.ip_data.cfg.s3d_enabled) {
+               if (hdmi.ip_data.cfg.cm.mode == HDMI_DVI) {
+                       arr = vesa_timings;
+                       len = ARRAY_SIZE(vesa_timings);
+               } else {
+                       arr = cea_timings;
+                       len = ARRAY_SIZE(cea_timings);
+               }
+       } else {
+               arr = s3d_timings;
+               len = ARRAY_SIZE(s3d_timings);
+       }
 
-       return hdmi_find_timing(arr, len);
+       return hdmi_find_timing(arr, len);
 }
 
 static bool hdmi_timings_compare(struct omap_video_timings *timing1,
@@ -450,6 +475,12 @@ static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing)
                        goto end;
                }
        }
+       for (i = 0; i < ARRAY_SIZE(s3d_timings); i++) {
+               if (hdmi_timings_compare(timing, &s3d_timings[i].timings)) {
+                       cm = s3d_timings[i].cm;
+                       goto end;
+               }
+       }
 
 end:   return cm;
 
@@ -465,6 +496,7 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
                struct hdmi_pll_info *pi)
 {
        unsigned long clkin, refclk;
+       enum omapdss_version version = omapdss_get_version();
        u32 mf;
 
        clkin = clk_get_rate(hdmi.sys_clk) / 10000;
@@ -479,10 +511,28 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
 
        refclk = clkin / pi->regn;
 
-       if (dssdev->clocks.hdmi.regm2 == 0)
-               pi->regm2 = HDMI_DEFAULT_REGM2;
-       else
+       if (dssdev->clocks.hdmi.regm2 == 0) {
+               switch (version)
+               {
+               case OMAPDSS_VER_OMAP4430_ES1:
+               case OMAPDSS_VER_OMAP4430_ES2:
+               case OMAPDSS_VER_OMAP4:
+                       pi->regm2 = HDMI_DEFAULT_REGM2;
+                       break;
+               case OMAPDSS_VER_OMAP5:
+                       if (phy <= 50000)
+                               pi->regm2 = 5;
+                       else
+                               pi->regm2 = 1;
+                       break;
+               default:
+                       DSSWARN("invalid omapdss version");
+                       break;
+
+               }
+       } else {
                pi->regm2 = dssdev->clocks.hdmi.regm2;
+       }
 
        /*
         * multiplier is pixel_clk/ref_clk
@@ -516,8 +566,15 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev)
 {
        int r;
 
-       gpio_set_value(hdmi.ct_cp_hpd_gpio, 1);
-       gpio_set_value(hdmi.ls_oe_gpio, 1);
+       if (gpio_cansleep(hdmi.ct_cp_hpd_gpio))
+               gpio_set_value_cansleep(hdmi.ct_cp_hpd_gpio, 1);
+       else
+               gpio_set_value(hdmi.ct_cp_hpd_gpio, 1);
+
+       if (gpio_cansleep(hdmi.ls_oe_gpio))
+               gpio_set_value_cansleep(hdmi.ls_oe_gpio, 1);
+       else
+               gpio_set_value(hdmi.ls_oe_gpio, 1);
 
        /* wait 300us after CT_CP_HPD for the 5V power output to reach 90% */
        udelay(300);
@@ -538,8 +595,15 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev)
 err_runtime_get:
        regulator_disable(hdmi.vdda_hdmi_dac_reg);
 err_vdac_enable:
-       gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
-       gpio_set_value(hdmi.ls_oe_gpio, 0);
+       if (gpio_cansleep(hdmi.ct_cp_hpd_gpio))
+               gpio_set_value_cansleep(hdmi.ct_cp_hpd_gpio, 0);
+       else
+               gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
+
+       if (gpio_cansleep(hdmi.ls_oe_gpio))
+               gpio_set_value_cansleep(hdmi.ls_oe_gpio, 0);
+       else
+               gpio_set_value(hdmi.ls_oe_gpio, 0);
        return r;
 }
 
@@ -547,8 +611,15 @@ static void hdmi_power_off_core(struct omap_dss_device *dssdev)
 {
        hdmi_runtime_put();
        regulator_disable(hdmi.vdda_hdmi_dac_reg);
-       gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
-       gpio_set_value(hdmi.ls_oe_gpio, 0);
+       if (gpio_cansleep(hdmi.ct_cp_hpd_gpio))
+               gpio_set_value_cansleep(hdmi.ct_cp_hpd_gpio, 0);
+       else
+               gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
+
+       if (gpio_cansleep(hdmi.ls_oe_gpio))
+               gpio_set_value_cansleep(hdmi.ls_oe_gpio, 0);
+       else
+               gpio_set_value(hdmi.ls_oe_gpio, 0);
 }
 
 static int hdmi_power_on_full(struct omap_dss_device *dssdev)
@@ -568,7 +639,23 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
 
        DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
 
-       phy = p->pixel_clock;
+       switch (hdmi.ip_data.cfg.deep_color) {
+       case HDMI_DEEP_COLOR_30BIT:
+               phy = (p->pixel_clock * 125) / 100 ;
+               break;
+       case HDMI_DEEP_COLOR_36BIT:
+               if (p->pixel_clock >= 148500) {
+                       DSSERR("36 bit deep color not supported for the pixel clock %d\n",
+                               p->pixel_clock);
+                       goto err_deep_color;
+               }
+               phy = (p->pixel_clock * 150) / 100;
+               break;
+       case HDMI_DEEP_COLOR_24BIT:
+       default:
+               phy = p->pixel_clock;
+               break;
+       }
 
        hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data);
 
@@ -612,6 +699,7 @@ err_vid_enable:
 err_phy_enable:
        hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
 err_pll_enable:
+err_deep_color:
        hdmi_power_off_core(dssdev);
        return -EIO;
 }
@@ -626,9 +714,65 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev)
        hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
        hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
 
+       hdmi.ip_data.cfg.deep_color = HDMI_DEEP_COLOR_24BIT;
+
        hdmi_power_off_core(dssdev);
 }
 
+int omapdss_hdmi_set_deepcolor(struct omap_dss_device *dssdev, int val,
+               bool hdmi_restart)
+{
+       int r;
+
+       if (!hdmi_restart) {
+               hdmi.ip_data.cfg.deep_color = val;
+               return 0;
+       }
+
+       omapdss_hdmi_display_disable(dssdev);
+
+       hdmi.ip_data.cfg.deep_color = val;
+
+       r = omapdss_hdmi_display_enable(dssdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+int omapdss_hdmi_get_deepcolor(void)
+{
+       return hdmi.ip_data.cfg.deep_color;
+}
+
+int omapdss_hdmi_set_range(int range)
+{
+       int r = 0;
+       enum hdmi_range old_range;
+
+       old_range = hdmi.ip_data.cfg.range;
+       hdmi.ip_data.cfg.range = range;
+
+       /* HDMI 1.3 section 6.6 VGA (640x480) format requires Full Range */
+       if ((range == 0) &&
+               ((hdmi.ip_data.cfg.cm.code == 4 &&
+               hdmi.ip_data.cfg.cm.mode == HDMI_DVI) ||
+               (hdmi.ip_data.cfg.cm.code == 1 &&
+               hdmi.ip_data.cfg.cm.mode == HDMI_HDMI)))
+                       return -EINVAL;
+
+       r = hdmi.ip_data.ops->configure_range(&hdmi.ip_data);
+       if (r)
+               hdmi.ip_data.cfg.range = old_range;
+
+       return r;
+}
+
+int omapdss_hdmi_get_range(void)
+{
+       return hdmi.ip_data.cfg.range;
+}
+
 int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev,
                                        struct omap_video_timings *timings)
 {
@@ -643,6 +787,100 @@ int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev,
 
 }
 
+int omapdss_hdmi_display_3d_enable(struct omap_dss_device *dssdev,
+                                       struct s3d_disp_info *info, int code)
+{
+       struct omap_dss_output *out = dssdev->output;
+       int r = 0;
+
+       DSSDBG("ENTER hdmi_display_3d_enable\n");
+
+       mutex_lock(&hdmi.lock);
+
+       if (out == NULL || out->manager == NULL) {
+               DSSERR("failed to enable display: no output/manager\n");
+               r = -ENODEV;
+               goto err0;
+       }
+
+       r = omap_dss_start_device(dssdev);
+       if (r) {
+               DSSERR("failed to start device\n");
+               goto err0;
+       }
+
+       if (dssdev->platform_enable) {
+               r = dssdev->platform_enable(dssdev);
+               if (r) {
+                       DSSERR("failed to enable GPIO's\n");
+                       goto err1;
+               }
+       }
+
+       /* hdmi.s3d_enabled will be updated when powering display up */
+       /* if there's no S3D support it will be reset to false */
+       switch (info->type) {
+       case S3D_DISP_OVERUNDER:
+               if (info->sub_samp == S3D_DISP_SUB_SAMPLE_NONE) {
+                       dssdev->panel.s3d_info = *info;
+                       hdmi.ip_data.cfg.s3d_info.frame_struct =
+                               HDMI_S3D_FRAME_PACKING;
+                       hdmi.ip_data.cfg.s3d_info.subsamp = false;
+                       hdmi.ip_data.cfg.s3d_info.subsamp_pos = 0;
+                       hdmi.ip_data.cfg.s3d_enabled = true;
+                       hdmi.ip_data.cfg.s3d_info.vsi_enabled = true;
+               } else {
+                       goto err2;
+               }
+               break;
+       case S3D_DISP_SIDEBYSIDE:
+               dssdev->panel.s3d_info = *info;
+               if (info->sub_samp == S3D_DISP_SUB_SAMPLE_NONE) {
+                       hdmi.ip_data.cfg.s3d_info.frame_struct =
+                               HDMI_S3D_SIDE_BY_SIDE_FULL;
+                       hdmi.ip_data.cfg.s3d_info.subsamp = true;
+                       hdmi.ip_data.cfg.s3d_info.subsamp_pos =
+                               HDMI_S3D_HOR_EL_ER;
+                       hdmi.ip_data.cfg.s3d_enabled = true;
+                       hdmi.ip_data.cfg.s3d_info.vsi_enabled = true;
+               } else if (info->sub_samp == S3D_DISP_SUB_SAMPLE_H) {
+                       hdmi.ip_data.cfg.s3d_info.frame_struct =
+                               HDMI_S3D_SIDE_BY_SIDE_HALF;
+                       hdmi.ip_data.cfg.s3d_info.subsamp = true;
+                       hdmi.ip_data.cfg.s3d_info.subsamp_pos =
+                               HDMI_S3D_HOR_EL_ER;
+                       hdmi.ip_data.cfg.s3d_info.vsi_enabled = true;
+               } else {
+                       goto err2;
+               }
+               break;
+       default:
+               goto err2;
+       }
+       if (hdmi.ip_data.cfg.s3d_enabled) {
+               hdmi.ip_data.cfg.cm.code = code;
+               hdmi.ip_data.cfg.cm.mode = HDMI_HDMI;
+       }
+
+       r = hdmi_power_on_full(dssdev);
+       if (r) {
+               DSSERR("failed to power on device\n");
+               goto err2;
+       }
+
+       mutex_unlock(&hdmi.lock);
+       return 0;
+
+err2:
+       if (dssdev->platform_disable)
+               dssdev->platform_disable(dssdev);
+err1:
+       omap_dss_stop_device(dssdev);
+err0:
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+
 void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev,
                struct omap_video_timings *timings)
 {
@@ -800,6 +1038,19 @@ void omapdss_hdmi_core_disable(struct omap_dss_device *dssdev)
        mutex_unlock(&hdmi.lock);
 }
 
+static irqreturn_t hdmi_irq_handler(int irq, void *arg)
+{
+       int r = 0;
+
+       r = hdmi.ip_data.ops->irq_handler(&hdmi.ip_data);
+       DSSDBG("Received HDMI IRQ = %08x\n", r);
+
+       r = hdmi.ip_data.ops->irq_core_handler(&hdmi.ip_data);
+       DSSDBG("Received HDMI core IRQ = %08x\n", r);
+
+       return IRQ_HANDLED;
+}
+
 static int hdmi_get_clocks(struct platform_device *pdev)
 {
        struct clk *clk;
@@ -821,103 +1072,150 @@ static void hdmi_put_clocks(void)
                clk_put(hdmi.sys_clk);
 }
 
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+static int hdmi_probe_audio(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct platform_device *aud_pdev;
+       u32 port_offset, port_size;
+       struct resource aud_res[2] = {
+               DEFINE_RES_MEM(-1, -1),
+               DEFINE_RES_DMA(-1),
+       };
+
+       res = platform_get_resource(hdmi.pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               DSSERR("can't get IORESOURCE_MEM HDMI\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Pass DMA audio port to audio drivers.
+        * Audio drivers should not ioremap it.
+        */
+       hdmi.ip_data.ops->audio_get_dma_port(&port_offset, &port_size);
+
+       aud_res[0].start = res->start + port_offset;
+       aud_res[0].end =  aud_res[0].start + port_size - 1;
+
+       res = platform_get_resource(hdmi.pdev, IORESOURCE_DMA, 0);
+       if (!res) {
+               DSSERR("can't get IORESOURCE_DMA HDMI\n");
+               return -EINVAL;
+       }
+
+       /* Pass the audio DMA request resource to audio drivers. */
+       aud_res[1].start = res->start;
+
+       /* create platform device for HDMI audio driver */
+       aud_pdev = platform_device_register_simple("omap-hdmi-audio",
+                                                  pdev->id, aud_res,
+                                                  ARRAY_SIZE(aud_res));
+       if (IS_ERR(aud_pdev)) {
+               DSSERR("Can't instantiate hdmi-audio\n");
+               return -ENODEV;
+       }
+
+       hdmi.audio_pdev = aud_pdev;
+
+       return 0;
+}
+
 int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts)
 {
+       int r;
        u32 deep_color;
-       bool deep_color_correct = false;
        u32 pclk = hdmi.ip_data.cfg.timings.pixel_clock;
 
-       if (n == NULL || cts == NULL)
+       if (n == NULL || cts == NULL || sample_freq == 0)
                return -EINVAL;
 
        /* TODO: When implemented, query deep color mode here. */
        deep_color = 100;
 
-       /*
-        * When using deep color, the default N value (as in the HDMI
-        * specification) yields to an non-integer CTS. Hence, we
-        * modify it while keeping the restrictions described in
-        * section 7.2.1 of the HDMI 1.4a specification.
-        */
        switch (sample_freq) {
        case 32000:
-       case 48000:
-       case 96000:
-       case 192000:
-               if (deep_color == 125)
-                       if (pclk == 27027 || pclk == 74250)
-                               deep_color_correct = true;
-               if (deep_color == 150)
-                       if (pclk == 27027)
-                               deep_color_correct = true;
-               break;
-       case 44100:
-       case 88200:
-       case 176400:
-               if (deep_color == 125)
-                       if (pclk == 27027)
-                               deep_color_correct = true;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       if (deep_color_correct) {
-               switch (sample_freq) {
-               case 32000:
+               if (deep_color == 125 && pclk == 74250) {
                        *n = 8192;
                        break;
-               case 44100:
-                       *n = 12544;
+               }
+
+               if (deep_color == 125 && pclk == 27027) {
+                       /*
+                        * For this specific configuration, no value within the
+                        * allowed interval of N (as per the HDMI spec) will
+                        * produce an integer value of CTS. The value we use
+                        * here will produce CTS = 11587.000427246, which is
+                        * slightly larger than the integer. This difference
+                        * could cause the audio clock at the sink to slowly
+                        * drift. The true solution requires alternating between
+                        * two CTS relevant values with careful timing in order
+                        * to, on average, obtain the true CTS float value.
+                       */
+                       *n = 13529;
                        break;
-               case 48000:
+               }
+
+               if (deep_color == 150 && pclk == 27027) {
                        *n = 8192;
                        break;
-               case 88200:
-                       *n = 25088;
-                       break;
-               case 96000:
-                       *n = 16384;
-                       break;
-               case 176400:
-                       *n = 50176;
-                       break;
-               case 192000:
-                       *n = 32768;
-                       break;
-               default:
-                       return -EINVAL;
                }
-       } else {
-               switch (sample_freq) {
-               case 32000:
-                       *n = 4096;
-                       break;
-               case 44100:
-                       *n = 6272;
-                       break;
-               case 48000:
-                       *n = 6144;
-                       break;
-               case 88200:
+
+               *n = 4096;
+               break;
+       case 44100:
+               if (deep_color == 125 && pclk == 27027) {
                        *n = 12544;
                        break;
-               case 96000:
-                       *n = 12288;
-                       break;
-               case 176400:
-                       *n = 25088;
+               }
+
+               *n = 6272;
+               break;
+       case 48000:
+               if (deep_color == 125 && (pclk == 27027 || pclk == 74250)) {
+                       *n = 8192;
                        break;
-               case 192000:
-                       *n = 24576;
+               }
+
+               if (deep_color == 150 && pclk == 27027) {
+                       *n = 8192;
                        break;
-               default:
-                       return -EINVAL;
                }
+
+               *n = 6144;
+               break;
+       case 88200:
+               r = hdmi_compute_acr(44100, n, cts);
+               *n *= 2;
+               return r;
+       case 96000:
+               r = hdmi_compute_acr(48000, n, cts);
+               *n *= 2;
+               return r;
+       case 176400:
+               r = hdmi_compute_acr(44100, n, cts);
+               *n *= 4;
+               return r;
+       case 192000:
+               r = hdmi_compute_acr(48000, n, cts);
+               *n *= 4;
+               return r;
+       default:
+               return -EINVAL;
        }
-       /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */
-       *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10);
+
+       /*
+        * Calculate CTS. See HDMI 1.3a or 1.4a specifications. Preserve the
+        * remainder in case N is not a multiple of 128.
+        */
+       *cts = (*n / 128) * pclk * deep_color;
+       *cts += (*n % 128) * pclk * deep_color / 128;
+       *cts /= (sample_freq / 10);
+
+       if ((pclk * (*n / 128) * deep_color) % (sample_freq / 10))
+               DSSWARN("CTS is not integer fs[%u]pclk[%u]N[%u]\n",
+                       sample_freq, pclk, *n);
 
        return 0;
 }
@@ -1044,6 +1342,92 @@ static void __init hdmi_probe_pdata(struct platform_device *pdev)
        }
 }
 
+static void __init hdmi_probe_of(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *child;
+       struct omap_dss_device *dssdev;
+       int r, gpio;
+       enum omap_channel channel;
+       u32 v;
+
+       r = of_property_read_u32(node, "video-source", &v);
+       if (r) {
+               DSSERR("parsing channel failed\n");
+               return;
+       }
+
+       channel = v;
+
+       node = of_find_compatible_node(node, NULL, "ti,tpd12s015");
+       if (!node)
+               return;
+
+       child = of_get_next_available_child(node, NULL);
+       if (!child)
+               return;
+
+       if (of_gpio_count(node) != 3) {
+               DSSERR("wrong number of GPIOs\n");
+               return;
+       }
+
+       gpio = of_get_gpio(node, 0);
+       if (gpio_is_valid(gpio)) {
+               hdmi.ct_cp_hpd_gpio = gpio;
+       } else {
+               DSSERR("failed to parse CT CP HPD gpio\n");
+               return;
+       }
+
+       gpio = of_get_gpio(node, 1);
+       if (gpio_is_valid(gpio)) {
+               hdmi.ls_oe_gpio = gpio;
+       } else {
+               DSSERR("failed to parse LS OE gpio\n");
+               return;
+       }
+
+       gpio = of_get_gpio(node, 2);
+       if (gpio_is_valid(gpio)) {
+               hdmi.hpd_gpio = gpio;
+       } else {
+               DSSERR("failed to parse HPD gpio\n");
+               return;
+       }
+
+       dssdev = dss_alloc_and_init_device(&pdev->dev);
+       if (!dssdev)
+               return;
+
+       dssdev->dev.of_node = child;
+       dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
+       dssdev->name = child->name;
+       dssdev->channel = channel;
+
+       r = hdmi_init_display(dssdev);
+       if (r) {
+               DSSERR("device %s init failed: %d\n", dssdev->name, r);
+               dss_put_device(dssdev);
+               return;
+       }
+
+       r = omapdss_output_set_device(&hdmi.output, dssdev);
+       if (r) {
+               DSSERR("failed to connect output to new device: %s\n",
+                               dssdev->name);
+               dss_put_device(dssdev);
+               return;
+       }
+
+       r = dss_add_device(dssdev);
+       if (r) {
+               DSSERR("dss_add_device failed %d\n", r);
+               dss_put_device(dssdev);
+               return;
+       }
+}
+
 static void __init hdmi_init_output(struct platform_device *pdev)
 {
        struct omap_dss_output *out = &hdmi.output;
@@ -1073,9 +1457,13 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev)
        mutex_init(&hdmi.lock);
        mutex_init(&hdmi.ip_data.lock);
 
-       res = platform_get_resource(hdmi.pdev, IORESOURCE_MEM, 0);
+       dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version());
+
+       /* HDMI wrapper memory remap */
+       res = platform_get_resource_byname(hdmi.pdev,
+                                          IORESOURCE_MEM, "hdmi_wp");
        if (!res) {
-               DSSERR("can't get IORESOURCE_MEM HDMI\n");
+               DSSERR("can't get WP IORESOURCE_MEM HDMI\n");
                return -EINVAL;
        }
 
@@ -1086,6 +1474,48 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       /* HDMI PLLCTRL memory remap */
+       res = platform_get_resource_byname(hdmi.pdev,
+                                          IORESOURCE_MEM, "pllctrl");
+       if (!res) {
+               DSSERR("can't get PLL CTRL IORESOURCE_MEM HDMI\n");
+               return -EINVAL;
+       }
+
+       hdmi.ip_data.base_pllctrl = devm_request_and_ioremap(&pdev->dev, res);
+       if (!hdmi.ip_data.base_pllctrl) {
+               DSSERR("can't ioremap PLL ctrl\n");
+               return -ENOMEM;
+       }
+
+       /* HDMI TXPHYCTRL memory remap */
+       res = platform_get_resource_byname(hdmi.pdev,
+                                          IORESOURCE_MEM, "hdmitxphy");
+       if (!res) {
+               DSSERR("can't get TXPHY CTRL IORESOURCE_MEM HDMI\n");
+               return -EINVAL;
+       }
+
+       hdmi.ip_data.base_txphyctrl = devm_request_and_ioremap(&pdev->dev, res);
+       if (!hdmi.ip_data.base_txphyctrl) {
+               DSSERR("can't ioremap TXPHY ctrl\n");
+               return -ENOMEM;
+       }
+
+       /* HDMI core memory remap */
+       res = platform_get_resource_byname(hdmi.pdev,
+                                          IORESOURCE_MEM, "hdmi_core");
+       if (!res) {
+               DSSERR("can't get core IORESOURCE_MEM HDMI\n");
+               return -EINVAL;
+       }
+
+       hdmi.ip_data.base_core = devm_request_and_ioremap(&pdev->dev, res);
+       if (!hdmi.ip_data.base_core) {
+               DSSERR("can't ioremap core\n");
+               return -ENOMEM;
+       }
+
        r = hdmi_get_clocks(pdev);
        if (r) {
                DSSERR("can't get clocks\n");
@@ -1094,10 +1524,12 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev)
 
        pm_runtime_enable(&pdev->dev);
 
-       hdmi.ip_data.core_sys_offset = HDMI_CORE_SYS;
-       hdmi.ip_data.core_av_offset = HDMI_CORE_AV;
-       hdmi.ip_data.pll_offset = HDMI_PLLCTRL;
-       hdmi.ip_data.phy_offset = HDMI_PHY;
+       hdmi.hdmi_irq = platform_get_irq(pdev, 0);
+       r = request_irq(hdmi.hdmi_irq, hdmi_irq_handler, 0, "OMAP HDMI", NULL);
+       if (r < 0) {
+               pr_err("hdmi: request_irq %s failed\n", pdev->name);
+               return -EINVAL;
+       }
 
        r = hdmi_panel_init();
        if (r) {
@@ -1109,7 +1541,17 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev)
 
        hdmi_init_output(pdev);
 
-       hdmi_probe_pdata(pdev);
+       if (pdev->dev.of_node)
+               hdmi_probe_of(pdev);
+       else if (pdev->dev.platform_data)
+               hdmi_probe_pdata(pdev);
+
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+       r = hdmi_probe_audio(pdev);
+       if (r)
+               DSSWARN("could not create platform device for audio");
+#endif
 
        return 0;
 
@@ -1127,6 +1569,12 @@ static int __exit hdmi_remove_child(struct device *dev, void *data)
 
 static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
 {
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+       if (hdmi.audio_pdev != NULL)
+               platform_device_unregister(hdmi.audio_pdev);
+#endif
+
        device_for_each_child(&pdev->dev, NULL, hdmi_remove_child);
 
        dss_unregister_child_devices(&pdev->dev);
@@ -1169,12 +1617,24 @@ static const struct dev_pm_ops hdmi_pm_ops = {
        .runtime_resume = hdmi_runtime_resume,
 };
 
+#if defined(CONFIG_OF)
+static const struct of_device_id hdmi_of_match[] = {
+       {
+               .compatible = "ti,omap4-hdmi",
+       },
+       {},
+};
+#else
+#define hdmi_of_match NULL
+#endif
+
 static struct platform_driver omapdss_hdmihw_driver = {
        .remove         = __exit_p(omapdss_hdmihw_remove),
        .driver         = {
                .name   = "omapdss_hdmi",
                .owner  = THIS_MODULE,
                .pm     = &hdmi_pm_ops,
+               .of_match_table = hdmi_of_match,
        },
 };
 
index dfb8eda81b61d85169e55b424fa92dbc5a7ce8c5..9574f4dbddc9661ef7bd3b4b37c8adf90aa7cbf6 100644 (file)
 #include <linux/module.h>
 #include <video/omapdss.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 #include "dss.h"
 
 static struct {
        /* This protects the panel ops, mainly when accessing the HDMI IP. */
        struct mutex lock;
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+       struct omap_dss_device *dssdev;
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
        /* This protects the audio ops, specifically. */
        spinlock_t audio_lock;
 #endif
 } hdmi;
 
+static ssize_t hdmi_deepcolor_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       int deepcolor;
+
+       deepcolor = omapdss_hdmi_get_deepcolor();
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", deepcolor);
+}
+
+static ssize_t hdmi_deepcolor_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t size)
+{
+       int r, deepcolor, curr_deepcolor;
+
+       r = kstrtoint(buf, 0, &deepcolor);
+       if (r || deepcolor > 3)
+               return -EINVAL;
+
+       curr_deepcolor = omapdss_hdmi_get_deepcolor();
+
+       if (deepcolor == curr_deepcolor)
+               return size;
+
+       if (hdmi.dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+               r = omapdss_hdmi_set_deepcolor(hdmi.dssdev, deepcolor, false);
+       else
+               r = omapdss_hdmi_set_deepcolor(hdmi.dssdev, deepcolor, true);
+       if (r)
+               return r;
+
+       return size;
+}
+
+static DEVICE_ATTR(deepcolor, S_IRUGO | S_IWUSR, hdmi_deepcolor_show,
+                       hdmi_deepcolor_store);
+
+static ssize_t hdmi_range_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       int r;
+
+       r = omapdss_hdmi_get_range();
+       return snprintf(buf, PAGE_SIZE, "%d\n", r);
+}
+
+static ssize_t hdmi_range_store(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf, size_t size)
+{
+       unsigned long range;
+       int r = kstrtoul(buf, 0, &range);
+
+       if (r || range > 1)
+               return -EINVAL;
+
+       r = omapdss_hdmi_set_range(range);
+       if (r)
+               return r;
+       return size;
+}
+
+static DEVICE_ATTR(range, S_IRUGO | S_IWUSR, hdmi_range_show, hdmi_range_store);
 
 static int hdmi_panel_probe(struct omap_dss_device *dssdev)
 {
@@ -63,6 +130,16 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev)
 
        dssdev->panel.timings = default_timings;
 
+       /* sysfs entry to provide user space control to set
+        * quantization range
+        */
+       if (device_create_file(&dssdev->dev, &dev_attr_range))
+               DSSERR("failed to create sysfs file\n");
+
+       /* sysfs entry to provide user space control to set deepcolor mode */
+       if (device_create_file(&dssdev->dev, &dev_attr_deepcolor))
+               DSSERR("failed to create sysfs file\n");
+
        DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n",
                dssdev->panel.timings.x_res,
                dssdev->panel.timings.y_res);
@@ -74,10 +151,12 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev)
 
 static void hdmi_panel_remove(struct omap_dss_device *dssdev)
 {
-
+       device_remove_file(&dssdev->dev, &dev_attr_deepcolor);
+       device_remove_file(&dssdev->dev, &dev_attr_range);
 }
 
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
 static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev)
 {
        unsigned long flags;
@@ -111,7 +190,8 @@ static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev)
 
        spin_lock_irqsave(&hdmi.audio_lock, flags);
 
-       hdmi_audio_disable();
+       if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
+               hdmi_audio_disable();
 
        dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED;
 
@@ -261,6 +341,33 @@ err:
        return r;
 }
 
+static int hdmi_panel_3d_enable(struct omap_dss_device *dssdev,
+                               struct s3d_disp_info *info, int code)
+{
+       int r = 0;
+       DSSDBG("ENTER hdmi_panel_3d_enable\n");
+
+       mutex_lock(&hdmi.lock);
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+               r = -EINVAL;
+               goto err;
+       }
+
+       r = omapdss_hdmi_display_3d_enable(dssdev, info, code);
+       if (r) {
+               DSSERR("failed to power on\n");
+               goto err;
+       }
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+err:
+       mutex_unlock(&hdmi.lock);
+
+       return r;
+}
+
 static void hdmi_panel_disable(struct omap_dss_device *dssdev)
 {
        mutex_lock(&hdmi.lock);
@@ -374,6 +481,19 @@ err:
        return r;
 }
 
+#if defined(CONFIG_OF)
+static const struct of_device_id hdmi_panel_of_match[] = {
+       {
+               .compatible = "ti,hdmi_panel",
+       },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, hdmi_panel_of_match);
+#else
+#define dss_of_match NULL
+#endif
+
 static struct omap_dss_driver hdmi_driver = {
        .probe          = hdmi_panel_probe,
        .remove         = hdmi_panel_remove,
@@ -390,9 +510,11 @@ static struct omap_dss_driver hdmi_driver = {
        .audio_stop     = hdmi_panel_audio_stop,
        .audio_supported        = hdmi_panel_audio_supported,
        .audio_config   = hdmi_panel_audio_config,
+       .s3d_enable     = hdmi_panel_3d_enable,
        .driver                 = {
                .name   = "hdmi_panel",
                .owner  = THIS_MODULE,
+               .of_match_table = hdmi_panel_of_match,
        },
 };
 
@@ -400,7 +522,8 @@ int hdmi_panel_init(void)
 {
        mutex_init(&hdmi.lock);
 
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined (CONFIG_OMAP5_DSS_HDMI_AUDIO)
        spin_lock_init(&hdmi.audio_lock);
 #endif
 
index 216aa704f9d72ef08b06a82865d5b8fda03cc876..02eae1508d0f7216917622a75eb551d590e63826 100644 (file)
 #define _TI_HDMI_H
 
 struct hdmi_ip_data;
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+struct hdmi_audio_dma;
+#endif
 
 enum hdmi_pll_pwr {
        HDMI_PLLPWRCMD_ALLOFF = 0,
@@ -42,6 +46,48 @@ enum hdmi_clk_refsel {
        HDMI_REFSEL_SYSCLK = 3
 };
 
+enum hdmi_deep_color_mode {
+       HDMI_DEEP_COLOR_24BIT = 0,
+       HDMI_DEEP_COLOR_30BIT = 1,
+       HDMI_DEEP_COLOR_36BIT = 2,
+};
+
+enum hdmi_range {
+       HDMI_LIMITED_RANGE = 0,
+       HDMI_FULL_RANGE,
+};
+
+enum hdmi_s3d_frame_structure {
+       HDMI_S3D_FRAME_PACKING          = 0,
+       HDMI_S3D_FIELD_ALTERNATIVE      = 1,
+       HDMI_S3D_LINE_ALTERNATIVE       = 2,
+       HDMI_S3D_SIDE_BY_SIDE_FULL      = 3,
+       HDMI_S3D_L_DEPTH                = 4,
+       HDMI_S3D_L_DEPTH_GP_GP_DEPTH    = 5,
+       HDMI_S3D_SIDE_BY_SIDE_HALF      = 8
+};
+
+/* Subsampling types used for Stereoscopic 3D over HDMI. Below HOR
+stands for Horizontal, QUI for Quinxcunx Subsampling, O for odd fields,
+E for Even fields, L for left view and R for Right view*/
+enum hdmi_s3d_subsampling_type {
+       HDMI_S3D_HOR_OL_OR = 0,
+       HDMI_S3D_HOR_OL_ER = 1,
+       HDMI_S3D_HOR_EL_OR = 2,
+       HDMI_S3D_HOR_EL_ER = 3,
+       HDMI_S3D_QUI_OL_OR = 4,
+       HDMI_S3D_QUI_OL_ER = 5,
+       HDMI_S3D_QUI_EL_OR = 6,
+       HDMI_S3D_QUI_EL_ER = 7
+};
+
+struct hdmi_s3d_info {
+       bool subsamp;
+       enum hdmi_s3d_frame_structure  frame_struct;
+       enum hdmi_s3d_subsampling_type  subsamp_pos;
+       bool vsi_enabled;
+};
+
 struct hdmi_cm {
        int     code;
        int     mode;
@@ -50,6 +96,10 @@ struct hdmi_cm {
 struct hdmi_config {
        struct omap_video_timings timings;
        struct hdmi_cm cm;
+       bool s3d_enabled;
+       struct hdmi_s3d_info s3d_info;
+       enum hdmi_deep_color_mode deep_color;
+       enum hdmi_range range;
 };
 
 /* HDMI PLL structure */
@@ -63,6 +113,22 @@ struct hdmi_pll_info {
        enum hdmi_clk_refsel refsel;
 };
 
+struct hdmi_irq_vector {
+       u8      pll_recal;
+       u8      pll_unlock;
+       u8      pll_lock;
+       u8      phy_disconnect;
+       u8      phy_connect;
+       u8      phy_short_5v;
+       u8      video_end_fr;
+       u8      video_vsync;
+       u8      fifo_sample_req;
+       u8      fifo_overflow;
+       u8      fifo_underflow;
+       u8      ocp_timeout;
+       u8      core;
+};
+
 struct ti_hdmi_ip_ops {
 
        void (*video_configure)(struct hdmi_ip_data *ip_data);
@@ -91,7 +157,8 @@ struct ti_hdmi_ip_ops {
 
        void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s);
 
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
        int (*audio_enable)(struct hdmi_ip_data *ip_data);
 
        void (*audio_disable)(struct hdmi_ip_data *ip_data);
@@ -106,6 +173,11 @@ struct ti_hdmi_ip_ops {
        int (*audio_get_dma_port)(u32 *offset, u32 *size);
 #endif
 
+       int (*irq_handler) (struct hdmi_ip_data *ip_data);
+
+       int (*irq_core_handler) (struct hdmi_ip_data *ip_data);
+
+       int (*configure_range)(struct hdmi_ip_data *ip_data);
 };
 
 /*
@@ -151,10 +223,9 @@ struct hdmi_core_infoframe_avi {
 
 struct hdmi_ip_data {
        void __iomem    *base_wp;       /* HDMI wrapper */
-       unsigned long   core_sys_offset;
-       unsigned long   core_av_offset;
-       unsigned long   pll_offset;
-       unsigned long   phy_offset;
+       void __iomem    *base_pllctrl;  /* HDMI DPLL */
+       void __iomem    *base_txphyctrl;/* HDMI TXPHY */
+       void __iomem    *base_core;     /* HDMI IP CORE */
        const struct ti_hdmi_ip_ops *ops;
        struct hdmi_config cfg;
        struct hdmi_pll_info pll_data;
@@ -172,13 +243,20 @@ int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data);
 void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data);
 int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data);
 void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data);
+int ti_hdmi_4xxx_irq_handler(struct hdmi_ip_data *ip_data);
+int ti_hdmi_4xxx_core_irq_handler(struct hdmi_ip_data *ip_data);
 void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data);
 void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
 void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
 void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
 void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
 int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts);
+void ti_hdmi_4xxx_wp_audio_config_dma(struct hdmi_ip_data *ip_data,
+                                       struct hdmi_audio_dma *aud_dma);
+#endif
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
 int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data);
 void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data);
 int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data);
@@ -187,4 +265,16 @@ int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data,
                struct omap_dss_audio *audio);
 int ti_hdmi_4xxx_audio_get_dma_port(u32 *offset, u32 *size);
 #endif
+#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+int ti_hdmi_5xxx_audio_start(struct hdmi_ip_data *ip_data);
+void ti_hdmi_5xxx_audio_stop(struct hdmi_ip_data *ip_data);
+int ti_hdmi_5xxx_audio_config(struct hdmi_ip_data *ip_data,
+               struct omap_dss_audio *audio);
+#endif
+void ti_hdmi_5xxx_basic_configure(struct hdmi_ip_data *ip_data);
+void ti_hdmi_5xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
+int ti_hdmi_5xxx_read_edid(struct hdmi_ip_data *ip_data,
+                               u8 *edid, int len);
+int ti_hdmi_5xxx_core_irq_handler(struct hdmi_ip_data *ip_data);
+int ti_hdmi_5xxx_configure_range(struct hdmi_ip_data *ip_data);
 #endif
index e18b222ed739081819c35ef6276e7b7094068fa4..947ec62aec58270ab9c366f0e892e21e5bb8bec9 100644 (file)
 #include "dss.h"
 #include "dss_features.h"
 
+/* define here the required offset inside the core address space */
+#define HDMI_CORE_SYS 0
+#define HDMI_CORE_AV 0x500
+
 static inline void hdmi_write_reg(void __iomem *base_addr,
                                const u16 idx, u32 val)
 {
@@ -57,22 +61,22 @@ static inline void __iomem *hdmi_wp_base(struct hdmi_ip_data *ip_data)
 
 static inline void __iomem *hdmi_phy_base(struct hdmi_ip_data *ip_data)
 {
-       return ip_data->base_wp + ip_data->phy_offset;
+       return ip_data->base_txphyctrl;
 }
 
 static inline void __iomem *hdmi_pll_base(struct hdmi_ip_data *ip_data)
 {
-       return ip_data->base_wp + ip_data->pll_offset;
+       return ip_data->base_pllctrl;
 }
 
 static inline void __iomem *hdmi_av_base(struct hdmi_ip_data *ip_data)
 {
-       return ip_data->base_wp + ip_data->core_av_offset;
+       return ip_data->base_core + HDMI_CORE_AV;
 }
 
 static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data)
 {
-       return ip_data->base_wp + ip_data->core_sys_offset;
+       return ip_data->base_core + HDMI_CORE_SYS;
 }
 
 static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
@@ -192,8 +196,27 @@ static int hdmi_set_pll_pwr(struct hdmi_ip_data *ip_data, enum hdmi_pll_pwr val)
 
 static int hdmi_pll_reset(struct hdmi_ip_data *ip_data)
 {
+
+       enum omapdss_version ver = omapdss_get_version();
+       u32 val;
+
+       switch (ver) {
+       case OMAPDSS_VER_OMAP4430_ES1:
+       case OMAPDSS_VER_OMAP4430_ES2:
+       case OMAPDSS_VER_OMAP4:
+               val = 0;
+               break;
+       case OMAPDSS_VER_OMAP5:
+               val = 1;
+               break;
+       default:
+               DSSWARN("Invalid OMAP version");
+               return -EINVAL;
+       }
+
        /* SYSRESET  controlled by power FSM */
-       REG_FLD_MOD(hdmi_pll_base(ip_data), PLLCTRL_PLL_CONTROL, 0x0, 3, 3);
+       REG_FLD_MOD(hdmi_pll_base(ip_data), PLLCTRL_PLL_CONTROL, val, 3, 3);
+
 
        /* READ 0x0 reset is in progress */
        if (hdmi_wait_for_bit_change(hdmi_pll_base(ip_data),
@@ -246,14 +269,21 @@ static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data)
                r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
        else
                r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
-
+       /*
+        * HDMI_WP_PWR_CTRL doesn't seem to reflect the change in power
+        * states, ignore the error for now
+        */
+#if 0
        if (r) {
                DSSERR("Failed to %s PHY TX power\n",
                                hpd ? "enable" : "disable");
                goto err;
        }
+#endif
 
+#if 0
 err:
+#endif
        mutex_unlock(&ip_data->lock);
        return r;
 }
@@ -271,10 +301,27 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
 {
        u16 r = 0;
        void __iomem *phy_base = hdmi_phy_base(ip_data);
+       enum omapdss_version version = omapdss_get_version();
+       unsigned long pclk = ip_data->cfg.timings.pixel_clock;
+       u16 freqout = 1;
+
+       /*
+        * In OMAP5, the HFBITCLK must be divided by 2 before issuing the
+        * HDMI_PHYPWRCMD_LDOON command.
+        */
+       if (version == OMAPDSS_VER_OMAP5)
+               REG_FLD_MOD(phy_base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11);
 
        r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
+
+       /*
+        * HDMI_WP_PWR_CTRL doesn't seem to reflect the change in power
+        * states, ignore the error for now
+        */
+#if 0
        if (r)
                return r;
+#endif
 
        /*
         * Read address 0 in order to get the SCP reset done completed
@@ -286,7 +333,28 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
         * Write to phy address 0 to configure the clock
         * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
         */
-       REG_FLD_MOD(phy_base, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30);
+       switch (version){
+       case OMAPDSS_VER_OMAP4430_ES1:
+       case OMAPDSS_VER_OMAP4430_ES2:
+       case OMAPDSS_VER_OMAP4:
+               freqout = 1;
+               break;
+       case OMAPDSS_VER_OMAP5:
+               if (pclk < 62500) {
+                       freqout = 0;
+               } else if ((pclk >= 62500) && (pclk < 185000)) {
+                       freqout = 1;
+               } else {
+                       /* clock frequency > 185MHz */
+                       freqout = 2;
+               }
+               break;
+       default:
+               DSSWARN("invalid omapdss version");
+               return -EINVAL;
+       }
+
+       REG_FLD_MOD(phy_base, HDMI_TXPHY_TX_CTRL, freqout, 31, 30);
 
        /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
        hdmi_write_reg(phy_base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000);
@@ -528,7 +596,7 @@ static void hdmi_core_init(struct hdmi_core_video_config *video_cfg,
 static void hdmi_core_powerdown_disable(struct hdmi_ip_data *ip_data)
 {
        pr_debug("Enter hdmi_core_powerdown_disable\n");
-       REG_FLD_MOD(hdmi_core_sys_base(ip_data), HDMI_CORE_CTRL1, 0x0, 0, 0);
+       REG_FLD_MOD(hdmi_core_sys_base(ip_data), HDMI_CORE_SYS_SYS_CTRL1, 0x0, 0, 0);
 }
 
 static void hdmi_core_swreset_release(struct hdmi_ip_data *ip_data)
@@ -551,12 +619,12 @@ static void hdmi_core_video_config(struct hdmi_ip_data *ip_data,
        void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
 
        /* sys_ctrl1 default configuration not tunable */
-       r = hdmi_read_reg(core_sys_base, HDMI_CORE_CTRL1);
-       r = FLD_MOD(r, HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC, 5, 5);
-       r = FLD_MOD(r, HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC, 4, 4);
-       r = FLD_MOD(r, HDMI_CORE_CTRL1_BSEL_24BITBUS, 2, 2);
-       r = FLD_MOD(r, HDMI_CORE_CTRL1_EDGE_RISINGEDGE, 1, 1);
-       hdmi_write_reg(core_sys_base, HDMI_CORE_CTRL1, r);
+       r = hdmi_read_reg(core_sys_base, HDMI_CORE_SYS_SYS_CTRL1);
+       r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_VEN_FOLLOWVSYNC, 5, 5);
+       r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_HEN_FOLLOWHSYNC, 4, 4);
+       r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_BSEL_24BITBUS, 2, 2);
+       r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_EDGE_RISINGEDGE, 1, 1);
+       hdmi_write_reg(core_sys_base, HDMI_CORE_SYS_SYS_CTRL1, r);
 
        REG_FLD_MOD(core_sys_base,
                        HDMI_CORE_SYS_VID_ACEN, cfg->ip_bus_width, 7, 6);
@@ -680,8 +748,9 @@ static void hdmi_core_av_packet_config(struct hdmi_ip_data *ip_data,
                (repeat_cfg.generic_pkt_repeat));
 }
 
-static void hdmi_wp_init(struct omap_video_timings *timings,
-                       struct hdmi_video_format *video_fmt)
+void hdmi_wp_init(struct omap_video_timings *timings,
+                 struct hdmi_video_format *video_fmt,
+                 struct hdmi_irq_vector *irq_enable)
 {
        pr_debug("Enter hdmi_wp_init\n");
 
@@ -696,6 +765,19 @@ static void hdmi_wp_init(struct omap_video_timings *timings,
        video_fmt->y_res = 0;
        video_fmt->x_res = 0;
 
+       irq_enable->pll_recal = 0;
+       irq_enable->pll_unlock = 0;
+       irq_enable->pll_lock = 0;
+       irq_enable->phy_disconnect = 0;
+       irq_enable->phy_connect = 0;
+       irq_enable->phy_short_5v = 0;
+       irq_enable->video_end_fr = 0;
+       irq_enable->video_vsync = 0;
+       irq_enable->fifo_sample_req = 0;
+       irq_enable->fifo_overflow = 0;
+       irq_enable->fifo_underflow = 0;
+       irq_enable->ocp_timeout = 0;
+       irq_enable->core = 0;
 }
 
 int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data)
@@ -709,8 +791,9 @@ void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data)
        REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, false, 31, 31);
 }
 
-static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt,
-       struct omap_video_timings *timings, struct hdmi_config *param)
+void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt,
+                              struct omap_video_timings *timings,
+                              struct hdmi_config *param)
 {
        pr_debug("Enter hdmi_wp_video_init_format\n");
 
@@ -725,8 +808,8 @@ static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt,
        timings->vsw = param->timings.vsw;
 }
 
-static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data,
-               struct hdmi_video_format *video_fmt)
+void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data,
+                                struct hdmi_video_format *video_fmt)
 {
        u32 l = 0;
 
@@ -738,7 +821,7 @@ static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data,
        hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_SIZE, l);
 }
 
-static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data)
+void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data)
 {
        u32 r;
        bool vsync_pol, hsync_pol;
@@ -751,12 +834,12 @@ static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data)
        r = FLD_MOD(r, vsync_pol, 7, 7);
        r = FLD_MOD(r, hsync_pol, 6, 6);
        r = FLD_MOD(r, ip_data->cfg.timings.interlace, 3, 3);
-       r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */
+       r = FLD_MOD(r, ip_data->cfg.deep_color + 1 , 1, 0);
        hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, r);
 }
 
-static void hdmi_wp_video_config_timing(struct hdmi_ip_data *ip_data,
-               struct omap_video_timings *timings)
+void hdmi_wp_video_config_timing(struct hdmi_ip_data *ip_data,
+                                struct omap_video_timings *timings)
 {
        u32 timing_h = 0;
        u32 timing_v = 0;
@@ -774,22 +857,67 @@ static void hdmi_wp_video_config_timing(struct hdmi_ip_data *ip_data,
        hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_TIMING_V, timing_v);
 }
 
+void hdmi_wp_irq_enable(struct hdmi_ip_data *ip_data,
+                       struct hdmi_irq_vector *irq_enable)
+{
+       u32 r = 0;
+       r = ((irq_enable->pll_recal << 31) |
+               (irq_enable->pll_unlock << 30) |
+               (irq_enable->pll_lock << 29) |
+               (irq_enable->phy_disconnect << 26) |
+               (irq_enable->phy_connect << 25) |
+               (irq_enable->phy_short_5v << 24) |
+               (irq_enable->video_end_fr << 17) |
+               (irq_enable->video_vsync << 16) |
+               (irq_enable->fifo_sample_req << 10) |
+               (irq_enable->fifo_overflow << 9) |
+               (irq_enable->fifo_underflow << 8) |
+               (irq_enable->ocp_timeout << 4) |
+               (irq_enable->core << 0));
+
+       hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_SET, r);
+}
+
+int ti_hdmi_4xxx_core_irq_handler(struct hdmi_ip_data *ip_data)
+{
+       /*TODO: handle core interrupts*/
+       return 0;
+}
+
+int ti_hdmi_4xxx_irq_handler(struct hdmi_ip_data *ip_data)
+{
+       u32 val = 0;
+       void __iomem *wp_base = hdmi_wp_base(ip_data);
+
+       pr_debug("Enter hdmi_ti_4xxx_irq_handler\n");
+
+       val = hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
+       if (val & HDMI_WP_IRQSTATUS_CORE_MASK)
+               pr_debug("HDMI_WP_IRQSTATUS = 0x%x\n", val);
+
+       /* Ack other interrupts if any */
+       hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS, val);
+       /* flush posted write */
+       hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
+
+       return val;
+}
+
 void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
 {
        /* HDMI */
        struct omap_video_timings video_timing;
        struct hdmi_video_format video_format;
        /* HDMI core */
-       struct hdmi_core_infoframe_avi avi_cfg = ip_data->avi_cfg;
+       struct hdmi_core_infoframe_avi *avi_cfg = &ip_data->avi_cfg;
        struct hdmi_core_video_config v_core_cfg;
        struct hdmi_core_packet_enable_repeat repeat_cfg;
        struct hdmi_config *cfg = &ip_data->cfg;
+       struct hdmi_irq_vector irq_enable;
 
-       hdmi_wp_init(&video_timing, &video_format);
+       hdmi_wp_init(&video_timing, &video_format, &irq_enable);
 
-       hdmi_core_init(&v_core_cfg,
-               &avi_cfg,
-               &repeat_cfg);
+       hdmi_core_init(&v_core_cfg, avi_cfg, &repeat_cfg);
 
        hdmi_wp_video_init_format(&video_format, &video_timing, cfg);
 
@@ -823,24 +951,24 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
         * configure packet
         * info frame video see doc CEA861-D page 65
         */
-       avi_cfg.db1_format = HDMI_INFOFRAME_AVI_DB1Y_RGB;
-       avi_cfg.db1_active_info =
-               HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF;
-       avi_cfg.db1_bar_info_dv = HDMI_INFOFRAME_AVI_DB1B_NO;
-       avi_cfg.db1_scan_info = HDMI_INFOFRAME_AVI_DB1S_0;
-       avi_cfg.db2_colorimetry = HDMI_INFOFRAME_AVI_DB2C_NO;
-       avi_cfg.db2_aspect_ratio = HDMI_INFOFRAME_AVI_DB2M_NO;
-       avi_cfg.db2_active_fmt_ar = HDMI_INFOFRAME_AVI_DB2R_SAME;
-       avi_cfg.db3_itc = HDMI_INFOFRAME_AVI_DB3ITC_NO;
-       avi_cfg.db3_ec = HDMI_INFOFRAME_AVI_DB3EC_XVYUV601;
-       avi_cfg.db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_DEFAULT;
-       avi_cfg.db3_nup_scaling = HDMI_INFOFRAME_AVI_DB3SC_NO;
-       avi_cfg.db4_videocode = cfg->cm.code;
-       avi_cfg.db5_pixel_repeat = HDMI_INFOFRAME_AVI_DB5PR_NO;
-       avi_cfg.db6_7_line_eoftop = 0;
-       avi_cfg.db8_9_line_sofbottom = 0;
-       avi_cfg.db10_11_pixel_eofleft = 0;
-       avi_cfg.db12_13_pixel_sofright = 0;
+       avi_cfg->db1_format = HDMI_INFOFRAME_AVI_DB1Y_RGB;
+       avi_cfg->db1_active_info =
+                       HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF;
+       avi_cfg->db1_bar_info_dv = HDMI_INFOFRAME_AVI_DB1B_NO;
+       avi_cfg->db1_scan_info = HDMI_INFOFRAME_AVI_DB1S_0;
+       avi_cfg->db2_colorimetry = HDMI_INFOFRAME_AVI_DB2C_NO;
+       avi_cfg->db2_aspect_ratio = HDMI_INFOFRAME_AVI_DB2M_NO;
+       avi_cfg->db2_active_fmt_ar = HDMI_INFOFRAME_AVI_DB2R_SAME;
+       avi_cfg->db3_itc = HDMI_INFOFRAME_AVI_DB3ITC_NO;
+       avi_cfg->db3_ec = HDMI_INFOFRAME_AVI_DB3EC_XVYUV601;
+       avi_cfg->db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_DEFAULT;
+       avi_cfg->db3_nup_scaling = HDMI_INFOFRAME_AVI_DB3SC_NO;
+       avi_cfg->db4_videocode = cfg->cm.code;
+       avi_cfg->db5_pixel_repeat = HDMI_INFOFRAME_AVI_DB5PR_NO;
+       avi_cfg->db6_7_line_eoftop = 0;
+       avi_cfg->db8_9_line_sofbottom = 0;
+       avi_cfg->db10_11_pixel_eofleft = 0;
+       avi_cfg->db12_13_pixel_sofright = 0;
 
        hdmi_core_aux_infoframe_avi_config(ip_data);
 
@@ -862,8 +990,11 @@ void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
        DUMPREG(HDMI_WP_SYSCONFIG);
        DUMPREG(HDMI_WP_IRQSTATUS_RAW);
        DUMPREG(HDMI_WP_IRQSTATUS);
-       DUMPREG(HDMI_WP_PWR_CTRL);
        DUMPREG(HDMI_WP_IRQENABLE_SET);
+       DUMPREG(HDMI_WP_IRQENABLE_CLR);
+       DUMPREG(HDMI_WP_IRQWAKEEN);
+       DUMPREG(HDMI_WP_PWR_CTRL);
+       DUMPREG(HDMI_WP_DEBOUNCE);
        DUMPREG(HDMI_WP_VIDEO_CFG);
        DUMPREG(HDMI_WP_VIDEO_SIZE);
        DUMPREG(HDMI_WP_VIDEO_TIMING_H);
@@ -886,6 +1017,8 @@ void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
        DUMPPLL(PLLCTRL_CFG1);
        DUMPPLL(PLLCTRL_CFG2);
        DUMPPLL(PLLCTRL_CFG3);
+       DUMPPLL(PLLCTRL_SSC_CFG1);
+       DUMPPLL(PLLCTRL_SSC_CFG2);
        DUMPPLL(PLLCTRL_CFG4);
 }
 
@@ -907,8 +1040,9 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
        DUMPCORE(HDMI_CORE_SYS_DEV_IDH);
        DUMPCORE(HDMI_CORE_SYS_DEV_REV);
        DUMPCORE(HDMI_CORE_SYS_SRST);
-       DUMPCORE(HDMI_CORE_CTRL1);
+       DUMPCORE(HDMI_CORE_SYS_SYS_CTRL1);
        DUMPCORE(HDMI_CORE_SYS_SYS_STAT);
+       DUMPCORE(HDMI_CORE_SYS_SYS_CTRL3);
        DUMPCORE(HDMI_CORE_SYS_DE_DLY);
        DUMPCORE(HDMI_CORE_SYS_DE_CTRL);
        DUMPCORE(HDMI_CORE_SYS_DE_TOP);
@@ -916,14 +1050,58 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
        DUMPCORE(HDMI_CORE_SYS_DE_CNTH);
        DUMPCORE(HDMI_CORE_SYS_DE_LINL);
        DUMPCORE(HDMI_CORE_SYS_DE_LINH_1);
+       DUMPCORE(HDMI_CORE_SYS_HRES_L);
+       DUMPCORE(HDMI_CORE_SYS_HRES_H);
+       DUMPCORE(HDMI_CORE_SYS_VRES_L);
+       DUMPCORE(HDMI_CORE_SYS_VRES_H);
+       DUMPCORE(HDMI_CORE_SYS_IADJUST);
+       DUMPCORE(HDMI_CORE_SYS_POLDETECT);
+       DUMPCORE(HDMI_CORE_SYS_HWIDTH1);
+       DUMPCORE(HDMI_CORE_SYS_HWIDTH2);
+       DUMPCORE(HDMI_CORE_SYS_VWIDTH);
+       DUMPCORE(HDMI_CORE_SYS_VID_CTRL);
        DUMPCORE(HDMI_CORE_SYS_VID_ACEN);
        DUMPCORE(HDMI_CORE_SYS_VID_MODE);
+       DUMPCORE(HDMI_CORE_SYS_VID_BLANK1);
+       DUMPCORE(HDMI_CORE_SYS_VID_BLANK3);
+       DUMPCORE(HDMI_CORE_SYS_VID_BLANK1);
+       DUMPCORE(HDMI_CORE_SYS_DC_HEADER);
+       DUMPCORE(HDMI_CORE_SYS_VID_DITHER);
+       DUMPCORE(HDMI_CORE_SYS_RGB2XVYCC_CT);
+       DUMPCORE(HDMI_CORE_SYS_R2Y_COEFF_LOW);
+       DUMPCORE(HDMI_CORE_SYS_R2Y_COEFF_UP);
+       DUMPCORE(HDMI_CORE_SYS_G2Y_COEFF_LOW);
+       DUMPCORE(HDMI_CORE_SYS_G2Y_COEFF_UP);
+       DUMPCORE(HDMI_CORE_SYS_B2Y_COEFF_LOW);
+       DUMPCORE(HDMI_CORE_SYS_B2Y_COEFF_UP);
+       DUMPCORE(HDMI_CORE_SYS_R2CB_COEFF_LOW);
+       DUMPCORE(HDMI_CORE_SYS_R2CB_COEFF_UP);
+       DUMPCORE(HDMI_CORE_SYS_G2CB_COEFF_LOW);
+       DUMPCORE(HDMI_CORE_SYS_G2CB_COEFF_UP);
+       DUMPCORE(HDMI_CORE_SYS_B2CB_COEFF_LOW);
+       DUMPCORE(HDMI_CORE_SYS_B2CB_COEFF_UP);
+       DUMPCORE(HDMI_CORE_SYS_R2CR_COEFF_LOW);
+       DUMPCORE(HDMI_CORE_SYS_R2CR_COEFF_UP);
+       DUMPCORE(HDMI_CORE_SYS_G2CR_COEFF_LOW);
+       DUMPCORE(HDMI_CORE_SYS_G2CR_COEFF_UP);
+       DUMPCORE(HDMI_CORE_SYS_B2CR_COEFF_LOW);
+       DUMPCORE(HDMI_CORE_SYS_B2CR_COEFF_UP);
+       DUMPCORE(HDMI_CORE_SYS_RGB_OFFSET_LOW);
+       DUMPCORE(HDMI_CORE_SYS_RGB_OFFSET_UP);
+       DUMPCORE(HDMI_CORE_SYS_Y_OFFSET_LOW);
+       DUMPCORE(HDMI_CORE_SYS_Y_OFFSET_UP);
+       DUMPCORE(HDMI_CORE_SYS_CBCR_OFFSET_LOW);
+       DUMPCORE(HDMI_CORE_SYS_CBCR_OFFSET_UP);
        DUMPCORE(HDMI_CORE_SYS_INTR_STATE);
        DUMPCORE(HDMI_CORE_SYS_INTR1);
        DUMPCORE(HDMI_CORE_SYS_INTR2);
        DUMPCORE(HDMI_CORE_SYS_INTR3);
        DUMPCORE(HDMI_CORE_SYS_INTR4);
-       DUMPCORE(HDMI_CORE_SYS_UMASK1);
+       DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK1);
+       DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK2);
+       DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK3);
+       DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK4);
+       DUMPCORE(HDMI_CORE_SYS_INTR_CTRL);
        DUMPCORE(HDMI_CORE_SYS_TMDS_CTRL);
 
        DUMPCORE(HDMI_CORE_DDC_ADDR);
@@ -1021,8 +1199,31 @@ void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
        DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL);
        DUMPPHY(HDMI_TXPHY_POWER_CTRL);
        DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL);
+       if (omapdss_get_version() == OMAPDSS_VER_OMAP5)
+               DUMPPHY(HDMI_TXPHY_BIST_CONTROL);
 }
 
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || \
+       defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+void ti_hdmi_4xxx_wp_audio_config_dma(struct hdmi_ip_data *ip_data,
+                                       struct hdmi_audio_dma *aud_dma)
+{
+       u32 r;
+
+       DSSDBG("Enter hdmi_wp_audio_config_dma\n");
+
+       r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG2);
+       r = FLD_MOD(r, aud_dma->transfer_size, 15, 8);
+       r = FLD_MOD(r, aud_dma->block_size, 7, 0);
+       hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG2, r);
+
+       r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL);
+       r = FLD_MOD(r, aud_dma->mode, 9, 9);
+       r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0);
+       hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, r);
+}
+#endif
+
 #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
 static void ti_hdmi_4xxx_wp_audio_config_format(struct hdmi_ip_data *ip_data,
                                        struct hdmi_audio_format *aud_fmt)
@@ -1043,24 +1244,6 @@ static void ti_hdmi_4xxx_wp_audio_config_format(struct hdmi_ip_data *ip_data,
        hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG, r);
 }
 
-static void ti_hdmi_4xxx_wp_audio_config_dma(struct hdmi_ip_data *ip_data,
-                                       struct hdmi_audio_dma *aud_dma)
-{
-       u32 r;
-
-       DSSDBG("Enter hdmi_wp_audio_config_dma\n");
-
-       r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG2);
-       r = FLD_MOD(r, aud_dma->transfer_size, 15, 8);
-       r = FLD_MOD(r, aud_dma->block_size, 7, 0);
-       hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG2, r);
-
-       r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL);
-       r = FLD_MOD(r, aud_dma->mode, 9, 9);
-       r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0);
-       hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, r);
-}
-
 static void ti_hdmi_4xxx_core_audio_config(struct hdmi_ip_data *ip_data,
                                        struct hdmi_core_audio_config *cfg)
 {
index 8366ae19e82eece140606c7178e6a70216fbec87..1a9318a344f7e02008df036b6ded6e89d94744bd 100644 (file)
 #define HDMI_WP_SYSCONFIG                      0x10
 #define HDMI_WP_IRQSTATUS_RAW                  0x24
 #define HDMI_WP_IRQSTATUS                      0x28
-#define HDMI_WP_PWR_CTRL                       0x40
 #define HDMI_WP_IRQENABLE_SET                  0x2C
+#define HDMI_WP_IRQENABLE_CLR                  0x30
+#define HDMI_WP_IRQWAKEEN                      0x34
+#define HDMI_WP_PWR_CTRL                       0x40
+#define HDMI_WP_DEBOUNCE                       0x44
 #define HDMI_WP_VIDEO_CFG                      0x50
 #define HDMI_WP_VIDEO_SIZE                     0x60
 #define HDMI_WP_VIDEO_TIMING_H                 0x68
 #define HDMI_CORE_SYS_DEV_IDH                  0xC
 #define HDMI_CORE_SYS_DEV_REV                  0x10
 #define HDMI_CORE_SYS_SRST                     0x14
-#define HDMI_CORE_CTRL1                                0x20
+#define HDMI_CORE_SYS_SYS_CTRL1                        0x20
 #define HDMI_CORE_SYS_SYS_STAT                 0x24
+#define HDMI_CORE_SYS_SYS_CTRL3                        0x28
+#define HDMI_CORE_SYS_DCTL                     0x34
 #define HDMI_CORE_SYS_DE_DLY                   0xC8
 #define HDMI_CORE_SYS_DE_CTRL                  0xCC
 #define HDMI_CORE_SYS_DE_TOP                   0xD0
 #define HDMI_CORE_SYS_DE_CNTH                  0xDC
 #define HDMI_CORE_SYS_DE_LINL                  0xE0
 #define HDMI_CORE_SYS_DE_LINH_1                        0xE4
+#define HDMI_CORE_SYS_HRES_L                   0xE8
+#define HDMI_CORE_SYS_HRES_H                   0xEC
+#define HDMI_CORE_SYS_VRES_L                   0xF0
+#define HDMI_CORE_SYS_VRES_H                   0xF4
+#define HDMI_CORE_SYS_IADJUST                  0xF8
+#define HDMI_CORE_SYS_POLDETECT                        0xFC
+#define HDMI_CORE_SYS_HWIDTH1                  0x110
+#define HDMI_CORE_SYS_HWIDTH2                  0x114
+#define HDMI_CORE_SYS_VWIDTH                   0x11C
+#define HDMI_CORE_SYS_VID_CTRL                 0x120
 #define HDMI_CORE_SYS_VID_ACEN                 0x124
 #define HDMI_CORE_SYS_VID_MODE                 0x128
+#define HDMI_CORE_SYS_VID_BLANK1               0x12C
+#define HDMI_CORE_SYS_VID_BLANK2               0x130
+#define HDMI_CORE_SYS_VID_BLANK3               0x134
+#define HDMI_CORE_SYS_DC_HEADER                        0x138
+#define HDMI_CORE_SYS_VID_DITHER               0x13C
+#define HDMI_CORE_SYS_RGB2XVYCC_CT             0x140
+#define HDMI_CORE_SYS_R2Y_COEFF_LOW            0x144
+#define HDMI_CORE_SYS_R2Y_COEFF_UP             0x148
+#define HDMI_CORE_SYS_G2Y_COEFF_LOW            0x14C
+#define HDMI_CORE_SYS_G2Y_COEFF_UP             0x150
+#define HDMI_CORE_SYS_B2Y_COEFF_LOW            0x154
+#define HDMI_CORE_SYS_B2Y_COEFF_UP             0x158
+#define HDMI_CORE_SYS_R2CB_COEFF_LOW           0x15C
+#define HDMI_CORE_SYS_R2CB_COEFF_UP            0x160
+#define HDMI_CORE_SYS_G2CB_COEFF_LOW           0x164
+#define HDMI_CORE_SYS_G2CB_COEFF_UP            0x168
+#define HDMI_CORE_SYS_B2CB_COEFF_LOW           0x16C
+#define HDMI_CORE_SYS_B2CB_COEFF_UP            0x170
+#define HDMI_CORE_SYS_R2CR_COEFF_LOW           0x174
+#define HDMI_CORE_SYS_R2CR_COEFF_UP            0x178
+#define HDMI_CORE_SYS_G2CR_COEFF_LOW           0x17C
+#define HDMI_CORE_SYS_G2CR_COEFF_UP            0x180
+#define HDMI_CORE_SYS_B2CR_COEFF_LOW           0x184
+#define HDMI_CORE_SYS_B2CR_COEFF_UP            0x188
+#define HDMI_CORE_SYS_RGB_OFFSET_LOW           0x18C
+#define HDMI_CORE_SYS_RGB_OFFSET_UP            0x190
+#define HDMI_CORE_SYS_Y_OFFSET_LOW             0x194
+#define HDMI_CORE_SYS_Y_OFFSET_UP              0x198
+#define HDMI_CORE_SYS_CBCR_OFFSET_LOW          0x19C
+#define HDMI_CORE_SYS_CBCR_OFFSET_UP           0x1A0
 #define HDMI_CORE_SYS_INTR_STATE               0x1C0
 #define HDMI_CORE_SYS_INTR1                    0x1C4
 #define HDMI_CORE_SYS_INTR2                    0x1C8
 #define HDMI_CORE_SYS_INTR3                    0x1CC
 #define HDMI_CORE_SYS_INTR4                    0x1D0
-#define HDMI_CORE_SYS_UMASK1                   0x1D4
+#define HDMI_CORE_SYS_INTR_UNMASK1             0x1D4
+#define HDMI_CORE_SYS_INTR_UNMASK2             0x1D8
+#define HDMI_CORE_SYS_INTR_UNMASK3             0x1DC
+#define HDMI_CORE_SYS_INTR_UNMASK4             0x1E0
+#define HDMI_CORE_SYS_INTR_CTRL                        0x1E4
 #define HDMI_CORE_SYS_TMDS_CTRL                        0x208
 
-#define HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC        0x1
-#define HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC        0x1
-#define HDMI_CORE_CTRL1_BSEL_24BITBUS  0x1
-#define HDMI_CORE_CTRL1_EDGE_RISINGEDGE        0x1
+/* value definitions for HDMI_CORE_SYS_SYS_CTRL1 fields */
+#define HDMI_CORE_SYS_SYS_CTRL1_VEN_FOLLOWVSYNC        0x1
+#define HDMI_CORE_SYS_SYS_CTRL1_HEN_FOLLOWHSYNC        0x1
+#define HDMI_CORE_SYS_SYS_CTRL1_BSEL_24BITBUS  0x1
+#define HDMI_CORE_SYS_SYS_CTRL1_EDGE_RISINGEDGE        0x1
 
 /* HDMI DDC E-DID */
 #define HDMI_CORE_DDC_ADDR                     0x3B4
 #define PLLCTRL_CFG1                           0xC
 #define PLLCTRL_CFG2                           0x10
 #define PLLCTRL_CFG3                           0x14
+#define PLLCTRL_SSC_CFG1                       0x18
+#define PLLCTRL_SSC_CFG2                       0x1C
 #define PLLCTRL_CFG4                           0x20
 
 /* HDMI PHY */
 #define HDMI_TXPHY_DIGITAL_CTRL                        0x4
 #define HDMI_TXPHY_POWER_CTRL                  0x8
 #define HDMI_TXPHY_PAD_CFG_CTRL                        0xC
+#define HDMI_TXPHY_BIST_CONTROL                        0x1C
+
+/* Interrupt masks */
+#define HDMI_WP_IRQSTATUS_CORE_MASK                  0x1
 
 #define REG_FLD_MOD(base, idx, val, start, end) \
        hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
 #define REG_GET(base, idx, start, end) \
        FLD_GET(hdmi_read_reg(base, idx), start, end)
 
+
+
 enum hdmi_phy_pwr {
        HDMI_PHYPWRCMD_OFF = 0,
        HDMI_PHYPWRCMD_LDOON = 1,
@@ -285,7 +343,8 @@ enum hdmi_packing_mode {
 
 enum hdmi_core_audio_layout {
        HDMI_AUDIO_LAYOUT_2CH = 0,
-       HDMI_AUDIO_LAYOUT_8CH = 1
+       HDMI_AUDIO_LAYOUT_8CH = 1,
+       HDMI_AUDIO_LAYOUT_6CH = 2
 };
 
 enum hdmi_core_cts_mode {
@@ -433,4 +492,18 @@ struct hdmi_core_audio_config {
        bool                                    en_spdif;
 };
 
+void hdmi_wp_video_config_timing(struct hdmi_ip_data *ip_data,
+                                struct omap_video_timings *timings);
+void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data);
+void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data,
+                                struct hdmi_video_format *video_fmt);
+void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt,
+                              struct omap_video_timings *timings,
+                              struct hdmi_config *param);
+void hdmi_wp_init(struct omap_video_timings *timings,
+                 struct hdmi_video_format *video_fmt,
+                 struct hdmi_irq_vector *irq_enable);
+void hdmi_wp_irq_enable(struct hdmi_ip_data *ip_data,
+                       struct hdmi_irq_vector *irq_enable);
+
 #endif
diff --git a/drivers/video/omap2/dss/ti_hdmi_5xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_5xxx_ip.c
new file mode 100644 (file)
index 0000000..69570cb
--- /dev/null
@@ -0,0 +1,1079 @@
+
+/*
+ * ti_hdmi_5xxx_ip.c
+ *
+ * HDMI TI OMAP5 IP driver Library
+ * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Mythri pk <mythripk@ti.com>
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/seq_file.h>
+#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+#include <sound/asound.h>
+#include <sound/asoundef.h>
+#endif
+
+#include "ti_hdmi_5xxx_ip.h"
+#include "dss.h"
+
+static const struct csc_table csc_table_deepcolor[4] = {
+       /* HDMI_DEEP_COLOR_24BIT */
+       [0] = { 7036, 0, 0, 32,
+               0, 7036, 0, 32,
+               0, 0, 7036, 32 },
+       /* HDMI_DEEP_COLOR_30BIT */
+       [1] = { 7015, 0, 0, 128,
+               0, 7015, 0, 128,
+               0, 0, 7015, 128 },
+       /* HDMI_DEEP_COLOR_36BIT */
+       [2] = { 7010, 0, 0, 512,
+               0, 7010, 0, 512,
+               0, 0, 7010, 512},
+       /* FULL RANGE */
+       [3] = { 8192, 0, 0, 0,
+               0, 8192, 0, 0,
+               0, 0, 8192, 0},
+};
+
+static inline void hdmi_write_reg(void __iomem *base_addr,
+               const unsigned long idx, u32 val)
+{
+       __raw_writel(val, base_addr + idx);
+}
+
+static inline u32 hdmi_read_reg(void __iomem *base_addr,
+               const unsigned long idx)
+{
+       return __raw_readl(base_addr + idx);
+}
+
+static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data)
+{
+       return ip_data->base_core;
+}
+
+static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
+                       const unsigned long idx,
+                       int b2, int b1, u32 val)
+{
+       u32 t = 0;
+       while (val != REG_GET(base_addr, idx, b2, b1)) {
+               udelay(1);
+               if (t++ > 10000)
+                       return !val;
+       }
+       return val;
+}
+
+static inline void hdmi_core_ddc_req_addr(struct hdmi_ip_data *ip_data,
+                                               u8 addr, int ext)
+{
+       u8 seg_ptr = ext / 2;
+       u8 edidaddr = ((ext % 2) * 0x80) + addr;
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_ADDRESS, edidaddr, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_SEGPTR, seg_ptr, 7, 0);
+
+       if (seg_ptr)
+               REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_OPERATION, 1, 1, 1);
+       else
+               REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_OPERATION, 1, 0, 0);
+}
+
+static void hdmi_core_ddc_init(struct hdmi_ip_data *ip_data)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_SDA_HOLD_ADDR, 0x19, 7, 0);
+
+       /*Mask the interrupts*/
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_CTLINT, 0x0, 2, 2);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_CTLINT, 0x0, 6, 6);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_INT, 0x0, 2, 2);
+
+       /* Master clock division */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_DIV, 0x5, 3, 0);
+
+       /* Standard speed counter */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR, 0x0,
+               7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR, 0x79,
+               7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR, 0x0,
+               7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR, 0x91,
+               7, 0);
+
+       /* Fast speed counter*/
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR, 0x0,
+               7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR, 0x0F,
+               7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR, 0x0,
+               7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR, 0x21,
+               7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_SLAVE, 0x50, 6, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_SEGADDR, 0x30, 6, 0);
+}
+
+static int hdmi_core_ddc_edid(struct hdmi_ip_data *ip_data,
+                                       u8 *pedid, int ext)
+{
+       u8 cur_addr = 0;
+       char checksum = 0;
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+
+       hdmi_core_ddc_req_addr(ip_data, cur_addr, ext);
+
+       /* Unmask the interrupts*/
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_CTLINT, 0x1, 2, 2);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_CTLINT, 0x1, 6, 6);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_INT, 0x1, 2, 2);
+
+       /* FIXME:This is a hack to  read only 128 bytes data with a mdelay
+        * Ideally the read has to be based on the done interrupt and
+        * status which is not received thus it is ignored for now
+        */
+       while (cur_addr < 128) {
+       #if 0
+               if (hdmi_wait_for_bit_change(HDMI_CORE_I2CM_INT,
+                                               0, 0, 1) != 1) {
+                       DSSERR("Failed to recieve done interrupt\n");
+                       return -ETIMEDOUT;
+               }
+       #endif
+               mdelay(1);
+               pedid[cur_addr] = REG_GET(core_sys_base,
+                                       HDMI_CORE_I2CM_DATAI, 7, 0);
+               DSSDBG("pedid[%d] = %d", cur_addr, pedid[cur_addr]);
+               checksum += pedid[cur_addr++];
+               hdmi_core_ddc_req_addr(ip_data, cur_addr, ext);
+       }
+
+       return 0;
+
+}
+
+int ti_hdmi_5xxx_read_edid(struct hdmi_ip_data *ip_data,
+                               u8 *edid, int len)
+{
+       int r, l;
+
+       if (len < 128)
+               return -EINVAL;
+
+       hdmi_core_ddc_init(ip_data);
+
+       r = hdmi_core_ddc_edid(ip_data, edid, 0);
+       if (r)
+               return r;
+
+       l = 128;
+
+       if (len >= 128 * 2 && edid[0x7e] > 0) {
+               r = hdmi_core_ddc_edid(ip_data, edid + 0x80, 1);
+               if (r)
+                       return r;
+               l += 128;
+       }
+
+       return l;
+}
+void ti_hdmi_5xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
+{
+
+#define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\
+               hdmi_read_reg(hdmi_core_sys_base(ip_data), r))
+
+       DUMPCORE(HDMI_CORE_FC_INVIDCONF);
+       DUMPCORE(HDMI_CORE_FC_INHACTIV0);
+       DUMPCORE(HDMI_CORE_FC_INHACTIV1);
+       DUMPCORE(HDMI_CORE_FC_INHBLANK0);
+       DUMPCORE(HDMI_CORE_FC_INHBLANK1);
+       DUMPCORE(HDMI_CORE_FC_INVACTIV0);
+       DUMPCORE(HDMI_CORE_FC_INVACTIV1);
+       DUMPCORE(HDMI_CORE_FC_INVBLANK);
+       DUMPCORE(HDMI_CORE_FC_HSYNCINDELAY0);
+       DUMPCORE(HDMI_CORE_FC_HSYNCINDELAY1);
+       DUMPCORE(HDMI_CORE_FC_HSYNCINWIDTH0);
+       DUMPCORE(HDMI_CORE_FC_HSYNCINWIDTH1);
+       DUMPCORE(HDMI_CORE_FC_VSYNCINDELAY);
+       DUMPCORE(HDMI_CORE_FC_VSYNCINWIDTH);
+       DUMPCORE(HDMI_CORE_FC_CTRLDUR);
+       DUMPCORE(HDMI_CORE_FC_EXCTRLDUR);
+       DUMPCORE(HDMI_CORE_FC_EXCTRLSPAC);
+       DUMPCORE(HDMI_CORE_FC_CH0PREAM);
+       DUMPCORE(HDMI_CORE_FC_CH1PREAM);
+       DUMPCORE(HDMI_CORE_FC_CH2PREAM);
+       DUMPCORE(HDMI_CORE_FC_AVICONF0);
+       DUMPCORE(HDMI_CORE_FC_AVICONF1);
+       DUMPCORE(HDMI_CORE_FC_AVICONF2);
+       DUMPCORE(HDMI_CORE_FC_AVIVID);
+       DUMPCORE(HDMI_CORE_FC_PRCONF);
+
+       DUMPCORE(HDMI_CORE_MC_CLKDIS);
+       DUMPCORE(HDMI_CORE_MC_SWRSTZREQ);
+       DUMPCORE(HDMI_CORE_MC_FLOWCTRL);
+       DUMPCORE(HDMI_CORE_MC_PHYRSTZ);
+       DUMPCORE(HDMI_CORE_MC_LOCKONCLOCK);
+
+       DUMPCORE(HDMI_CORE_I2CM_SLAVE);
+       DUMPCORE(HDMI_CORE_I2CM_ADDRESS);
+       DUMPCORE(HDMI_CORE_I2CM_DATAO);
+       DUMPCORE(HDMI_CORE_I2CM_DATAI);
+       DUMPCORE(HDMI_CORE_I2CM_OPERATION);
+       DUMPCORE(HDMI_CORE_I2CM_INT);
+       DUMPCORE(HDMI_CORE_I2CM_CTLINT);
+       DUMPCORE(HDMI_CORE_I2CM_DIV);
+       DUMPCORE(HDMI_CORE_I2CM_SEGADDR);
+       DUMPCORE(HDMI_CORE_I2CM_SOFTRSTZ);
+       DUMPCORE(HDMI_CORE_I2CM_SEGPTR);
+       DUMPCORE(HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR);
+}
+
+static void hdmi_core_init(struct hdmi_core_vid_config *video_cfg,
+                       struct hdmi_core_infoframe_avi *avi_cfg,
+                       struct hdmi_config *cfg)
+{
+       printk(KERN_INFO "Enter hdmi_core_init\n");
+
+       /* video core */
+       video_cfg->data_enable_pol = 1; /* It is always 1*/
+       video_cfg->v_fc_config.timings.hsync_level = cfg->timings.hsync_level;
+       video_cfg->v_fc_config.timings.x_res = cfg->timings.x_res;
+       video_cfg->v_fc_config.timings.hsw = cfg->timings.hsw - 1;
+       video_cfg->v_fc_config.timings.hbp = cfg->timings.hbp;
+       video_cfg->v_fc_config.timings.hfp = cfg->timings.hfp;
+       video_cfg->hblank = cfg->timings.hfp +
+                               cfg->timings.hbp + cfg->timings.hsw - 1;
+       video_cfg->v_fc_config.timings.vsync_level = cfg->timings.vsync_level;
+       video_cfg->v_fc_config.timings.y_res = cfg->timings.y_res;
+       video_cfg->v_fc_config.timings.vsw = cfg->timings.vsw;
+       video_cfg->v_fc_config.timings.vfp = cfg->timings.vfp;
+       video_cfg->v_fc_config.timings.vbp = cfg->timings.vbp;
+       video_cfg->vblank_osc = 0; /* Always 0 - need to confirm */
+       video_cfg->vblank = cfg->timings.vsw +
+                               cfg->timings.vfp + cfg->timings.vbp;
+       video_cfg->v_fc_config.cm.mode = cfg->cm.mode;
+       video_cfg->v_fc_config.timings.interlace = cfg->timings.interlace;
+
+       /* info frame */
+       avi_cfg->db1_format = 0;
+       avi_cfg->db1_active_info = 0;
+       avi_cfg->db1_bar_info_dv = 0;
+       avi_cfg->db1_scan_info = 0;
+       avi_cfg->db2_colorimetry = 0;
+       avi_cfg->db2_aspect_ratio = 0;
+       avi_cfg->db2_active_fmt_ar = 0;
+       avi_cfg->db3_itc = 0;
+       avi_cfg->db3_ec = 0;
+       avi_cfg->db3_q_range = 0;
+       avi_cfg->db3_nup_scaling = 0;
+       avi_cfg->db4_videocode = 0;
+       avi_cfg->db5_pixel_repeat = 0;
+       avi_cfg->db6_7_line_eoftop = 0 ;
+       avi_cfg->db8_9_line_sofbottom = 0;
+       avi_cfg->db10_11_pixel_eofleft = 0;
+       avi_cfg->db12_13_pixel_sofright = 0;
+
+}
+
+static void hdmi_core_infoframe_vsi_config(struct hdmi_ip_data *ip_data,
+                               struct hdmi_s3d_info info_s3d)
+{
+       int length;
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+
+       /* For side-by-side(HALF) we need to specify subsampling
+        * in 3D_ext_data
+        */
+       length = info_s3d.frame_struct == HDMI_S3D_SIDE_BY_SIDE_HALF ? 6 : 5;
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_DATAUTO0, 0, 3, 3);
+       /* LSB 24bit IEEE Registration Identifier */
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_VSDIEEEID0, 0x03);
+       /* Mid 24bit IEEE Registration Identifier */
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_VSDIEEEID1, 0x0c);
+       /* MSB 24bit IEEE Registration Identifier */
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_VSDIEEEID2, 0x00);
+
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_VSDSIZE, length);
+
+       /* HDMI Video Format => 3D Format indication present
+        * 3d structure and potentially 3D_Ext_Data
+        */
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_VSDPAYLOAD(0), 0x40);
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_VSDPAYLOAD(1),
+                                               info_s3d.frame_struct << 4);
+       if (info_s3d.frame_struct == HDMI_S3D_SIDE_BY_SIDE_HALF)
+               hdmi_write_reg(core_sys_base, HDMI_CORE_FC_VSDPAYLOAD(2),
+                                               info_s3d.subsamp_pos << 4);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_DATAUTO0, 1, 3, 3);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_DATAUTO1, 0, 3, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_DATAUTO2, 1, 7, 4);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_DATAUTO2, 0, 3, 0);
+}
+
+/* DSS_HDMI_CORE_VIDEO_CONFIG */
+static void hdmi_core_video_config(struct hdmi_ip_data *ip_data,
+                               struct hdmi_core_vid_config *cfg)
+{
+       unsigned char r = 0;
+       bool vsync_pol, hsync_pol;
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+
+       vsync_pol = cfg->v_fc_config.timings.vsync_level
+               == OMAPDSS_SIG_ACTIVE_HIGH;
+       hsync_pol = cfg->v_fc_config.timings.hsync_level
+               == OMAPDSS_SIG_ACTIVE_HIGH;
+
+       /* Set hsync, vsync and data-enable polarity  */
+       r = hdmi_read_reg(core_sys_base, HDMI_CORE_FC_INVIDCONF);
+
+       r = FLD_MOD(r, vsync_pol, 6, 6);
+       r = FLD_MOD(r, hsync_pol, 5, 5);
+       r = FLD_MOD(r, cfg->data_enable_pol, 4, 4);
+       r = FLD_MOD(r, cfg->vblank_osc, 1, 1);
+       r = FLD_MOD(r, cfg->v_fc_config.timings.interlace, 0, 0);
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_INVIDCONF, r);
+
+       /* set x resolution */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_INHACTIV1,
+                       (cfg->v_fc_config.timings.x_res >> 8), 4, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_INHACTIV0,
+                       (cfg->v_fc_config.timings.x_res & 0xFF), 7, 0);
+
+       /* set y resolution */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_INVACTIV1,
+                       (cfg->v_fc_config.timings.y_res >> 8), 4, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_INVACTIV0,
+                       (cfg->v_fc_config.timings.y_res & 0xFF), 7, 0);
+
+       /* set horizontal blanking pixels */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_INHBLANK1,
+                       (cfg->hblank >> 8), 4, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_INHBLANK0,
+                       (cfg->hblank & 0xFF), 7, 0);
+
+       /* set vertial blanking pixels */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_INVBLANK, cfg->vblank, 7, 0);
+
+       /* set horizontal sync offset */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_HSYNCINDELAY1,
+                       (cfg->v_fc_config.timings.hfp >> 8), 4, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_HSYNCINDELAY0,
+                       (cfg->v_fc_config.timings.hfp & 0xFF), 7, 0);
+
+       /* set vertical sync offset */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_VSYNCINDELAY,
+                       cfg->v_fc_config.timings.vfp, 7, 0);
+
+       /* set horizontal sync pulse width */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_HSYNCINWIDTH1,
+                       (cfg->v_fc_config.timings.hsw >> 8), 1, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_HSYNCINWIDTH0,
+                       (cfg->v_fc_config.timings.hsw & 0xFF), 7, 0);
+
+       /*  set vertical sync pulse width */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_VSYNCINWIDTH,
+                       cfg->v_fc_config.timings.vsw, 5, 0);
+
+       /* select DVI mode */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_INVIDCONF,
+               cfg->v_fc_config.cm.mode, 3, 3);
+}
+
+static void hdmi_core_config_video_packetizer(struct hdmi_ip_data *ip_data)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+       struct hdmi_config *cfg = &ip_data->cfg;
+       int clr_depth;
+
+       switch (cfg->deep_color) {
+       case HDMI_DEEP_COLOR_30BIT:
+               clr_depth = 5;
+               break;
+       case HDMI_DEEP_COLOR_36BIT:
+               clr_depth = 6;
+               break;
+       case HDMI_DEEP_COLOR_24BIT:
+       default:
+               clr_depth = 0;
+               break;
+       }
+
+       /* COLOR_DEPTH */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_VP_PR_CD, clr_depth, 7, 4);
+
+       /* BYPASS_EN */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_VP_CONF, clr_depth ? 0 : 1, 6, 6);
+
+       /* PP_EN */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_VP_CONF, clr_depth ? 1 : 0, 5, 5);
+
+       /* YCC422_EN */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_VP_CONF, 0, 3, 3);
+
+       /* PP_STUFFING */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_VP_STUFF, clr_depth ? 1 : 0, 1, 1);
+
+       /* YCC422_STUFFING */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_VP_STUFF, 1, 2, 2);
+
+       /* OUTPUT_SELECTOR */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_VP_CONF, clr_depth ? 0 : 2, 1, 0);
+}
+
+static void hdmi_core_config_csc(struct hdmi_ip_data *ip_data)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+       struct hdmi_config *cfg = &ip_data->cfg;
+       int clr_depth;
+
+       switch (cfg->deep_color) {
+       case HDMI_DEEP_COLOR_30BIT:
+               clr_depth = 5;
+               break;
+       case HDMI_DEEP_COLOR_36BIT:
+               clr_depth = 6;
+               break;
+       case HDMI_DEEP_COLOR_24BIT:
+       default:
+               clr_depth = 0;
+               break;
+       }
+
+       /* CSC_COLORDEPTH */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_SCALE, clr_depth, 7, 4);
+}
+
+static void hdmi_core_config_video_sampler(struct hdmi_ip_data *ip_data)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+       struct hdmi_config *cfg = &ip_data->cfg;
+       int video_mapping;
+
+       switch (cfg->deep_color) {
+       case HDMI_DEEP_COLOR_30BIT:
+               video_mapping = 3;
+               break;
+       case HDMI_DEEP_COLOR_36BIT:
+               video_mapping = 5;
+               break;
+       case HDMI_DEEP_COLOR_24BIT:
+       default:
+               video_mapping = 1;
+               break;
+       }
+
+       /* VIDEO_MAPPING */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_TX_INVID0, video_mapping, 4, 0);
+}
+
+static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+       struct hdmi_core_infoframe_avi info_avi = ip_data->avi_cfg;
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVICONF0,
+                               info_avi.db1_format, 1, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVICONF0,
+                               info_avi.db1_active_info, 6, 6);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVICONF0,
+                               info_avi.db1_bar_info_dv, 3, 2);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVICONF0,
+                               info_avi.db1_scan_info, 5, 4);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVICONF1,
+                               info_avi.db2_colorimetry, 7, 6);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVICONF1,
+                               info_avi.db2_aspect_ratio, 5, 4);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVICONF1,
+                               info_avi.db2_active_fmt_ar, 3, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVICONF2,
+                               info_avi.db3_itc, 7, 7);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVICONF2,
+                               info_avi.db3_ec, 6, 4);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVICONF2,
+                               info_avi.db3_q_range, 3, 2);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVICONF2,
+                               info_avi.db3_nup_scaling, 1, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AVIVID,
+                               info_avi.db4_videocode, 6, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_PRCONF,
+                               info_avi.db5_pixel_repeat, 3, 0);
+}
+
+static void hdmi_core_csc_config(struct hdmi_ip_data *ip_data,
+                               struct csc_table csc_coeff)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_A1_MSB,
+                       csc_coeff.a1 >> 8 , 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_A1_LSB,
+                       csc_coeff.a1, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_A2_MSB,
+                       csc_coeff.a2 >> 8, 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_A2_LSB,
+                       csc_coeff.a2, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_A3_MSB,
+                       csc_coeff.a3 >> 8, 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_A3_LSB,
+                       csc_coeff.a3, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_A4_MSB,
+                       csc_coeff.a4 >> 8, 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_A4_LSB,
+                       csc_coeff.a4, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_B1_MSB,
+                       csc_coeff.b1 >> 8, 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_B1_LSB,
+                       csc_coeff.b1, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_B2_MSB,
+                       csc_coeff.b2 >> 8, 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_B2_LSB,
+                       csc_coeff.b2, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_B3_MSB,
+                       csc_coeff.b3 >> 8, 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_B3_LSB,
+                       csc_coeff.b3, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_B4_MSB,
+                       csc_coeff.b4 >> 8, 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_B4_LSB,
+                       csc_coeff.b4, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_C1_MSB,
+                       csc_coeff.c1 >> 8, 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_C1_LSB,
+                       csc_coeff.c1, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_C2_MSB,
+                       csc_coeff.c2 >> 8, 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_C2_LSB,
+                       csc_coeff.c2, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_C3_MSB,
+                       csc_coeff.c3 >> 8, 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_C3_LSB,
+                       csc_coeff.c3, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_C4_MSB,
+                       csc_coeff.c4 >> 8, 6, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CSC_COEF_C4_LSB,
+                       csc_coeff.c4, 7, 0);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_MC_FLOWCTRL, 0x1, 0, 0);
+}
+
+int ti_hdmi_5xxx_configure_range(struct hdmi_ip_data *ip_data)
+{
+       struct csc_table csc_coeff = {0};
+
+       switch (ip_data->cfg.range) {
+       case HDMI_LIMITED_RANGE:
+               csc_coeff =  csc_table_deepcolor[ip_data->cfg.deep_color];
+               ip_data->avi_cfg.db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_LR;
+               break;
+       case HDMI_FULL_RANGE:
+               csc_coeff =  csc_table_deepcolor[3];  /* full range */
+               ip_data->avi_cfg.db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_FR;
+               break;
+       }
+       hdmi_core_csc_config(ip_data, csc_coeff);
+       hdmi_core_aux_infoframe_avi_config(ip_data);
+       return 0;
+}
+
+static void hdmi_enable_video_path(struct hdmi_ip_data *ip_data)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+
+       printk(KERN_INFO "Enable video_path\n");
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_CTRLDUR, 0x0C, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_EXCTRLDUR, 0x20, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_EXCTRLSPAC, 0x01, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_CH0PREAM, 0x0B, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_CH1PREAM, 0x16, 5, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_CH2PREAM, 0x21, 5, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_MC_CLKDIS, 0x00, 0, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_MC_CLKDIS, 0x00, 1, 1);
+}
+
+static void hdmi_core_mask_interrupts(struct hdmi_ip_data *ip_data)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_VP_MASK, 0x0, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_MASK0, 0x0, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_MASK1, 0x0, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_MASK2, 0x0, 1, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_PHY_MASK0, 0x0, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_PHY_I2CM_INT_ADDR, 0x8, 3, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_PHY_I2CM_CTLINT_ADDR, 0x88, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_INT, 0xa3, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_CC08, 0x0, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_D010, 0x0, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_GP_MASK, 0x3, 1, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_HDCP_MASK, 0x0, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_CEC_MASK, 0xff, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_INT, 0x1, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_I2CM_CTLINT, 0xff, 7, 0);
+}
+
+static void hdmi_core_enable_interrupts(struct hdmi_ip_data *ip_data)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+       /* Unmute interrupts */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_IH_MUTE, 0x0, 1, 0);
+}
+
+int ti_hdmi_5xxx_core_irq_handler(struct hdmi_ip_data *ip_data)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_IH_FC_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_IH_FC_STAT1, 0xff, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_IH_FC_STAT2, 0xff, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_IH_AS_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_IH_I2CM_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_IH_CEC_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_IH_VP_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_IH_I2CMPHY_STAT0, 0xff, 7, 0);
+
+       return 0;
+}
+
+void ti_hdmi_5xxx_basic_configure(struct hdmi_ip_data *ip_data)
+{
+       /* HDMI */
+       struct omap_video_timings video_timing;
+       struct hdmi_video_format video_format;
+       /* HDMI core */
+       struct hdmi_core_vid_config v_core_cfg;
+       struct hdmi_core_infoframe_avi *avi_cfg = &ip_data->avi_cfg;
+       struct hdmi_config *cfg = &ip_data->cfg;
+       struct hdmi_irq_vector irq_enable;
+
+       hdmi_core_mask_interrupts(ip_data);
+
+       hdmi_wp_init(&video_timing, &video_format, &irq_enable);
+
+       hdmi_core_init(&v_core_cfg, avi_cfg, cfg);
+
+       hdmi_wp_video_init_format(&video_format, &video_timing, cfg);
+
+       hdmi_wp_video_config_timing(ip_data, &video_timing);
+
+       /* video config */
+       video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422;
+
+       hdmi_wp_video_config_format(ip_data, &video_format);
+
+       hdmi_wp_video_config_interface(ip_data);
+
+       if (ip_data->cfg.cm.mode == HDMI_DVI ||
+       (ip_data->cfg.cm.code == 1 && ip_data->cfg.cm.mode == HDMI_HDMI)) {
+               ip_data->cfg.range = HDMI_FULL_RANGE;
+               ti_hdmi_5xxx_configure_range(ip_data);
+       } else {
+               ip_data->cfg.range = HDMI_LIMITED_RANGE;
+               ti_hdmi_5xxx_configure_range(ip_data);
+       }
+
+       if (ip_data->cfg.cm.mode == HDMI_DVI ||
+       (ip_data->cfg.cm.code == 1 && ip_data->cfg.cm.mode == HDMI_HDMI)) {
+               ip_data->cfg.range = HDMI_FULL_RANGE;
+               ti_hdmi_5xxx_configure_range(ip_data);
+       } else {
+               ip_data->cfg.range = HDMI_LIMITED_RANGE;
+               ti_hdmi_5xxx_configure_range(ip_data);
+       }
+
+       /* Enable pll and core interrupts */
+       irq_enable.pll_recal = 1;
+       irq_enable.pll_unlock = 1;
+       irq_enable.pll_lock = 1;
+       irq_enable.phy_disconnect = 1;
+       irq_enable.phy_connect = 1;
+       irq_enable.phy_short_5v = 1;
+       irq_enable.video_end_fr = 1;
+       /* irq_enable.video_vsync = 1; */
+       irq_enable.fifo_sample_req = 1;
+       irq_enable.fifo_overflow = 1;
+       irq_enable.fifo_underflow = 1;
+       irq_enable.ocp_timeout = 1;
+
+       hdmi_wp_irq_enable(ip_data, &irq_enable);
+
+       /*
+        * configure core video part
+        * set software reset in the core
+        */
+       v_core_cfg.packet_mode = HDMI_PACKETMODE24BITPERPIXEL;
+
+       hdmi_core_video_config(ip_data, &v_core_cfg);
+
+       hdmi_core_config_video_packetizer(ip_data);
+       hdmi_core_config_csc(ip_data);
+       hdmi_core_config_video_sampler(ip_data);
+
+       /*
+        * configure packet
+        * info frame video see doc CEA861-D page 65
+        */
+       avi_cfg->db1_format = HDMI_INFOFRAME_AVI_DB1Y_RGB;
+       avi_cfg->db1_active_info =
+                       HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF;
+       avi_cfg->db1_bar_info_dv = HDMI_INFOFRAME_AVI_DB1B_NO;
+       avi_cfg->db1_scan_info = HDMI_INFOFRAME_AVI_DB1S_0;
+       avi_cfg->db2_colorimetry = HDMI_INFOFRAME_AVI_DB2C_NO;
+       avi_cfg->db2_aspect_ratio = HDMI_INFOFRAME_AVI_DB2M_NO;
+       avi_cfg->db2_active_fmt_ar = HDMI_INFOFRAME_AVI_DB2R_SAME;
+       avi_cfg->db3_itc = HDMI_INFOFRAME_AVI_DB3ITC_NO;
+       avi_cfg->db3_ec = HDMI_INFOFRAME_AVI_DB3EC_XVYUV601;
+       avi_cfg->db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_DEFAULT;
+       avi_cfg->db3_nup_scaling = HDMI_INFOFRAME_AVI_DB3SC_NO;
+       avi_cfg->db4_videocode = cfg->cm.code;
+       avi_cfg->db5_pixel_repeat = HDMI_INFOFRAME_AVI_DB5PR_NO;
+       avi_cfg->db6_7_line_eoftop = 0;
+       avi_cfg->db8_9_line_sofbottom = 0;
+       avi_cfg->db10_11_pixel_eofleft = 0;
+       avi_cfg->db12_13_pixel_sofright = 0;
+
+       hdmi_core_aux_infoframe_avi_config(ip_data);
+
+       if (ip_data->cfg.s3d_info.vsi_enabled)
+               hdmi_core_infoframe_vsi_config(ip_data, ip_data->cfg.s3d_info);
+
+       hdmi_enable_video_path(ip_data);
+
+       hdmi_core_enable_interrupts(ip_data);
+}
+
+
+#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+static void ti_hdmi_5xxx_wp_audio_config_format(struct hdmi_ip_data *ip_data,
+                                       struct hdmi_audio_format *aud_fmt)
+{
+       u32 r;
+
+       DSSDBG("Enter hdmi_wp_audio_config_format\n");
+       r = hdmi_read_reg(ip_data->base_wp, HDMI_WP_AUDIO_CFG);
+       r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5);
+       r = FLD_MOD(r, aud_fmt->type, 4, 4);
+       r = FLD_MOD(r, aud_fmt->justification, 3, 3);
+       r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1);
+       r = FLD_MOD(r, aud_fmt->sample_size, 0, 0);
+       hdmi_write_reg(ip_data->base_wp, HDMI_WP_AUDIO_CFG, r);
+}
+
+static void ti_hdmi_5xxx_core_audio_config(struct hdmi_ip_data *ip_data,
+                                       struct hdmi_core_audio_config *cfg)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+       u8 val;
+
+       /* Mute audio before configuring */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCONF, 0xf, 7, 4);
+
+       /* Set the N parameter */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_N1, cfg->n, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_N2, cfg->n >> 8, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_N3, cfg->n >> 16, 3, 0);
+
+       /*
+        * CTS manual mode. Automatic mode is not supported
+        * when using audio parallel interface.
+        */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_CTS3, 1, 4, 4);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_CTS1, cfg->cts, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_CTS2, cfg->cts >> 8, 7, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_CTS3,
+               cfg->cts >> 16, 3, 0);
+
+       /* Layout of Audio Sample Packets: 2-channel or multichannels */
+       if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH)
+               REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCONF, 0, 0, 0);
+       else
+               REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCONF, 1, 0, 0);
+
+       /* Configure IEC-609580 Validity bits */
+       /* Channel 0 is valid */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSV, 0, 0, 0);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSV, 0, 4, 4);
+
+       if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH)
+               val = 1;
+       else
+               val = 0;
+
+       /* Channels 1, 2 setting */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSV, val, 1, 1);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSV, val, 5, 5);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSV, val, 2, 2);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSV, val, 6, 6);
+       /* Channel 3 setting */
+       if (cfg->layout == HDMI_AUDIO_LAYOUT_6CH)
+               val = 1;
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSV, val, 3, 3);
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSV, val, 7, 7);
+
+       /* Configure IEC-60958 User bits */
+       /* TODO: should be set by user. */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSU, 0, 7, 0);
+
+       /* Configure IEC-60958 Channel Status word */
+       /* CGMSA */
+       val = cfg->iec60958_cfg->status[5] & IEC958_AES5_CON_CGMSA;
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(0), val, 5, 4);
+
+       /* Copyright */
+       val = (cfg->iec60958_cfg->status[0] &
+                       IEC958_AES0_CON_NOT_COPYRIGHT)>>2;
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(0), val, 0, 0);
+
+       /* Category */
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(1),
+               cfg->iec60958_cfg->status[1]);
+
+       /* PCM audio mode */
+       val = (cfg->iec60958_cfg->status[0] & IEC958_AES0_CON_MODE)>>6;
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(2), val, 6, 4);
+
+       /* Source number */
+       val = cfg->iec60958_cfg->status[2] & IEC958_AES2_CON_SOURCE;
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(2), val, 3, 4);
+
+       /* Channel number right 0  */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(3), 2, 3, 0);
+       /* Channel number right 1*/
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(3), 4, 7, 4);
+       /* Channel number right 2  */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(4), 6, 3, 0);
+       /* Channel number right 3*/
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(4), 8, 7, 4);
+       /* Channel number left 0  */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(5), 1, 3, 0);
+       /* Channel number left 1*/
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(5), 3, 7, 4);
+       /* Channel number left 2  */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(6), 5, 3, 0);
+       /* Channel number left 3*/
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(6), 7, 7, 4);
+
+       /* Clock accuracy and sample rate */
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(7),
+               cfg->iec60958_cfg->status[3]);
+
+       /* Original sample rate and word length */
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_AUDSCHNLS(8),
+               cfg->iec60958_cfg->status[4]);
+
+       /* Enable FIFO empty and full interrupts */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_INT, 3, 3, 2);
+
+       /* Configure GPA */
+       /* select HBR/SPDIF interfaces */
+       if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH) {
+               /* select HBR/SPDIF interfaces */
+               REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_CONF0, 0, 5, 5);
+               /* enable two channels in GPA */
+               REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_GP_CONF1, 3, 7, 0);
+       } else {
+               /* select HBR/SPDIF interfaces */
+               REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_CONF0, 0, 5, 5);
+               /* enable two channels in GPA */
+               REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_GP_CONF1, 0xFF, 7, 0);
+       }
+
+       /* disable HBR */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_GP_CONF2, 0, 0, 0);
+       /* enable PCUV */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_GP_CONF2, 1, 1, 1);
+       /* Enable GPA FIFO full and empty mask */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_GP_MASK, 3, 1, 0);
+       /* Set polarity of GPA FIFO empty interrupts */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_AUD_GP_POL, 1, 0, 0);
+
+       /*Unmute audio */
+       REG_FLD_MOD(core_sys_base, HDMI_CORE_FC_AUDSCONF, 0, 7, 4);
+}
+
+static void ti_hdmi_5xxx_core_audio_infoframe_cfg
+       (struct hdmi_ip_data *ip_data,
+        struct snd_cea_861_aud_if *info_aud)
+{
+       void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_AUDICONF0,
+               info_aud->db1_ct_cc);
+
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_AUDICONF1,
+               info_aud->db2_sf_ss);
+
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_AUDICONF2,
+               info_aud->db4_ca);
+
+       hdmi_write_reg(core_sys_base, HDMI_CORE_FC_AUDICONF3,
+               info_aud->db5_dminh_lsv);
+}
+
+int ti_hdmi_5xxx_audio_config(struct hdmi_ip_data *ip_data,
+               struct omap_dss_audio *audio)
+{
+       struct hdmi_audio_format audio_format;
+       struct hdmi_audio_dma audio_dma;
+       struct hdmi_core_audio_config core;
+       int err, n, cts, channel_count;
+       unsigned int fs_nr;
+       bool word_length_16b = false;
+
+       if (!audio || !audio->iec || !audio->cea || !ip_data)
+               return -EINVAL;
+
+       core.iec60958_cfg = audio->iec;
+
+       if (!(audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24))
+               if (audio->iec->status[4] & IEC958_AES4_CON_WORDLEN_20_16)
+                       word_length_16b = true;
+
+       /* only 16-bit word length supported atm */
+       if (!word_length_16b)
+               return -EINVAL;
+
+       switch (audio->iec->status[3] & IEC958_AES3_CON_FS) {
+       case IEC958_AES3_CON_FS_32000:
+               fs_nr = 32000;
+               break;
+       case IEC958_AES3_CON_FS_44100:
+               fs_nr = 44100;
+               break;
+       case IEC958_AES3_CON_FS_48000:
+               fs_nr = 48000;
+               break;
+       case IEC958_AES3_CON_FS_88200:
+               fs_nr = 88200;
+               break;
+       case IEC958_AES3_CON_FS_96000:
+               fs_nr = 96000;
+               break;
+       case IEC958_AES3_CON_FS_176400:
+               fs_nr = 176400;
+               break;
+       case IEC958_AES3_CON_FS_192000:
+               fs_nr = 192000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       err = hdmi_compute_acr(fs_nr, &n, &cts);
+       core.n = n;
+       core.cts = cts;
+
+       /* Audio channels settings */
+       channel_count = (audio->cea->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CC) + 1;
+
+       if (channel_count == 2)
+               core.layout = HDMI_AUDIO_LAYOUT_2CH;
+       else if (channel_count == 6)
+               core.layout = HDMI_AUDIO_LAYOUT_6CH;
+       else
+               core.layout = HDMI_AUDIO_LAYOUT_8CH;
+
+       /* DMA settings */
+       if (word_length_16b)
+               audio_dma.transfer_size = 0x10;
+       else
+               audio_dma.transfer_size = 0x20;
+       audio_dma.block_size = 0xC0;
+       audio_dma.mode = HDMI_AUDIO_TRANSF_DMA;
+       audio_dma.fifo_threshold = 0x20; /* in number of samples */
+
+       /* audio FIFO format settings for 16-bit samples*/
+       audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES;
+       audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS;
+       audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT;
+
+       /* only LPCM atm */
+       audio_format.type = HDMI_AUDIO_TYPE_LPCM;
+
+       /* disable start/stop signals of IEC 60958 blocks */
+       audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON;
+
+       /* configure DMA and audio FIFO format*/
+       ti_hdmi_4xxx_wp_audio_config_dma(ip_data, &audio_dma);
+       ti_hdmi_5xxx_wp_audio_config_format(ip_data, &audio_format);
+
+       /* configure the core*/
+       ti_hdmi_5xxx_core_audio_config(ip_data, &core);
+
+       /* configure CEA 861 audio infoframe*/
+       ti_hdmi_5xxx_core_audio_infoframe_cfg(ip_data, audio->cea);
+
+       return 0;
+}
+
+int ti_hdmi_5xxx_audio_start(struct hdmi_ip_data *ip_data)
+{
+       REG_FLD_MOD(ip_data->base_wp,
+               HDMI_WP_AUDIO_CTRL, 1, 30, 30);
+       return 0;
+}
+
+void ti_hdmi_5xxx_audio_stop(struct hdmi_ip_data *ip_data)
+{
+       REG_FLD_MOD(ip_data->base_wp,
+               HDMI_WP_AUDIO_CTRL, 0, 30, 30);
+}
+
+#endif
diff --git a/drivers/video/omap2/dss/ti_hdmi_5xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_5xxx_ip.h
new file mode 100644 (file)
index 0000000..8dc79ba
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * ti_hdmi_5xxx_ip.h
+ *
+ * HDMI driver definition for TI OMAP5 processors.
+ *
+ * Copyright (C) 2011-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 version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _OMAP5_DSS_HDMI_H_
+#define _OMAP5_DSS_HDMI_H_
+
+#include <linux/string.h>
+#include <video/omapdss.h>
+#include "ti_hdmi_4xxx_ip.h"
+#include "ti_hdmi.h"
+
+/* HDMI IP Core System */
+
+/* HDMI Identification */
+#define HDMI_CORE_DESIGN_ID                    0x00000
+#define HDMI_CORE_REVISION_ID                  0x00004
+#define HDMI_CORE_PRODUCT_ID0                  0x00008
+#define HDMI_CORE_PRODUCT_ID1                  0x0000C
+#define HDMI_CORE_CONFIG0_ID                   0x00010
+#define HDMI_CORE_CONFIG1_ID                   0x00014
+#define HDMI_CORE_CONFIG2_ID                   0x00018
+#define HDMI_CORE_CONFIG3_ID                   0x0001C
+
+/* HDMI Interrupt */
+#define HDMI_CORE_IH_FC_STAT0                  0x00400
+#define HDMI_CORE_IH_FC_STAT1                  0x00404
+#define HDMI_CORE_IH_FC_STAT2                  0x00408
+#define HDMI_CORE_IH_AS_STAT0                  0x0040C
+#define HDMI_CORE_IH_PHY_STAT0                 0x00410
+#define HDMI_CORE_IH_I2CM_STAT0                        0x00414
+#define HDMI_CORE_IH_CEC_STAT0                 0x00418
+#define HDMI_CORE_IH_VP_STAT0                  0x0041C
+#define HDMI_CORE_IH_I2CMPHY_STAT0             0x00420
+#define HDMI_CORE_IH_MUTE                      0x007FC
+
+/* HDMI Video Sampler */
+#define HDMI_CORE_TX_INVID0                    0x00800
+#define HDMI_CORE_TX_INSTUFFING                        0x00804
+#define HDMI_CORE_TX_RGYDATA0                  0x00808
+#define HDMI_CORE_TX_RGYDATA1                  0x0080C
+#define HDMI_CORE_TX_RCRDATA0                  0x00810
+#define HDMI_CORE_TX_RCRDATA1                  0x00814
+#define HDMI_CORE_TX_BCBDATA0                  0x00818
+#define HDMI_CORE_TX_BCBDATA1                  0x0081C
+
+/* HDMI Video Packetizer */
+#define HDMI_CORE_VP_STATUS                    0x02000
+#define HDMI_CORE_VP_PR_CD                     0x02004
+#define HDMI_CORE_VP_STUFF                     0x02008
+#define HDMI_CORE_VP_REMAP                     0x0200C
+#define HDMI_CORE_VP_CONF                      0x02010
+#define HDMI_CORE_VP_STAT                      0x02014
+#define HDMI_CORE_VP_INT                       0x02018
+#define HDMI_CORE_VP_MASK                      0x0201C
+#define HDMI_CORE_VP_POL                       0x02020
+
+/* Frame Composer */
+#define HDMI_CORE_FC_INVIDCONF                 0x04000
+#define HDMI_CORE_FC_INHACTIV0                 0x04004
+#define HDMI_CORE_FC_INHACTIV1                 0x04008
+#define HDMI_CORE_FC_INHBLANK0                 0x0400C
+#define HDMI_CORE_FC_INHBLANK1                 0x04010
+#define HDMI_CORE_FC_INVACTIV0                 0x04014
+#define HDMI_CORE_FC_INVACTIV1                 0x04018
+#define HDMI_CORE_FC_INVBLANK                  0x0401C
+#define HDMI_CORE_FC_HSYNCINDELAY0             0x04020
+#define HDMI_CORE_FC_HSYNCINDELAY1             0x04024
+#define HDMI_CORE_FC_HSYNCINWIDTH0             0x04028
+#define HDMI_CORE_FC_HSYNCINWIDTH1             0x0402C
+#define HDMI_CORE_FC_VSYNCINDELAY              0x04030
+#define HDMI_CORE_FC_VSYNCINWIDTH              0x04034
+#define HDMI_CORE_FC_INFREQ0                   0x04038
+#define HDMI_CORE_FC_INFREQ1                   0x0403C
+#define HDMI_CORE_FC_INFREQ2                   0x04040
+#define HDMI_CORE_FC_CTRLDUR                   0x04044
+#define HDMI_CORE_FC_EXCTRLDUR                 0x04048
+#define HDMI_CORE_FC_EXCTRLSPAC                        0x0404C
+#define HDMI_CORE_FC_CH0PREAM                  0x04050
+#define HDMI_CORE_FC_CH1PREAM                  0x04054
+#define HDMI_CORE_FC_CH2PREAM                  0x04058
+#define HDMI_CORE_FC_AVICONF3                  0x0405C
+#define HDMI_CORE_FC_GCP                       0x04060
+#define HDMI_CORE_FC_AVICONF0                  0x04064
+#define HDMI_CORE_FC_AVICONF1                  0x04068
+#define HDMI_CORE_FC_AVICONF2                  0x0406C
+#define HDMI_CORE_FC_AVIVID                    0x04070
+#define HDMI_CORE_FC_AVIETB0                   0x04074
+#define HDMI_CORE_FC_AVIETB1                   0x04078
+#define HDMI_CORE_FC_AVISBB0                   0x0407C
+#define HDMI_CORE_FC_AVISBB1                   0x04080
+#define HDMI_CORE_FC_AVIELB0                   0x04084
+#define HDMI_CORE_FC_AVIELB1                   0x04088
+#define HDMI_CORE_FC_AVISRB0                   0x0408C
+#define HDMI_CORE_FC_AVISRB1                   0x04090
+#define HDMI_CORE_FC_AUDICONF0                 0x04094
+#define HDMI_CORE_FC_AUDICONF1                 0x04098
+#define HDMI_CORE_FC_AUDICONF2                 0x0409C
+#define HDMI_CORE_FC_AUDICONF3                 0x040A0
+#define HDMI_CORE_FC_VSDIEEEID0                        0x040A4
+#define HDMI_CORE_FC_VSDSIZE                   0x040A8
+#define HDMI_CORE_FC_VSDIEEEID1                        0x040C0
+#define HDMI_CORE_FC_VSDIEEEID2                        0x040C4
+#define HDMI_CORE_FC_VSDPAYLOAD(n)             (n * 4 + 0x040C8)
+#define HDMI_CORE_FC_SPDVENDORNAME(n)          (n * 4 + 0x04128)
+#define HDMI_CORE_FC_SPDPRODUCTNAME(n)         (n * 4 + 0x04148)
+#define HDMI_CORE_FC_SPDDEVICEINF              0x04188
+#define HDMI_CORE_FC_AUDSCONF                  0x0418C
+#define HDMI_CORE_FC_AUDSSTAT                  0x04190
+#define HDMI_CORE_FC_AUDSV                     0x04194
+#define HDMI_CORE_FC_AUDSU                     0x04198
+#define HDMI_CORE_FC_AUDSCHNLS(n)              (n * 4 + 0x0419C)
+#define HDMI_CORE_FC_CTRLQHIGH                 0x041CC
+#define HDMI_CORE_FC_CTRLQLOW                  0x041D0
+#define HDMI_CORE_FC_ACP0                      0x041D4
+#define HDMI_CORE_FC_ACP(n)                    ((16-n) * 4 + 0x04208)
+#define HDMI_CORE_FC_ISCR1_0                   0x04248
+#define HDMI_CORE_FC_ISCR1(n)                  ((16-n) * 4 + 0x0424C)
+#define HDMI_CORE_FC_ISCR2(n)                  ((15-n) * 4 + 0x0428C)
+#define HDMI_CORE_FC_DATAUTO0                  0x042CC
+#define HDMI_CORE_FC_DATAUTO1                  0x042D0
+#define HDMI_CORE_FC_DATAUTO2                  0x042D4
+#define HDMI_CORE_FC_DATMAN                    0x042D8
+#define HDMI_CORE_FC_DATAUTO3                  0x042DC
+#define HDMI_CORE_FC_RDRB(n)                   (n * 4 + 0x042E0)
+#define HDMI_CORE_FC_STAT0                     0x04340
+#define HDMI_CORE_FC_INT0                      0x04344
+#define HDMI_CORE_FC_MASK0                     0x04348
+#define HDMI_CORE_FC_POL0                      0x0434C
+#define HDMI_CORE_FC_STAT1                     0x04350
+#define HDMI_CORE_FC_INT1                      0x04354
+#define HDMI_CORE_FC_MASK1                     0x04358
+#define HDMI_CORE_FC_POL1                      0x0435C
+#define HDMI_CORE_FC_STAT2                     0x04360
+#define HDMI_CORE_FC_INT2                      0x04364
+#define HDMI_CORE_FC_MASK2                     0x04368
+#define HDMI_CORE_FC_POL2                      0x0436C
+#define HDMI_CORE_FC_PRCONF                    0x04380
+#define HDMI_CORE_FC_GMD_STAT                  0x04400
+#define HDMI_CORE_FC_GMD_EN                    0x04404
+#define HDMI_CORE_FC_GMD_UP                    0x04408
+#define HDMI_CORE_FC_GMD_CONF                  0x0440C
+#define HDMI_CORE_FC_GMD_HB                    0x04410
+#define HDMI_CORE_FC_GMD_PB(n)                 (n * 4 + 0x04414)
+#define HDMI_CORE_FC_DBGFORCE                  0x04800
+#define HDMI_CORE_FC_DBGAUD0CH0                        0x04804
+#define HDMI_CORE_FC_DBGAUD1CH0                        0x04808
+#define HDMI_CORE_FC_DBGAUD2CH0                        0x0480C
+#define HDMI_CORE_FC_DBGAUD0CH1                        0x04810
+#define HDMI_CORE_FC_DBGAUD1CH1                        0x04814
+#define HDMI_CORE_FC_DBGAUD2CH1                        0x04818
+#define HDMI_CORE_FC_DBGAUD0CH2                        0x0481C
+#define HDMI_CORE_FC_DBGAUD1CH2                        0x04820
+#define HDMI_CORE_FC_DBGAUD2CH2                        0x04824
+#define HDMI_CORE_FC_DBGAUD0CH3                        0x04828
+#define HDMI_CORE_FC_DBGAUD1CH3                        0x0482C
+#define HDMI_CORE_FC_DBGAUD2CH3                        0x04830
+#define HDMI_CORE_FC_DBGAUD0CH4                        0x04834
+#define HDMI_CORE_FC_DBGAUD1CH4                        0x04838
+#define HDMI_CORE_FC_DBGAUD2CH4                        0x0483C
+#define HDMI_CORE_FC_DBGAUD0CH5                        0x04840
+#define HDMI_CORE_FC_DBGAUD1CH5                        0x04844
+#define HDMI_CORE_FC_DBGAUD2CH5                        0x04848
+#define HDMI_CORE_FC_DBGAUD0CH6                        0x0484C
+#define HDMI_CORE_FC_DBGAUD1CH6                        0x04850
+#define HDMI_CORE_FC_DBGAUD2CH6                        0x04854
+#define HDMI_CORE_FC_DBGAUD0CH7                        0x04858
+#define HDMI_CORE_FC_DBGAUD1CH7                        0x0485C
+#define HDMI_CORE_FC_DBGAUD2CH7                        0x04860
+#define HDMI_CORE_FC_DBGTMDS0                  0x04864
+#define HDMI_CORE_FC_DBGTMDS1                  0x04868
+#define HDMI_CORE_FC_DBGTMDS2                  0x0486C
+#define HDMI_CORE_PHY_MASK0                    0x0C018
+#define HDMI_CORE_PHY_I2CM_INT_ADDR            0x0C09C
+#define HDMI_CORE_PHY_I2CM_CTLINT_ADDR         0x0C0A0
+
+/* HDMI Audio */
+#define HDMI_CORE_AUD_CONF0                    0x0C400
+#define HDMI_CORE_AUD_CONF1                    0x0C404
+#define HDMI_CORE_AUD_INT                      0x0C408
+#define HDMI_CORE_AUD_N1                       0x0C800
+#define HDMI_CORE_AUD_N2                       0x0C804
+#define HDMI_CORE_AUD_N3                       0x0C808
+#define HDMI_CORE_AUD_CTS1                     0x0C80C
+#define HDMI_CORE_AUD_CTS2                     0x0C810
+#define HDMI_CORE_AUD_CTS3                     0x0C814
+#define HDMI_CORE_AUD_INCLKFS                  0x0C818
+#define HDMI_CORE_AUD_CC08                     0x0CC08
+#define HDMI_CORE_AUD_GP_CONF0                 0x0D400
+#define HDMI_CORE_AUD_GP_CONF1                 0x0D404
+#define HDMI_CORE_AUD_GP_CONF2                 0x0D408
+#define HDMI_CORE_AUD_D010                     0x0D010
+#define HDMI_CORE_AUD_GP_STAT                  0x0D40C
+#define HDMI_CORE_AUD_GP_INT                   0x0D410
+#define HDMI_CORE_AUD_GP_POL                   0x0D414
+#define HDMI_CORE_AUD_GP_MASK                  0x0D418
+
+/* HDMI Main Controller */
+#define HDMI_CORE_MC_CLKDIS                    0x10004
+#define HDMI_CORE_MC_SWRSTZREQ                 0x10008
+#define HDMI_CORE_MC_FLOWCTRL                  0x10010
+#define HDMI_CORE_MC_PHYRSTZ                   0x10014
+#define HDMI_CORE_MC_LOCKONCLOCK               0x10018
+
+/* HDMI COLOR SPACE CONVERTER */
+#define HDMI_CORE_CSC_CFG                      0x10400
+#define HDMI_CORE_CSC_SCALE                    0x10404
+#define HDMI_CORE_CSC_COEF_A1_MSB              0x10408
+#define HDMI_CORE_CSC_COEF_A1_LSB              0x1040C
+#define HDMI_CORE_CSC_COEF_A2_MSB              0x10410
+#define HDMI_CORE_CSC_COEF_A2_LSB              0x10414
+#define HDMI_CORE_CSC_COEF_A3_MSB              0x10418
+#define HDMI_CORE_CSC_COEF_A3_LSB              0x1041C
+#define HDMI_CORE_CSC_COEF_A4_MSB              0x10420
+#define HDMI_CORE_CSC_COEF_A4_LSB              0x10424
+#define HDMI_CORE_CSC_COEF_B1_MSB              0x10428
+#define HDMI_CORE_CSC_COEF_B1_LSB              0x1042C
+#define HDMI_CORE_CSC_COEF_B2_MSB              0x10430
+#define HDMI_CORE_CSC_COEF_B2_LSB              0x10434
+#define HDMI_CORE_CSC_COEF_B3_MSB              0x10438
+#define HDMI_CORE_CSC_COEF_B3_LSB              0x1043C
+#define HDMI_CORE_CSC_COEF_B4_MSB              0x10440
+#define HDMI_CORE_CSC_COEF_B4_LSB              0x10444
+#define HDMI_CORE_CSC_COEF_C1_MSB              0x10448
+#define HDMI_CORE_CSC_COEF_C1_LSB              0x1044C
+#define HDMI_CORE_CSC_COEF_C2_MSB              0x10450
+#define HDMI_CORE_CSC_COEF_C2_LSB              0x10454
+#define HDMI_CORE_CSC_COEF_C3_MSB              0x10458
+#define HDMI_CORE_CSC_COEF_C3_LSB              0x1045C
+#define HDMI_CORE_CSC_COEF_C4_MSB              0x10460
+#define HDMI_CORE_CSC_COEF_C4_LSB              0x10464
+
+/* HDMI HDCP */
+#define HDMI_CORE_HDCP_MASK                    0x14020
+
+/* HDMI CEC */
+#define HDMI_CORE_CEC_MASK                     0x17408
+
+/* HDMI I2C Master */
+#define HDMI_CORE_I2CM_SLAVE                   0x157C8
+#define HDMI_CORE_I2CM_ADDRESS                 0x157CC
+#define HDMI_CORE_I2CM_DATAO                   0x157D0
+#define HDMI_CORE_I2CM_DATAI                   0X157D4
+#define HDMI_CORE_I2CM_OPERATION               0x157D8
+#define HDMI_CORE_I2CM_INT                     0x157DC
+#define HDMI_CORE_I2CM_CTLINT                  0x157E0
+#define HDMI_CORE_I2CM_DIV                     0x157E4
+#define HDMI_CORE_I2CM_SEGADDR                 0x157E8
+#define HDMI_CORE_I2CM_SOFTRSTZ                        0x157EC
+#define HDMI_CORE_I2CM_SEGPTR                  0x157F0
+#define HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR      0x157F4
+#define HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR      0x157F8
+#define HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR      0x157FC
+#define HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR      0x15800
+#define HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR      0x15804
+#define HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR      0x15808
+#define HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR      0x1580C
+#define HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR      0x15810
+#define HDMI_CORE_I2CM_SDA_HOLD_ADDR           0x15814
+
+struct hdmi_core_vid_config    {
+       struct hdmi_config  v_fc_config;
+       enum hdmi_core_packet_mode packet_mode;
+       int data_enable_pol;
+       int vblank_osc;
+       int hblank;
+       int vblank;
+};
+
+struct csc_table {
+       u16 a1, a2, a3, a4;
+       u16 b1, b2, b3, b4;
+       u16 c1, c2, c3, c4;
+};
+#endif
index 19fa73a0374c2fab7a3d09c8f9808ca7ce813c72..782be4c18cf735783fd8e46188b3dc3e5defc32f 100644 (file)
@@ -103,6 +103,13 @@ config WM8350_WATCHDOG
          Support for the watchdog in the WM8350 AudioPlus PMIC.  When
          the watchdog triggers the system will be reset.
 
+config PALMAS_WATCHDOG
+       tristate "Palmas Watchdog"
+       depends on MFD_PALMAS
+       help
+         Support for the watchdog in the Palmas family of PMICs. When the
+         watchdog times out system will be reset.
+
 # ALPHA Architecture
 
 # ARM Architecture
index 97bbdb3a464844588fa97e526b89eb284f302145..1de19dc11addfed65478896609f04922f2b5ab8b 100644 (file)
@@ -169,3 +169,4 @@ obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
 obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
 obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
 obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
+obj-$(CONFIG_PALMAS_WATCHDOG) += palmas_wdt.o
index b0e541d022e68a23d6edb8d05bd306d21d291728..e75cce3fb75ee7512cebc8aa17fc80d299b5d3c0 100644 (file)
@@ -41,7 +41,9 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
 #include <linux/platform_data/omap-wd-timer.h>
+#include <linux/of.h>
 
 #include "omap_wdt.h"
 
@@ -49,6 +51,11 @@ static unsigned timer_margin;
 module_param(timer_margin, uint, 0);
 MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
 
+static int hw_pet;
+static int kernelpet = 1;
+module_param(kernelpet, int, 0);
+MODULE_PARM_DESC(kernelpet, "pet watchdog in kernel via irq");
+
 struct omap_wdt_dev {
        void __iomem    *base;          /* physical */
        struct device   *dev;
@@ -56,6 +63,7 @@ struct omap_wdt_dev {
        struct resource *mem;
        int             wdt_trgr_pattern;
        struct mutex    lock;           /* to avoid races with PM */
+       int             irq;
 };
 
 static void omap_wdt_reload(struct omap_wdt_dev *wdev)
@@ -107,6 +115,7 @@ static void omap_wdt_set_timer(struct omap_wdt_dev *wdev,
                                   unsigned int timeout)
 {
        u32 pre_margin = GET_WLDR_VAL(timeout);
+       u32 delay_period = GET_WLDR_VAL(timeout / 2);
        void __iomem *base = wdev->base;
 
        /* just count up at 32 KHz */
@@ -116,8 +125,29 @@ static void omap_wdt_set_timer(struct omap_wdt_dev *wdev,
        __raw_writel(pre_margin, base + OMAP_WATCHDOG_LDR);
        while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
                cpu_relax();
+
+       /* Set delay interrupt to half the watchdog interval. */
+       while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 1 << 5)
+               cpu_relax();
+       __raw_writel(delay_period, base + OMAP_WATCHDOG_WDLY);
 }
 
+static irqreturn_t omap_wdt_interrupt(int irq, void *dev_id)
+{
+       struct watchdog_device *wdog = dev_id;
+       struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+       void __iomem *base = wdev->base;
+       u32 i;
+
+       i = __raw_readl(base + OMAP_WATCHDOG_WIRQSTAT);
+       __raw_writel(i, base + OMAP_WATCHDOG_WIRQSTAT);
+
+       omap_wdt_reload(wdev);
+
+       return IRQ_HANDLED;
+}
+
+
 static int omap_wdt_start(struct watchdog_device *wdog)
 {
        struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
@@ -139,6 +169,13 @@ static int omap_wdt_start(struct watchdog_device *wdog)
 
        omap_wdt_set_timer(wdev, wdog->timeout);
        omap_wdt_reload(wdev); /* trigger loading of new timeout value */
+
+       /* Enable delay interrupt */
+       if (kernelpet && wdev->irq) {
+               __raw_writel(0x2, base + OMAP_WATCHDOG_WIRQENSET);
+               __raw_writel(0x2, base + OMAP_WATCHDOG_WIRQWAKEEN);
+       }
+
        omap_wdt_enable(wdev);
 
        mutex_unlock(&wdev->lock);
@@ -149,9 +186,13 @@ static int omap_wdt_start(struct watchdog_device *wdog)
 static int omap_wdt_stop(struct watchdog_device *wdog)
 {
        struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+       void __iomem *base = wdev->base;
 
        mutex_lock(&wdev->lock);
        omap_wdt_disable(wdev);
+       /* Disable delay interrupt */
+       if (kernelpet && wdev->irq)
+               __raw_writel(0x2, base + OMAP_WATCHDOG_WIRQENCLR);
        pm_runtime_put_sync(wdev->dev);
        wdev->omap_wdt_users = false;
        mutex_unlock(&wdev->lock);
@@ -162,6 +203,11 @@ static int omap_wdt_ping(struct watchdog_device *wdog)
 {
        struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
 
+       if (hw_pet) {
+               pr_info("Hw ping is enabled,Skipping userspace ping\n");
+               return 0;
+       }
+
        mutex_lock(&wdev->lock);
        omap_wdt_reload(wdev);
        mutex_unlock(&wdev->lock);
@@ -203,8 +249,9 @@ static int omap_wdt_probe(struct platform_device *pdev)
        struct omap_wd_timer_platform_data *pdata = pdev->dev.platform_data;
        bool nowayout = WATCHDOG_NOWAYOUT;
        struct watchdog_device *omap_wdt;
-       struct resource *res, *mem;
+       struct resource *res, *mem, *res_irq;
        struct omap_wdt_dev *wdev;
+       struct device_node *np_wdt = pdev->dev.of_node;
        u32 rs;
        int ret;
 
@@ -236,6 +283,23 @@ static int omap_wdt_probe(struct platform_device *pdev)
        if (!wdev->base)
                return -ENOMEM;
 
+       if (of_device_is_compatible(np_wdt, "ti,omap3-wdt")) {
+               hw_pet = 0;
+               kernelpet = hw_pet;
+       } else if (of_device_is_compatible(np_wdt, "ti,omap4-wdt")) {
+               hw_pet = kernelpet;
+       }
+
+       if (kernelpet) {
+               res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+               if (res_irq) {
+                       ret = request_irq(res_irq->start, omap_wdt_interrupt,
+                                         0, dev_name(&pdev->dev), omap_wdt);
+
+                       wdev->irq = res_irq->start;
+               }
+       }
+
        omap_wdt->info        = &omap_wdt_info;
        omap_wdt->ops         = &omap_wdt_ops;
        omap_wdt->min_timeout = TIMER_MARGIN_MIN;
@@ -276,6 +340,9 @@ static int omap_wdt_probe(struct platform_device *pdev)
 
        pm_runtime_put_sync(wdev->dev);
 
+       if (kernelpet && wdev->irq)
+               return omap_wdt_start(omap_wdt);
+
        return 0;
 }
 
@@ -348,7 +415,8 @@ static int omap_wdt_resume(struct platform_device *pdev)
 #endif
 
 static const struct of_device_id omap_wdt_of_match[] = {
-       { .compatible = "ti,omap3-wdt", },
+       { .compatible = "ti,omap3-wdt" },
+       { .compatible = "ti,omap4-wdt" },
        {},
 };
 MODULE_DEVICE_TABLE(of, omap_wdt_of_match);
index 09b774cf75b9be8ebd83fefbdbc199e2c432a631..5004537ede06bcbe308bf54ea03a3f3e8f23574b 100644 (file)
 #define OMAP_WATCHDOG_LDR              (0x2c)
 #define OMAP_WATCHDOG_TGR              (0x30)
 #define OMAP_WATCHDOG_WPS              (0x34)
+#define OMAP_WATCHDOG_WDLY             (0x44)
 #define OMAP_WATCHDOG_SPR              (0x48)
+#define OMAP_WATCHDOG_WIRQSTAT         (0x58)
+#define OMAP_WATCHDOG_WIRQENSET        (0x5c)
+#define OMAP_WATCHDOG_WIRQENCLR        (0x60)
+#define OMAP_WATCHDOG_WIRQWAKEEN       (0x64)
 
 /* Using the prescaler, the OMAP watchdog could go for many
  * months before firing.  These limits work without scaling,
diff --git a/drivers/watchdog/palmas_wdt.c b/drivers/watchdog/palmas_wdt.c
new file mode 100644 (file)
index 0000000..be77f02
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Driver for Watchdog part of Palmas PMIC Chips
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ *
+ * Based on twl4030_wdt.c
+ *
+ * Author: Timo Kokkonen <timo.t.kokkonen at 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/palmas.h>
+
+static struct platform_device *palmas_wdt_dev;
+
+struct palmas_wdt {
+       struct palmas *palmas;
+
+       struct miscdevice miscdev;
+       int timer_margin;
+       unsigned long state;
+};
+
+#define PALMAS_WDT_ENABLED     (1<<1)
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+       "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int palmas_wdt_write(struct palmas *palmas, unsigned int data)
+{
+       int slave;
+       unsigned int addr;
+
+       slave = PALMAS_BASE_TO_SLAVE(PALMAS_PMU_CONTROL_BASE);
+       addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE, PALMAS_WATCHDOG);
+
+       return regmap_write(palmas->regmap[slave], addr, data);
+}
+
+static int palmas_wdt_enable(struct palmas_wdt *wdt)
+{
+       return palmas_wdt_write(wdt->palmas,
+                       wdt->timer_margin | PALMAS_WATCHDOG_ENABLE);
+}
+
+static int palmas_wdt_disable(struct palmas_wdt *wdt)
+{
+       return palmas_wdt_write(wdt->palmas, wdt->timer_margin);
+}
+
+static int palmas_wdt_set_timeout(struct palmas_wdt *wdt, int timeout)
+{
+       if (timeout < 1 || timeout > 128) {
+               dev_warn(wdt->miscdev.parent,
+                       "Timeout can only be in the range [1-128] seconds");
+               return -EINVAL;
+       }
+       wdt->timer_margin = fls(timeout) - 1;
+       return palmas_wdt_enable(wdt);
+}
+
+static ssize_t palmas_wdt_write_fop(struct file *file,
+               const char __user *data, size_t len, loff_t *ppos)
+{
+       struct palmas_wdt *wdt = file->private_data;
+
+       if (len)
+               palmas_wdt_enable(wdt);
+
+       return len;
+}
+
+static long palmas_wdt_ioctl(struct file *file,
+               unsigned int cmd, unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       int __user *p = argp;
+       int new_margin;
+       struct palmas_wdt *wdt = file->private_data;
+       int time = 0;
+
+       static const struct watchdog_info palmas_wd_ident = {
+               .identity = "Palmas Watchdog",
+               .options = WDIOF_SETTIMEOUT,
+               .firmware_version = 0,
+       };
+
+       switch (cmd) {
+       case WDIOC_GETSUPPORT:
+               return copy_to_user(argp, &palmas_wd_ident,
+                               sizeof(palmas_wd_ident)) ? -EFAULT : 0;
+
+       case WDIOC_GETSTATUS:
+       case WDIOC_GETBOOTSTATUS:
+               return put_user(0, p);
+
+       case WDIOC_KEEPALIVE:
+               palmas_wdt_enable(wdt);
+               break;
+
+       case WDIOC_SETTIMEOUT:
+               if (get_user(new_margin, p))
+                       return -EFAULT;
+               if (palmas_wdt_set_timeout(wdt, new_margin))
+                       return -EINVAL;
+
+               time = 1 << wdt->timer_margin;
+
+               return put_user(time, p);
+
+       case WDIOC_GETTIMEOUT:
+               time = 1 << wdt->timer_margin;
+
+               return put_user(time, p);
+
+       default:
+               return -ENOTTY;
+       }
+
+       return 0;
+}
+
+static int palmas_wdt_open(struct inode *inode, struct file *file)
+{
+       struct palmas_wdt *wdt = platform_get_drvdata(palmas_wdt_dev);
+
+       /* /dev/watchdog can only be opened once */
+       if (test_and_set_bit(0, &wdt->state))
+               return -EBUSY;
+
+       wdt->state |= PALMAS_WDT_ENABLED;
+       file->private_data = (void *) wdt;
+
+       palmas_wdt_enable(wdt);
+       return nonseekable_open(inode, file);
+}
+
+static int palmas_wdt_release(struct inode *inode, struct file *file)
+{
+       struct palmas_wdt *wdt = file->private_data;
+       if (nowayout) {
+               dev_alert(wdt->miscdev.parent,
+                      "Unexpected close, watchdog still running!\n");
+               palmas_wdt_enable(wdt);
+       } else {
+               if (palmas_wdt_disable(wdt))
+                       return -EFAULT;
+               wdt->state &= ~PALMAS_WDT_ENABLED;
+       }
+
+       clear_bit(0, &wdt->state);
+       return 0;
+}
+
+static const struct file_operations palmas_wdt_fops = {
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .open           = palmas_wdt_open,
+       .release        = palmas_wdt_release,
+       .unlocked_ioctl = palmas_wdt_ioctl,
+       .write          = palmas_wdt_write_fop,
+};
+
+static int palmas_wdt_probe(struct platform_device *pdev)
+{
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+       struct palmas_wdt *wdt;
+       int ret = 0;
+
+       wdt = kzalloc(sizeof(*wdt), GFP_KERNEL);
+       if (!wdt)
+               return -ENOMEM;
+
+       wdt->palmas             = palmas;
+       wdt->state              = 0;
+       wdt->timer_margin       = 7;
+       wdt->miscdev.parent     = &pdev->dev;
+       wdt->miscdev.fops       = &palmas_wdt_fops;
+       wdt->miscdev.minor      = WATCHDOG_MINOR;
+       wdt->miscdev.name       = "watchdog";
+
+       platform_set_drvdata(pdev, wdt);
+
+       palmas_wdt_dev = pdev;
+
+       palmas_wdt_disable(wdt);
+
+       ret = misc_register(&wdt->miscdev);
+       if (ret) {
+               dev_err(wdt->miscdev.parent,
+                       "Failed to register misc device\n");
+               platform_set_drvdata(pdev, NULL);
+               kfree(wdt);
+               palmas_wdt_dev = NULL;
+               return ret;
+       }
+       return 0;
+}
+
+static int palmas_wdt_remove(struct platform_device *pdev)
+{
+       struct palmas_wdt *wdt = platform_get_drvdata(pdev);
+
+       if (wdt->state & PALMAS_WDT_ENABLED)
+               if (palmas_wdt_disable(wdt))
+                       return -EFAULT;
+
+       wdt->state &= ~PALMAS_WDT_ENABLED;
+       misc_deregister(&wdt->miscdev);
+
+       platform_set_drvdata(pdev, NULL);
+       kfree(wdt);
+       palmas_wdt_dev = NULL;
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int palmas_wdt_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct palmas_wdt *wdt = platform_get_drvdata(pdev);
+       if (wdt->state & PALMAS_WDT_ENABLED)
+               return palmas_wdt_disable(wdt);
+
+       return 0;
+}
+
+static int palmas_wdt_resume(struct platform_device *pdev)
+{
+       struct palmas_wdt *wdt = platform_get_drvdata(pdev);
+       if (wdt->state & PALMAS_WDT_ENABLED)
+               return palmas_wdt_enable(wdt);
+
+       return 0;
+}
+#else
+#define palmas_wdt_suspend        NULL
+#define palmas_wdt_resume         NULL
+#endif
+
+static struct of_device_id of_palmas_match_tbl[] = {
+       { .compatible = "ti,palmas-wdt", },
+       { /* end */ }
+};
+
+static struct platform_driver palmas_wdt_driver = {
+       .probe = palmas_wdt_probe,
+       .remove = palmas_wdt_remove,
+       .suspend = palmas_wdt_suspend,
+       .resume = palmas_wdt_resume,
+       .driver = {
+               .owner = THIS_MODULE,
+               .of_match_table = of_palmas_match_tbl,
+               .name = "palmas-wdt",
+       },
+};
+
+static int __init palmas_wdt_init(void)
+{
+       return platform_driver_register(&palmas_wdt_driver);
+}
+module_init(palmas_wdt_init);
+
+static void __exit palmas_wdt_exit(void)
+{
+       platform_driver_unregister(&palmas_wdt_driver);
+}
+module_exit(palmas_wdt_exit);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:palmas-wdt");
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
index 20df02c1cc70190b04802493d5a25b802e0adcba..547eaaaeb89c58456303ee11a1eb8c25cb52dcb6 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -613,7 +613,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift)
                 * when the old and new regions overlap clear from new_end.
                 */
                free_pgd_range(&tlb, new_end, old_end, new_end,
-                       vma->vm_next ? vma->vm_next->vm_start : 0);
+                       vma->vm_next ? vma->vm_next->vm_start : USER_PGTABLES_CEILING);
        } else {
                /*
                 * otherwise, clean from old_start; this is done to not touch
@@ -622,7 +622,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift)
                 * for the others its just a little faster.
                 */
                free_pgd_range(&tlb, old_start, old_end, new_end,
-                       vma->vm_next ? vma->vm_next->vm_start : 0);
+                       vma->vm_next ? vma->vm_next->vm_start : USER_PGTABLES_CEILING);
        }
        tlb_finish_mmu(&tlb, new_end, old_end);
 
index 5cf680a98f9bcb7494a2c0a4bc557fca00b84296..f50a87d3523924aacb26a3d7afcdbd59aa5b5449 100644 (file)
@@ -7,6 +7,16 @@
 #include <linux/mm_types.h>
 #include <linux/bug.h>
 
+/*
+ * On almost all architectures and configurations, 0 can be used as the
+ * upper ceiling to free_pgtables(): on many architectures it has the same
+ * effect as using TASK_SIZE.  However, there is one configuration which
+ * must impose a more careful limit, to avoid freeing kernel pgtables.
+ */
+#ifndef USER_PGTABLES_CEILING
+#define USER_PGTABLES_CEILING  0UL
+#endif
+
 #ifndef __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS
 extern int ptep_set_access_flags(struct vm_area_struct *vma,
                                 unsigned long address, pte_t *ptep,
index 73a25005d88aa7cc76f7e8049b9697be18955c39..4cf658022ce8a760bc04a38e38d6617bc7c6e9dd 100644 (file)
@@ -28,6 +28,7 @@ struct ahci_platform_data {
        const struct ata_port_info *ata_port_info;
        unsigned int force_port_map;
        unsigned int mask_port_map;
+       void *priv;
 };
 
 #endif /* _AHCI_PLATFORM_H */
similarity index 99%
rename from arch/arm/include/asm/hardware/pl080.h
rename to include/linux/amba/pl080.h
index 4eea2107214b7374ec5af34bd5c5a5e9425e5a64..3e7b62fbefbd28cf7992089b0f6068a7b741e97d 100644 (file)
@@ -1,4 +1,4 @@
-/* arch/arm/include/asm/hardware/pl080.h
+/* include/linux/amba/pl080.h
  *
  * Copyright 2008 Openmoko, Inc.
  * Copyright 2008 Simtec Electronics
index 9a0e3fa3ca9539123f1a5494cb0007c17b4c9a26..6a299f416288a54c3e936c0ab144fb202408010d 100644 (file)
@@ -27,7 +27,7 @@
 #define   BCMA_CC_FLASHT_NONE          0x00000000      /* No flash */
 #define   BCMA_CC_FLASHT_STSER         0x00000100      /* ST serial flash */
 #define   BCMA_CC_FLASHT_ATSER         0x00000200      /* Atmel serial flash */
-#define   BCMA_CC_FLASHT_NFLASH                0x00000200      /* NAND flash */
+#define   BCMA_CC_FLASHT_NAND          0x00000300      /* NAND flash */
 #define          BCMA_CC_FLASHT_PARA           0x00000700      /* Parallel flash */
 #define  BCMA_CC_CAP_PLLT              0x00038000      /* PLL Type */
 #define   BCMA_PLLTYPE_NONE            0x00000000
index 0baf8a56b7947b7300e5bb2b1b0a8dd5ef188483..fb61f3fb4ddbb1c208b2557f7aace2c692c6632e 100644 (file)
@@ -28,6 +28,7 @@
 #define BCMA_MIPS_MIPS74K_GPIOEN       0x0048
 #define BCMA_MIPS_MIPS74K_CLKCTLST     0x01E0
 
+#define BCMA_MIPS_OOBSELINA74          0x004
 #define BCMA_MIPS_OOBSELOUTA30         0x100
 
 struct bcma_device;
@@ -36,19 +37,23 @@ struct bcma_drv_mips {
        struct bcma_device *core;
        u8 setup_done:1;
        u8 early_setup_done:1;
-       unsigned int assigned_irqs;
 };
 
 #ifdef CONFIG_BCMA_DRIVER_MIPS
 extern void bcma_core_mips_init(struct bcma_drv_mips *mcore);
 extern void bcma_core_mips_early_init(struct bcma_drv_mips *mcore);
+
+extern unsigned int bcma_core_irq(struct bcma_device *core);
 #else
 static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { }
 static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) { }
+
+static inline unsigned int bcma_core_irq(struct bcma_device *core)
+{
+       return 0;
+}
 #endif
 
 extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore);
 
-extern unsigned int bcma_core_mips_irq(struct bcma_device *dev);
-
 #endif /* LINUX_BCMA_DRIVER_MIPS_H_ */
index c48d98d27b770ee8f13a6ff26aa918aeb89d66be..424760f01b9d7b71354e94ba8ab8e62c57bd7c04 100644 (file)
@@ -179,6 +179,8 @@ struct pci_dev;
 #define BCMA_CORE_PCI_CFG_FUN_MASK             7       /* Function mask */
 #define BCMA_CORE_PCI_CFG_OFF_MASK             0xfff   /* Register mask */
 
+#define BCMA_CORE_PCI_CFG_DEVCTRL              0xd8
+
 /* PCIE Root Capability Register bits (Host mode only) */
 #define BCMA_CORE_PCI_RC_CRS_VISIBILITY                0x0001
 
index d3201e438d1640ff51339514a561fc2347427220..1a4c988587b1ff702b40014efb591d5fa1c3a195 100644 (file)
@@ -371,6 +371,26 @@ struct dma_slave_config {
        unsigned int slave_id;
 };
 
+/* struct dmaengine_chan_caps - expose capability of a channel
+ * Note: each channel can have same or different capabilities
+ *
+ * This primarily classifies capabilities into
+ * a) APIs/ops supported
+ * b) channel physical capabilities
+ *
+ * @cap_mask: api/ops capability (DMA_INTERRUPT and DMA_PRIVATE
+ *            are invalid api/ops and will never be set)
+ * @seg_nr: maximum number of SG segments supported on a SG/SLAVE
+ *         channel (0 for no maximum or not a SG/SLAVE channel)
+ * @seg_len: maximum length of SG segments supported on a SG/SLAVE
+ *          channel (0 for no maximum or not a SG/SLAVE channel)
+ */
+struct dmaengine_chan_caps {
+       dma_cap_mask_t cap_mask;
+       int seg_nr;
+       int seg_len;
+};
+
 static inline const char *dma_chan_name(struct dma_chan *chan)
 {
        return dev_name(&chan->dev->device);
@@ -534,6 +554,7 @@ struct dma_tx_state {
  *     struct with auxiliary transfer status information, otherwise the call
  *     will just return a simple status code
  * @device_issue_pending: push pending transactions to hardware
+ * @device_channel_caps: return the channel capabilities
  */
 struct dma_device {
 
@@ -602,13 +623,18 @@ struct dma_device {
                                            dma_cookie_t cookie,
                                            struct dma_tx_state *txstate);
        void (*device_issue_pending)(struct dma_chan *chan);
+       struct dmaengine_chan_caps *(*device_channel_caps)(
+               struct dma_chan *chan, enum dma_transfer_direction direction);
 };
 
 static inline int dmaengine_device_control(struct dma_chan *chan,
                                           enum dma_ctrl_cmd cmd,
                                           unsigned long arg)
 {
-       return chan->device->device_control(chan, cmd, arg);
+       if (chan->device->device_control)
+               return chan->device->device_control(chan, cmd, arg);
+       else
+               return -ENOSYS;
 }
 
 static inline int dmaengine_slave_config(struct dma_chan *chan,
@@ -618,6 +644,11 @@ static inline int dmaengine_slave_config(struct dma_chan *chan,
                        (unsigned long)config);
 }
 
+static inline bool is_slave_direction(enum dma_transfer_direction direction)
+{
+       return (direction == DMA_MEM_TO_DEV) || (direction == DMA_DEV_TO_MEM);
+}
+
 static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single(
        struct dma_chan *chan, dma_addr_t buf, size_t len,
        enum dma_transfer_direction dir, unsigned long flags)
@@ -660,6 +691,13 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(
                                                period_len, dir, flags, NULL);
 }
 
+static inline struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma(
+               struct dma_chan *chan, struct dma_interleaved_template *xt,
+               unsigned long flags)
+{
+       return chan->device->device_prep_interleaved_dma(chan, xt, flags);
+}
+
 static inline int dmaengine_terminate_all(struct dma_chan *chan)
 {
        return dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0);
@@ -849,20 +887,6 @@ static inline bool async_tx_test_ack(struct dma_async_tx_descriptor *tx)
        return (tx->flags & DMA_CTRL_ACK) == DMA_CTRL_ACK;
 }
 
-#define first_dma_cap(mask) __first_dma_cap(&(mask))
-static inline int __first_dma_cap(const dma_cap_mask_t *srcp)
-{
-       return min_t(int, DMA_TX_TYPE_END,
-               find_first_bit(srcp->bits, DMA_TX_TYPE_END));
-}
-
-#define next_dma_cap(n, mask) __next_dma_cap((n), &(mask))
-static inline int __next_dma_cap(int n, const dma_cap_mask_t *srcp)
-{
-       return min_t(int, DMA_TX_TYPE_END,
-               find_next_bit(srcp->bits, DMA_TX_TYPE_END, n+1));
-}
-
 #define dma_cap_set(tx, mask) __dma_cap_set((tx), &(mask))
 static inline void
 __dma_cap_set(enum dma_transaction_type tx_type, dma_cap_mask_t *dstp)
@@ -891,9 +915,7 @@ __dma_has_cap(enum dma_transaction_type tx_type, dma_cap_mask_t *srcp)
 }
 
 #define for_each_dma_cap_mask(cap, mask) \
-       for ((cap) = first_dma_cap(mask);       \
-               (cap) < DMA_TX_TYPE_END;        \
-               (cap) = next_dma_cap((cap), (mask)))
+       for_each_set_bit(cap, mask.bits, DMA_TX_TYPE_END)
 
 /**
  * dma_async_issue_pending - flush pending transactions to HW
@@ -907,8 +929,6 @@ static inline void dma_async_issue_pending(struct dma_chan *chan)
        chan->device->device_issue_pending(chan);
 }
 
-#define dma_async_memcpy_issue_pending(chan) dma_async_issue_pending(chan)
-
 /**
  * dma_async_is_tx_complete - poll for transaction completion
  * @chan: DMA channel
@@ -934,16 +954,13 @@ static inline enum dma_status dma_async_is_tx_complete(struct dma_chan *chan,
        return status;
 }
 
-#define dma_async_memcpy_complete(chan, cookie, last, used)\
-       dma_async_is_tx_complete(chan, cookie, last, used)
-
 /**
  * dma_async_is_complete - test a cookie against chan state
  * @cookie: transaction identifier to test status of
  * @last_complete: last know completed transaction
  * @last_used: last cookie value handed out
  *
- * dma_async_is_complete() is used in dma_async_memcpy_complete()
+ * dma_async_is_complete() is used in dma_async_is_tx_complete()
  * the test logic is separated for lightweight testing of multiple cookies
  */
 static inline enum dma_status dma_async_is_complete(dma_cookie_t cookie,
@@ -969,11 +986,29 @@ dma_set_tx_state(struct dma_tx_state *st, dma_cookie_t last, dma_cookie_t used,
        }
 }
 
+/**
+ * dma_get_channel_caps - flush pending transactions to HW
+ * @chan: target DMA channel
+ * @dir: direction of transfer
+ *
+ * Get the channel-specific capabilities. If the dmaengine
+ * driver does not implement per channel capbilities then
+ * NULL is returned.
+ */
+static inline struct dmaengine_chan_caps
+*dma_get_channel_caps(struct dma_chan *chan, enum dma_transfer_direction dir)
+{
+       if (chan->device->device_channel_caps)
+               return chan->device->device_channel_caps(chan, dir);
+       return NULL;
+}
+
 enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie);
 #ifdef CONFIG_DMA_ENGINE
 enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx);
 void dma_issue_pending_all(void);
 struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param);
+struct dma_chan *dma_request_slave_channel(struct device *dev, char *name);
 void dma_release_channel(struct dma_chan *chan);
 #else
 static inline enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
@@ -988,6 +1023,11 @@ static inline struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask,
 {
        return NULL;
 }
+static inline struct dma_chan *dma_request_slave_channel(struct device *dev,
+                                                        char *name)
+{
+       return NULL;
+}
 static inline void dma_release_channel(struct dma_chan *chan)
 {
 }
@@ -1001,6 +1041,22 @@ void dma_run_dependencies(struct dma_async_tx_descriptor *tx);
 struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type);
 struct dma_chan *net_dma_find_channel(void);
 #define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y)
+#define dma_request_slave_channel_compat(mask, x, y, dev, name) \
+       __dma_request_slave_channel_compat(&(mask), x, y, dev, name)
+
+static inline struct dma_chan
+*__dma_request_slave_channel_compat(dma_cap_mask_t *mask, dma_filter_fn fn,
+                                 void *fn_param, struct device *dev,
+                                 char *name)
+{
+       struct dma_chan *chan;
+
+       chan = dma_request_slave_channel(dev, name);
+       if (chan)
+               return chan;
+
+       return __dma_request_channel(mask, fn, fn_param);
+}
 
 /* --- Helper iov-locking functions --- */
 
index e1c8c9e919ac74401b566ccc08f9eeee67ca0a45..41766de66e3384016b9af12d8e5b81418db651fb 100644 (file)
 
 #include <linux/dmaengine.h>
 
+/**
+ * struct dw_dma_slave - Controller-specific information about a slave
+ *
+ * @dma_dev: required DMA master device. Depricated.
+ * @bus_id: name of this device channel, not just a device name since
+ *          devices may have more than one channel e.g. "foo_tx"
+ * @cfg_hi: Platform-specific initializer for the CFG_HI register
+ * @cfg_lo: Platform-specific initializer for the CFG_LO register
+ * @src_master: src master for transfers on allocated channel.
+ * @dst_master: dest master for transfers on allocated channel.
+ */
+struct dw_dma_slave {
+       struct device           *dma_dev;
+       const char              *bus_id;
+       u32                     cfg_hi;
+       u32                     cfg_lo;
+       u8                      src_master;
+       u8                      dst_master;
+};
+
 /**
  * struct dw_dma_platform_data - Controller configuration parameters
  * @nr_channels: Number of channels supported by hardware (max 8)
  * @is_private: The device channels should be marked as private and not for
  *     by the general purpose DMA channel allocator.
+ * @chan_allocation_order: Allocate channels starting from 0 or 7
+ * @chan_priority: Set channel priority increasing from 0 to 7 or 7 to 0.
  * @block_size: Maximum block size supported by the controller
  * @nr_masters: Number of AHB masters supported by the controller
  * @data_width: Maximum data width supported by hardware per AHB master
  *             (0 - 8bits, 1 - 16bits, ..., 5 - 256bits)
+ * @sd: slave specific data. Used for configuring channels
+ * @sd_count: count of slave data structures passed.
  */
 struct dw_dma_platform_data {
        unsigned int    nr_channels;
@@ -36,6 +60,9 @@ struct dw_dma_platform_data {
        unsigned short  block_size;
        unsigned char   nr_masters;
        unsigned char   data_width[4];
+
+       struct dw_dma_slave *sd;
+       unsigned int sd_count;
 };
 
 /* bursts size */
@@ -50,23 +77,6 @@ enum dw_dma_msize {
        DW_DMA_MSIZE_256,
 };
 
-/**
- * struct dw_dma_slave - Controller-specific information about a slave
- *
- * @dma_dev: required DMA master device
- * @cfg_hi: Platform-specific initializer for the CFG_HI register
- * @cfg_lo: Platform-specific initializer for the CFG_LO register
- * @src_master: src master for transfers on allocated channel.
- * @dst_master: dest master for transfers on allocated channel.
- */
-struct dw_dma_slave {
-       struct device           *dma_dev;
-       u32                     cfg_hi;
-       u32                     cfg_lo;
-       u8                      src_master;
-       u8                      dst_master;
-};
-
 /* Platform-configurable bits in CFG_HI */
 #define DWC_CFGH_FCMODE                (1 << 0)
 #define DWC_CFGH_FIFO_MODE     (1 << 1)
@@ -104,5 +114,6 @@ void dw_dma_cyclic_stop(struct dma_chan *chan);
 dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan);
 
 dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan);
+bool dw_dma_generic_filter(struct dma_chan *chan, void *param);
 
 #endif /* DW_DMAC_H */
index 1ff54b114efcb1971742cfcb584bfd662468ce49..488debbef8959caf079606e0e3876e843d157955 100644 (file)
  * address each module uses within a given i2c slave.
  */
 
+/* Module IDs for similar functionalities found in twl4030/twl6030 */
+enum twl_module_ids {
+       TWL_MODULE_USB,
+       TWL_MODULE_PIH,
+       TWL_MODULE_MAIN_CHARGE,
+       TWL_MODULE_PM_MASTER,
+       TWL_MODULE_PM_RECEIVER,
+
+       TWL_MODULE_RTC,
+       TWL_MODULE_PWM,
+       TWL_MODULE_LED,
+       TWL_MODULE_SECURED_REG,
+
+       TWL_MODULE_LAST,
+};
+
+/* Modules only available in twl4030 series */
 enum twl4030_module_ids {
-       TWL4030_MODULE_USB = 0,         /* Slave 0 (i2c address 0x48) */
-       TWL4030_MODULE_AUDIO_VOICE,     /* Slave 1 (i2c address 0x49) */
+       TWL4030_MODULE_AUDIO_VOICE = TWL_MODULE_LAST,
        TWL4030_MODULE_GPIO,
        TWL4030_MODULE_INTBR,
-       TWL4030_MODULE_PIH,
-
        TWL4030_MODULE_TEST,
-       TWL4030_MODULE_KEYPAD,          /* Slave 2 (i2c address 0x4a) */
+       TWL4030_MODULE_KEYPAD,
+
        TWL4030_MODULE_MADC,
        TWL4030_MODULE_INTERRUPTS,
-       TWL4030_MODULE_LED,
-
-       TWL4030_MODULE_MAIN_CHARGE,
        TWL4030_MODULE_PRECHARGE,
-       TWL4030_MODULE_PWM0,
-       TWL4030_MODULE_PWM1,
-       TWL4030_MODULE_PWMA,
+       TWL4030_MODULE_BACKUP,
+       TWL4030_MODULE_INT,
 
-       TWL4030_MODULE_PWMB,
        TWL5031_MODULE_ACCESSORY,
        TWL5031_MODULE_INTERRUPTS,
-       TWL4030_MODULE_BACKUP,          /* Slave 3 (i2c address 0x4b) */
-       TWL4030_MODULE_INT,
 
-       TWL4030_MODULE_PM_MASTER,
-       TWL4030_MODULE_PM_RECEIVER,
-       TWL4030_MODULE_RTC,
-       TWL4030_MODULE_SECURED_REG,
        TWL4030_MODULE_LAST,
 };
 
-/* Similar functionalities implemented in TWL4030/6030 */
-#define TWL_MODULE_USB         TWL4030_MODULE_USB
-#define TWL_MODULE_PIH         TWL4030_MODULE_PIH
-#define TWL_MODULE_MAIN_CHARGE TWL4030_MODULE_MAIN_CHARGE
-#define TWL_MODULE_PM_MASTER   TWL4030_MODULE_PM_MASTER
-#define TWL_MODULE_PM_RECEIVER TWL4030_MODULE_PM_RECEIVER
-#define TWL_MODULE_RTC         TWL4030_MODULE_RTC
-#define TWL_MODULE_PWM         TWL4030_MODULE_PWM0
-#define TWL_MODULE_LED         TWL4030_MODULE_LED
+/* Modules only available in twl6030 series */
+enum twl6030_module_ids {
+       TWL6030_MODULE_ID0 = TWL_MODULE_LAST,
+       TWL6030_MODULE_ID1,
+       TWL6030_MODULE_ID2,
+       TWL6030_MODULE_GPADC,
+       TWL6030_MODULE_GASGAUGE,
+
+       TWL6030_MODULE_LAST,
+};
 
-#define TWL6030_MODULE_ID0     13
-#define TWL6030_MODULE_ID1     14
-#define TWL6030_MODULE_ID2     15
+/* Until the clients has been converted to use TWL_MODULE_LED */
+#define TWL4030_MODULE_LED     TWL_MODULE_LED
 
 #define GPIO_INTR_OFFSET       0
 #define KEYPAD_INTR_OFFSET     1
@@ -170,21 +174,23 @@ static inline int twl_class_is_ ##class(void)     \
 TWL_CLASS_IS(4030, TWL4030_CLASS_ID)
 TWL_CLASS_IS(6030, TWL6030_CLASS_ID)
 
-/*
- * Read and write single 8-bit registers
- */
-int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg);
-int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg);
-
 /*
  * Read and write several 8-bit registers at once.
- *
- * IMPORTANT:  For twl_i2c_write(), allocate num_bytes + 1
- * for the value, and populate your data starting at offset 1.
  */
 int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
 int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
 
+/*
+ * Read and write single 8-bit registers
+ */
+static inline int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg) {
+       return twl_i2c_write(mod_no, &val, reg, 1);
+}
+
+static inline int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg) {
+       return twl_i2c_read(mod_no, val, reg, 1);
+}
+
 int twl_get_type(void);
 int twl_get_version(void);
 int twl_get_hfclk_rate(void);
index f0859cc73861720a982c185a3422fe66efbe5415..11c8bc87fdcbbedb9ef50b92c9e9188c7131f08b 100644 (file)
@@ -180,7 +180,7 @@ struct ieee80211_hdr {
        u8 addr3[6];
        __le16 seq_ctrl;
        u8 addr4[6];
-} __attribute__ ((packed));
+} __packed;
 
 struct ieee80211_hdr_3addr {
        __le16 frame_control;
@@ -189,7 +189,7 @@ struct ieee80211_hdr_3addr {
        u8 addr2[6];
        u8 addr3[6];
        __le16 seq_ctrl;
-} __attribute__ ((packed));
+} __packed;
 
 struct ieee80211_qos_hdr {
        __le16 frame_control;
@@ -199,7 +199,7 @@ struct ieee80211_qos_hdr {
        u8 addr3[6];
        __le16 seq_ctrl;
        __le16 qos_ctrl;
-} __attribute__ ((packed));
+} __packed;
 
 /**
  * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set
@@ -576,7 +576,7 @@ struct ieee80211s_hdr {
        __le32 seqnum;
        u8 eaddr1[6];
        u8 eaddr2[6];
-} __attribute__ ((packed));
+} __packed;
 
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4       0x1
@@ -614,7 +614,7 @@ struct ieee80211_quiet_ie {
        u8 period;
        __le16 duration;
        __le16 offset;
-} __attribute__ ((packed));
+} __packed;
 
 /**
  * struct ieee80211_msrment_ie
@@ -626,7 +626,7 @@ struct ieee80211_msrment_ie {
        u8 mode;
        u8 type;
        u8 request[0];
-} __attribute__ ((packed));
+} __packed;
 
 /**
  * struct ieee80211_channel_sw_ie
@@ -637,7 +637,7 @@ struct ieee80211_channel_sw_ie {
        u8 mode;
        u8 new_ch_num;
        u8 count;
-} __attribute__ ((packed));
+} __packed;
 
 /**
  * struct ieee80211_tim
@@ -650,7 +650,7 @@ struct ieee80211_tim_ie {
        u8 bitmap_ctrl;
        /* variable size: 1 - 251 bytes */
        u8 virtual_map[1];
-} __attribute__ ((packed));
+} __packed;
 
 /**
  * struct ieee80211_meshconf_ie
@@ -665,7 +665,7 @@ struct ieee80211_meshconf_ie {
        u8 meshconf_auth;
        u8 meshconf_form;
        u8 meshconf_cap;
-} __attribute__ ((packed));
+} __packed;
 
 /**
  * enum mesh_config_capab_flags - Mesh Configuration IE capability field flags
@@ -695,12 +695,17 @@ struct ieee80211_rann_ie {
        __le32 rann_seq;
        __le32 rann_interval;
        __le32 rann_metric;
-} __attribute__ ((packed));
+} __packed;
 
 enum ieee80211_rann_flags {
        RANN_FLAG_IS_GATE = 1 << 0,
 };
 
+enum ieee80211_ht_chanwidth_values {
+       IEEE80211_HT_CHANWIDTH_20MHZ = 0,
+       IEEE80211_HT_CHANWIDTH_ANY = 1,
+};
+
 #define WLAN_SA_QUERY_TR_ID_LEN 2
 
 struct ieee80211_mgmt {
@@ -717,33 +722,33 @@ struct ieee80211_mgmt {
                        __le16 status_code;
                        /* possibly followed by Challenge text */
                        u8 variable[0];
-               } __attribute__ ((packed)) auth;
+               } __packed auth;
                struct {
                        __le16 reason_code;
-               } __attribute__ ((packed)) deauth;
+               } __packed deauth;
                struct {
                        __le16 capab_info;
                        __le16 listen_interval;
                        /* followed by SSID and Supported rates */
                        u8 variable[0];
-               } __attribute__ ((packed)) assoc_req;
+               } __packed assoc_req;
                struct {
                        __le16 capab_info;
                        __le16 status_code;
                        __le16 aid;
                        /* followed by Supported rates */
                        u8 variable[0];
-               } __attribute__ ((packed)) assoc_resp, reassoc_resp;
+               } __packed assoc_resp, reassoc_resp;
                struct {
                        __le16 capab_info;
                        __le16 listen_interval;
                        u8 current_ap[6];
                        /* followed by SSID and Supported rates */
                        u8 variable[0];
-               } __attribute__ ((packed)) reassoc_req;
+               } __packed reassoc_req;
                struct {
                        __le16 reason_code;
-               } __attribute__ ((packed)) disassoc;
+               } __packed disassoc;
                struct {
                        __le64 timestamp;
                        __le16 beacon_int;
@@ -751,11 +756,11 @@ struct ieee80211_mgmt {
                        /* followed by some of SSID, Supported rates,
                         * FH Params, DS Params, CF Params, IBSS Params, TIM */
                        u8 variable[0];
-               } __attribute__ ((packed)) beacon;
+               } __packed beacon;
                struct {
                        /* only variable items: SSID, Supported rates */
                        u8 variable[0];
-               } __attribute__ ((packed)) probe_req;
+               } __packed probe_req;
                struct {
                        __le64 timestamp;
                        __le16 beacon_int;
@@ -763,7 +768,7 @@ struct ieee80211_mgmt {
                        /* followed by some of SSID, Supported rates,
                         * FH Params, DS Params, CF Params, IBSS Params */
                        u8 variable[0];
-               } __attribute__ ((packed)) probe_resp;
+               } __packed probe_resp;
                struct {
                        u8 category;
                        union {
@@ -772,55 +777,59 @@ struct ieee80211_mgmt {
                                        u8 dialog_token;
                                        u8 status_code;
                                        u8 variable[0];
-                               } __attribute__ ((packed)) wme_action;
+                               } __packed wme_action;
                                struct{
                                        u8 action_code;
                                        u8 element_id;
                                        u8 length;
                                        struct ieee80211_channel_sw_ie sw_elem;
-                               } __attribute__((packed)) chan_switch;
+                               } __packed chan_switch;
                                struct{
                                        u8 action_code;
                                        u8 dialog_token;
                                        u8 element_id;
                                        u8 length;
                                        struct ieee80211_msrment_ie msr_elem;
-                               } __attribute__((packed)) measurement;
+                               } __packed measurement;
                                struct{
                                        u8 action_code;
                                        u8 dialog_token;
                                        __le16 capab;
                                        __le16 timeout;
                                        __le16 start_seq_num;
-                               } __attribute__((packed)) addba_req;
+                               } __packed addba_req;
                                struct{
                                        u8 action_code;
                                        u8 dialog_token;
                                        __le16 status;
                                        __le16 capab;
                                        __le16 timeout;
-                               } __attribute__((packed)) addba_resp;
+                               } __packed addba_resp;
                                struct{
                                        u8 action_code;
                                        __le16 params;
                                        __le16 reason_code;
-                               } __attribute__((packed)) delba;
+                               } __packed delba;
                                struct {
                                        u8 action_code;
                                        u8 variable[0];
-                               } __attribute__((packed)) self_prot;
+                               } __packed self_prot;
                                struct{
                                        u8 action_code;
                                        u8 variable[0];
-                               } __attribute__((packed)) mesh_action;
+                               } __packed mesh_action;
                                struct {
                                        u8 action;
                                        u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
-                               } __attribute__ ((packed)) sa_query;
+                               } __packed sa_query;
                                struct {
                                        u8 action;
                                        u8 smps_control;
-                               } __attribute__ ((packed)) ht_smps;
+                               } __packed ht_smps;
+                               struct {
+                                       u8 action_code;
+                                       u8 chanwidth;
+                               } __packed ht_notify_cw;
                                struct {
                                        u8 action_code;
                                        u8 dialog_token;
@@ -828,9 +837,9 @@ struct ieee80211_mgmt {
                                        u8 variable[0];
                                } __packed tdls_discover_resp;
                        } u;
-               } __attribute__ ((packed)) action;
+               } __packed action;
        } u;
-} __attribute__ ((packed));
+} __packed;
 
 /* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */
 #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
@@ -846,7 +855,7 @@ struct ieee80211_mmie {
        __le16 key_id;
        u8 sequence_number[6];
        u8 mic[8];
-} __attribute__ ((packed));
+} __packed;
 
 struct ieee80211_vendor_ie {
        u8 element_id;
@@ -861,20 +870,20 @@ struct ieee80211_rts {
        __le16 duration;
        u8 ra[6];
        u8 ta[6];
-} __attribute__ ((packed));
+} __packed;
 
 struct ieee80211_cts {
        __le16 frame_control;
        __le16 duration;
        u8 ra[6];
-} __attribute__ ((packed));
+} __packed;
 
 struct ieee80211_pspoll {
        __le16 frame_control;
        __le16 aid;
        u8 bssid[6];
        u8 ta[6];
-} __attribute__ ((packed));
+} __packed;
 
 /* TDLS */
 
@@ -967,7 +976,7 @@ struct ieee80211_bar {
        __u8 ta[6];
        __le16 control;
        __le16 start_seq_num;
-} __attribute__((packed));
+} __packed;
 
 /* 802.11 BAR control masks */
 #define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL   0x0000
@@ -992,7 +1001,7 @@ struct ieee80211_mcs_info {
        __le16 rx_highest;
        u8 tx_params;
        u8 reserved[3];
-} __attribute__((packed));
+} __packed;
 
 /* 802.11n HT capability MSC set */
 #define IEEE80211_HT_MCS_RX_HIGHEST_MASK       0x3ff
@@ -1031,7 +1040,7 @@ struct ieee80211_ht_cap {
        __le16 extended_ht_cap_info;
        __le32 tx_BF_cap_info;
        u8 antenna_selection_info;
-} __attribute__ ((packed));
+} __packed;
 
 /* 802.11n HT capabilities masks (for cap_info) */
 #define IEEE80211_HT_CAP_LDPC_CODING           0x0001
@@ -1102,7 +1111,7 @@ struct ieee80211_ht_operation {
        __le16 operation_mode;
        __le16 stbc_param;
        u8 basic_set[16];
-} __attribute__ ((packed));
+} __packed;
 
 /* for ht_param */
 #define IEEE80211_HT_PARAM_CHA_SEC_OFFSET              0x03
@@ -1311,16 +1320,21 @@ struct ieee80211_vht_operation {
 #define WLAN_CAPABILITY_SPECTRUM_MGMT  (1<<8)
 #define WLAN_CAPABILITY_QOS            (1<<9)
 #define WLAN_CAPABILITY_SHORT_SLOT_TIME        (1<<10)
+#define WLAN_CAPABILITY_APSD           (1<<11)
+#define WLAN_CAPABILITY_RADIO_MEASURE  (1<<12)
 #define WLAN_CAPABILITY_DSSS_OFDM      (1<<13)
+#define WLAN_CAPABILITY_DEL_BACK       (1<<14)
+#define WLAN_CAPABILITY_IMM_BACK       (1<<15)
 
 /* DMG (60gHz) 802.11ad */
 /* type - bits 0..1 */
+#define WLAN_CAPABILITY_DMG_TYPE_MASK          (3<<0)
 #define WLAN_CAPABILITY_DMG_TYPE_IBSS          (1<<0) /* Tx by: STA */
 #define WLAN_CAPABILITY_DMG_TYPE_PBSS          (2<<0) /* Tx by: PCP */
 #define WLAN_CAPABILITY_DMG_TYPE_AP            (3<<0) /* Tx by: AP */
 
 #define WLAN_CAPABILITY_DMG_CBAP_ONLY          (1<<2)
-#define WLAN_CAPABILITY_DMG_CBAP_SOURCE        (1<<3)
+#define WLAN_CAPABILITY_DMG_CBAP_SOURCE                (1<<3)
 #define WLAN_CAPABILITY_DMG_PRIVACY            (1<<4)
 #define WLAN_CAPABILITY_DMG_ECPAC              (1<<5)
 
@@ -1834,14 +1848,14 @@ struct ieee80211_country_ie_triplet {
                        u8 first_channel;
                        u8 num_channels;
                        s8 max_power;
-               } __attribute__ ((packed)) chans;
+               } __packed chans;
                struct {
                        u8 reg_extension_id;
                        u8 reg_class;
                        u8 coverage_class;
-               } __attribute__ ((packed)) ext;
+               } __packed ext;
        };
-} __attribute__ ((packed));
+} __packed;
 
 enum ieee80211_timeout_interval_type {
        WLAN_TIMEOUT_REASSOC_DEADLINE = 1 /* 802.11r */,
@@ -1884,7 +1898,10 @@ enum ieee80211_sa_query_action {
 /* AKM suite selectors */
 #define WLAN_AKM_SUITE_8021X           0x000FAC01
 #define WLAN_AKM_SUITE_PSK             0x000FAC02
-#define WLAN_AKM_SUITE_SAE                     0x000FAC08
+#define WLAN_AKM_SUITE_8021X_SHA256    0x000FAC05
+#define WLAN_AKM_SUITE_PSK_SHA256      0x000FAC06
+#define WLAN_AKM_SUITE_TDLS            0x000FAC07
+#define WLAN_AKM_SUITE_SAE             0x000FAC08
 #define WLAN_AKM_SUITE_FT_OVER_SAE     0x000FAC09
 
 #define WLAN_MAX_KEY_LEN               32
diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h
new file mode 100644 (file)
index 0000000..4a020a1
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * mailbox: interprocessor communication module
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef MAILBOX_H
+#define MAILBOX_H
+
+struct mailbox;
+
+typedef int __bitwise mailbox_irq_t;
+#define IRQ_TX ((__force mailbox_irq_t) 1)
+#define IRQ_RX ((__force mailbox_irq_t) 2)
+
+struct mailbox_msg {
+       int             size;
+       u32             header;
+       void            *pdata;
+};
+
+#define MAILBOX_FILL_MSG(_msg, _header, _pdata, _size) { \
+       _msg.header = _header; \
+       _msg.pdata = (void *)_pdata; \
+       _msg.size = _size; \
+}
+
+int mailbox_msg_send(struct mailbox *, struct mailbox_msg *msg);
+struct mailbox_msg *mailbox_msg_send_receive_no_irq(struct mailbox *,
+               struct mailbox_msg *msg);
+int mailbox_msg_send_no_irq(struct mailbox *, struct mailbox_msg *msg);
+int mailbox_empty_tx(struct mailbox *mbox);
+
+struct mailbox *mailbox_get(const char *, struct notifier_block *nb);
+void mailbox_put(struct mailbox *mbox, struct notifier_block *nb);
+
+void mailbox_save_ctx(struct mailbox *mbox);
+void mailbox_restore_ctx(struct mailbox *mbox);
+void mailbox_enable_irq(struct mailbox *mbox, mailbox_irq_t irq);
+void mailbox_disable_irq(struct mailbox *mbox, mailbox_irq_t irq);
+
+#endif /* MAILBOX_H */
index 8b1d1daaae16c27e59d037f46899a1f6c631acc4..ec3e2a2a6d77a9a16c8916f63783dc2e0822036e 100644 (file)
@@ -62,6 +62,8 @@
 
 #define ARIZONA_MAX_OUTPUT 6
 
+#define ARIZONA_MAX_AIF 3
+
 #define ARIZONA_HAP_ACT_ERM 0
 #define ARIZONA_HAP_ACT_LRA 2
 
@@ -96,6 +98,13 @@ struct arizona_pdata {
        /** Pin state for GPIO pins */
        int gpio_defaults[ARIZONA_MAX_GPIO];
 
+       /**
+        * Maximum number of channels clocks will be generated for,
+        * useful for systems where and I2S bus with multiple data
+        * lines is mastered.
+        */
+       int max_channels_clocked[ARIZONA_MAX_AIF];
+
        /** GPIO for mic detection polarity */
        int micd_pol_gpio;
 
index 0ab61320ffa8e2a239019b6169a0b2c2c3807a1c..7dd6524d2aac9becefa0103c69b60bb241a94bdf 100644 (file)
@@ -26,8 +26,7 @@
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/core.h>
-
-#include <mach/edma.h>
+#include <linux/platform_data/edma.h>
 
 /*
  * Register values.
index 29f6616e12f07605379c6b21aa9bad1ff9152df3..d6c3bec376b1f80bfbe3cad8312280b44a6e8ac3 100644 (file)
@@ -19,6 +19,9 @@
 #include <linux/leds.h>
 #include <linux/regmap.h>
 #include <linux/regulator/driver.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/usb/dwc3-omap.h>
+#include <linux/platform_data/dwc3-omap.h>
 
 #define PALMAS_NUM_CLIENTS             3
 
@@ -35,6 +38,8 @@ struct palmas {
 
        /* Stored chip id */
        int id;
+       int designrev;
+       int sw_revision;
 
        /* IRQ Data */
        int irq;
@@ -341,6 +346,8 @@ struct palmas_usb {
        struct palmas *palmas;
        struct device *dev;
 
+       struct phy_companion comparator;
+
        /* for vbus reporting with irqs disabled */
        spinlock_t lock;
 
@@ -356,7 +363,7 @@ struct palmas_usb {
 
        int vbus_enable;
 
-       u8 linkstat;
+       enum omap_dwc3_vbus_id_status linkstat;
 };
 
 #define comparator_to_palmas(x) container_of((x), struct palmas_usb, comparator)
@@ -427,11 +434,13 @@ enum usb_irq_events {
 #define PALMAS_PU_PD_OD_BASE                                   0x1F4
 #define PALMAS_LED_BASE                                                0x200
 #define PALMAS_INTERRUPT_BASE                                  0x210
+#define PALMAS_ID_BASE                                         0x24F
 #define PALMAS_USB_OTG_BASE                                    0x250
 #define PALMAS_VIBRATOR_BASE                                   0x270
 #define PALMAS_GPIO_BASE                                       0x280
 #define PALMAS_USB_BASE                                                0x290
 #define PALMAS_GPADC_BASE                                      0x2C0
+#define PALMAS_DESIGNREV_BASE                                  0x357
 #define PALMAS_TRIM_GPADC_BASE                                 0x3CD
 
 /* Registers for function RTC */
@@ -2149,6 +2158,28 @@ enum usb_irq_events {
 #define PALMAS_INT_CTRL_INT_CLEAR                              0x01
 #define PALMAS_INT_CTRL_INT_CLEAR_SHIFT                                0
 
+/* Registers for function ID */
+#define PALMAS_VENDOR_ID_LSB                                   0x0
+#define PALMAS_VENDOR_ID_MSB                                   0x1
+#define PALMAS_PRODUCT_ID_LSB                                  0x2
+#define PALMAS_PRODUCT_ID_MSB                                  0x3
+
+/* Bit definitions for VENDOR_ID_LSB */
+#define PALMAS_VENDOR_ID_LSB_VENDOR_ID_MASK                    0xff
+#define PALMAS_VENDOR_ID_LSB_VENDOR_ID_SHIFT                   0
+
+/* Bit definitions for VENDOR_ID_MSB */
+#define PALMAS_VENDOR_ID_MSB_VENDOR_ID_MASK                    0xff
+#define PALMAS_VENDOR_ID_MSB_VENDOR_ID_SHIFT                   0
+
+/* Bit definitions for PRODUCT_ID_LSB */
+#define PALMAS_PRODUCT_ID_LSB_PRODUCT_ID_MASK                  0xff
+#define PALMAS_PRODUCT_ID_LSB_PRODUCT_ID_SHIFT                 0
+
+/* Bit definitions for PRODUCT_ID_MSB */
+#define PALMAS_PRODUCT_ID_MSB_PRODUCT_ID_MASK                  0xff
+#define PALMAS_PRODUCT_ID_MSB_PRODUCT_ID_SHIFT                 0
+
 /* Registers for function USB_OTG */
 #define PALMAS_USB_WAKEUP                                      0x3
 #define PALMAS_USB_VBUS_CTRL_SET                               0x4
@@ -2771,6 +2802,13 @@ enum usb_irq_events {
 #define PALMAS_GPADC_SMPS_VSEL_MONITORING_SMPS_VSEL_MONITORING_MASK    0x7f
 #define PALMAS_GPADC_SMPS_VSEL_MONITORING_SMPS_VSEL_MONITORING_SHIFT   0
 
+/* Registers for function DESIGNREV */
+#define PALMAS_DESIGNREV                                        0x0
+
+/* Bit definitions for DESIGNREV */
+#define PALMAS_DESIGNREV_DESIGNREV_MASK                         0x0f
+#define PALMAS_DESIGNREV_DESIGNREV_SHIFT                        0
+
 /* Registers for function GPADC */
 #define PALMAS_GPADC_TRIM1                                     0x0
 #define PALMAS_GPADC_TRIM2                                     0x1
diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h
new file mode 100644 (file)
index 0000000..d15073e
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * OF helpers for DMA request / controller
+ *
+ * Based on of_gpio.h
+ *
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_OF_DMA_H
+#define __LINUX_OF_DMA_H
+
+#include <linux/of.h>
+#include <linux/dmaengine.h>
+
+struct device_node;
+
+struct of_dma {
+       struct list_head        of_dma_controllers;
+       struct device_node      *of_node;
+       int                     of_dma_nbcells;
+       struct dma_chan         *(*of_dma_xlate)
+                               (struct of_phandle_args *, struct of_dma *);
+       void                    *of_dma_data;
+       int                     use_count;
+};
+
+struct of_dma_filter_info {
+       dma_cap_mask_t  dma_cap;
+       dma_filter_fn   filter_fn;
+};
+
+#ifdef CONFIG_OF
+extern int of_dma_controller_register(struct device_node *np,
+               struct dma_chan *(*of_dma_xlate)
+               (struct of_phandle_args *, struct of_dma *),
+               void *data);
+extern int of_dma_controller_free(struct device_node *np);
+extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
+                                                    char *name);
+extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec,
+               struct of_dma *ofdma);
+#else
+static inline int of_dma_controller_register(struct device_node *np,
+               struct dma_chan *(*of_dma_xlate)
+               (struct of_phandle_args *, struct of_dma *),
+               void *data)
+{
+       return -ENODEV;
+}
+
+static inline int of_dma_controller_free(struct device_node *np)
+{
+       return -ENODEV;
+}
+
+static inline struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
+                                                    char *name)
+{
+       return NULL;
+}
+
+static inline struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec,
+               struct of_dma *ofdma)
+{
+       return NULL;
+}
+
+#endif
+
+#endif /* __LINUX_OF_DMA_H */
index 24368a2e8b8713dcd9678dfa9045834b8d00f465..bb3cd58d71e3fbac50c9aed1afbf7ef0cbeaa108 100644 (file)
@@ -21,6 +21,8 @@ struct cpsw_slave_data {
        char            phy_id[MII_BUS_ID_SIZE];
        int             phy_if;
        u8              mac_addr[ETH_ALEN];
+       u16             dual_emac_res_vlan;     /* Reserved VLAN for DualEMAC */
+
 };
 
 struct cpsw_platform_data {
@@ -28,13 +30,15 @@ struct cpsw_platform_data {
        u32     channels;       /* number of cpdma channels (symmetric) */
        u32     slaves;         /* number of slave cpgmac ports */
        struct cpsw_slave_data  *slave_data;
-       u32     cpts_active_slave; /* time stamping slave */
+       u32     active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */
        u32     cpts_clock_mult;  /* convert input clock ticks to nanoseconds */
        u32     cpts_clock_shift; /* convert input clock ticks to nanoseconds */
        u32     ale_entries;    /* ale table size */
        u32     bd_ram_size;  /*buffer descriptor ram size */
        u32     rx_descs;       /* Number of Rx Descriptios */
        u32     mac_control;    /* Mac control register */
+       u16     default_vlan;   /* Def VLAN for ALE lookup in VLAN aware mode*/
+       bool    dual_emac;      /* Enable Dual EMAC mode */
 };
 
 #endif /* __CPSW_H__ */
index 9ff93b06568695f04822dd066f5c78a35beca3e7..4b781014b0a0c510e02b6bd3d365f68e766e5188 100644 (file)
@@ -147,6 +147,16 @@ struct stedma40_chan_cfg {
  * @memcpy_conf_log: default configuration of logical channel memcpy
  * @disabled_channels: A vector, ending with -1, that marks physical channels
  * that are for different reasons not available for the driver.
+ * @soft_lli_chans: A vector, that marks physical channels will use LLI by SW
+ * which avoids HW bug that exists in some versions of the controller.
+ * SoftLLI introduces relink overhead that could impact performace for
+ * certain use cases.
+ * @num_of_soft_lli_chans: The number of channels that needs to be configured
+ * to use SoftLLI.
+ * @use_esram_lcla: flag for mapping the lcla into esram region
+ * @num_of_phy_chans: The number of physical channels implemented in HW.
+ * 0 means reading the number of channels from DMA HW but this is only valid
+ * for 'multiple of 4' channels, like 8.
  */
 struct stedma40_platform_data {
        u32                              dev_len;
@@ -157,7 +167,10 @@ struct stedma40_platform_data {
        struct stedma40_chan_cfg        *memcpy_conf_phy;
        struct stedma40_chan_cfg        *memcpy_conf_log;
        int                              disabled_channels[STEDMA40_MAX_PHYS];
+       int                             *soft_lli_chans;
+       int                              num_of_soft_lli_chans;
        bool                             use_esram_lcla;
+       int                              num_of_phy_chans;
 };
 
 #ifdef CONFIG_STE_DMA40
index ada401244e0b636cff9fe9996312727d4e504022..1d36ca874cc86fb7b9ec82724277eee625a8478d 100644 (file)
@@ -41,7 +41,3 @@ enum dwc3_omap_utmi_mode {
        DWC3_OMAP_UTMI_MODE_HW,
        DWC3_OMAP_UTMI_MODE_SW,
 };
-
-struct dwc3_omap_data {
-       enum dwc3_omap_utmi_mode        utmi_mode;
-};
similarity index 59%
rename from arch/arm/mach-davinci/include/mach/edma.h
rename to include/linux/platform_data/edma.h
index 7e84c906ceff4046aef96e484d9bc0b5dd895711..ffc1fb214f406dbacaac718a7ba12095fe56a7fd 100644 (file)
@@ -1,28 +1,12 @@
 /*
- *  TI DAVINCI dma definitions
+ *  TI EDMA definitions
  *
- *  Copyright (C) 2006-2009 Texas Instruments.
+ *  Copyright (C) 2006-2013 Texas Instruments.
  *
  *  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  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
- *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
- *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
- *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
- *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
- *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
- *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *  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.
- *
  */
 
 /*
@@ -69,11 +53,6 @@ struct edmacc_param {
        unsigned int ccnt;
 };
 
-#define CCINT0_INTERRUPT     16
-#define CCERRINT_INTERRUPT   17
-#define TCERRINT0_INTERRUPT   18
-#define TCERRINT1_INTERRUPT   19
-
 /* fields in edmacc_param.opt */
 #define SAM            BIT(0)
 #define DAM            BIT(1)
@@ -87,70 +66,6 @@ struct edmacc_param {
 #define TCCHEN         BIT(22)
 #define ITCCHEN                BIT(23)
 
-#define TRWORD (0x7<<2)
-#define PAENTRY (0x1ff<<5)
-
-/* Drivers should avoid using these symbolic names for dm644x
- * channels, and use platform_device IORESOURCE_DMA resources
- * instead.  (Other DaVinci chips have different peripherals
- * and thus have different DMA channel mappings.)
- */
-#define DAVINCI_DMA_MCBSP_TX              2
-#define DAVINCI_DMA_MCBSP_RX              3
-#define DAVINCI_DMA_VPSS_HIST             4
-#define DAVINCI_DMA_VPSS_H3A              5
-#define DAVINCI_DMA_VPSS_PRVU             6
-#define DAVINCI_DMA_VPSS_RSZ              7
-#define DAVINCI_DMA_IMCOP_IMXINT          8
-#define DAVINCI_DMA_IMCOP_VLCDINT         9
-#define DAVINCI_DMA_IMCO_PASQINT         10
-#define DAVINCI_DMA_IMCOP_DSQINT         11
-#define DAVINCI_DMA_SPI_SPIX             16
-#define DAVINCI_DMA_SPI_SPIR             17
-#define DAVINCI_DMA_UART0_URXEVT0        18
-#define DAVINCI_DMA_UART0_UTXEVT0        19
-#define DAVINCI_DMA_UART1_URXEVT1        20
-#define DAVINCI_DMA_UART1_UTXEVT1        21
-#define DAVINCI_DMA_UART2_URXEVT2        22
-#define DAVINCI_DMA_UART2_UTXEVT2        23
-#define DAVINCI_DMA_MEMSTK_MSEVT         24
-#define DAVINCI_DMA_MMCRXEVT             26
-#define DAVINCI_DMA_MMCTXEVT             27
-#define DAVINCI_DMA_I2C_ICREVT           28
-#define DAVINCI_DMA_I2C_ICXEVT           29
-#define DAVINCI_DMA_GPIO_GPINT0          32
-#define DAVINCI_DMA_GPIO_GPINT1          33
-#define DAVINCI_DMA_GPIO_GPINT2          34
-#define DAVINCI_DMA_GPIO_GPINT3          35
-#define DAVINCI_DMA_GPIO_GPINT4          36
-#define DAVINCI_DMA_GPIO_GPINT5          37
-#define DAVINCI_DMA_GPIO_GPINT6          38
-#define DAVINCI_DMA_GPIO_GPINT7          39
-#define DAVINCI_DMA_GPIO_GPBNKINT0       40
-#define DAVINCI_DMA_GPIO_GPBNKINT1       41
-#define DAVINCI_DMA_GPIO_GPBNKINT2       42
-#define DAVINCI_DMA_GPIO_GPBNKINT3       43
-#define DAVINCI_DMA_GPIO_GPBNKINT4       44
-#define DAVINCI_DMA_TIMER0_TINT0         48
-#define DAVINCI_DMA_TIMER1_TINT1         49
-#define DAVINCI_DMA_TIMER2_TINT2         50
-#define DAVINCI_DMA_TIMER3_TINT3         51
-#define DAVINCI_DMA_PWM0                 52
-#define DAVINCI_DMA_PWM1                 53
-#define DAVINCI_DMA_PWM2                 54
-
-/* DA830 specific EDMA3 information */
-#define EDMA_DA830_NUM_DMACH           32
-#define EDMA_DA830_NUM_TCC             32
-#define EDMA_DA830_NUM_PARAMENTRY      128
-#define EDMA_DA830_NUM_EVQUE           2
-#define EDMA_DA830_NUM_TC              2
-#define EDMA_DA830_CHMAP_EXIST         0
-#define EDMA_DA830_NUM_REGIONS         4
-#define DA830_DMACH2EVENT_MAP0         0x000FC03Fu
-#define DA830_DMACH2EVENT_MAP1         0x00000000u
-#define DA830_EDMA_ARM_OWN             0x30FFCCFFu
-
 /*ch_status paramater of callback function possible values*/
 #define DMA_COMPLETE 1
 #define DMA_CC_ERROR 2
@@ -262,6 +177,7 @@ struct edma_soc_info {
 
        const s8        (*queue_tc_mapping)[2];
        const s8        (*queue_priority_mapping)[2];
+       const s16       (*xbar_chans)[2];
 };
 
 #endif
index 03378ca840619575655f7196acb47d68a6ac9bd6..5c19a2a647c4f5a92f28c3dadedcba3920034555 100644 (file)
@@ -40,6 +40,7 @@
 /* Custom config requests */
 #define EMIF_CUSTOM_CONFIG_LPMODE                      0x00000001
 #define EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL    0x00000002
+#define EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART          0x00000004
 
 #ifndef __ASSEMBLY__
 /**
index a7bdb2f63b73599119de3b975b17ed3979df306c..da7e6274b175c7befc37f1d0b54b78598dec9c12 100644 (file)
@@ -53,6 +53,8 @@ struct freq_clip_table {
  * struct exynos_tmu_platform_data
  * @threshold: basic temperature for generating interrupt
  *            25 <= threshold <= 125 [unit: degree Celsius]
+ * @threshold_falling: differntial value for setting threshold
+ *                    of temperature falling interrupt.
  * @trigger_levels: array for each interrupt levels
  *     [unit: degree Celsius]
  *     0: temperature for trigger_level0 interrupt
@@ -97,6 +99,7 @@ struct freq_clip_table {
  */
 struct exynos_tmu_platform_data {
        u8 threshold;
+       u8 threshold_falling;
        u8 trigger_levels[4];
        bool trigger_level0_en;
        bool trigger_level1_en;
index 5d50b25a73d71374b523ba68b6614e9deb3268a4..ebc8b33b9a71cdb96288c63a35f340a2d282c187 100644 (file)
 #define OMAP4_GPIO_CLEARDATAOUT                0x0190
 #define OMAP4_GPIO_SETDATAOUT          0x0194
 
+#if defined(CONFIG_ARCH_OMAP1)
 #define OMAP_MAX_GPIO_LINES            192
+#elif defined(CONFIG_ARCH_OMAP2PLUS)
+#define OMAP_MAX_GPIO_LINES            256
+#endif
 
 #define OMAP_MPUIO(nr)         (OMAP_MAX_GPIO_LINES + (nr))
 #define OMAP_GPIO_IS_MPUIO(nr) ((nr) >= OMAP_MAX_GPIO_LINES)
diff --git a/include/linux/platform_data/mailbox-dbx500.h b/include/linux/platform_data/mailbox-dbx500.h
new file mode 100644 (file)
index 0000000..d5aebd9
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * mailbox-dbx500.h
+ *
+ * Copyright (C) ST-Ericsson SA 2012
+ * Author:  <loic.pallardy@st.com> for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+struct dbx500_plat_data {
+       unsigned int legacy_offset;
+       unsigned int upap_offset;
+};
diff --git a/include/linux/platform_data/mailbox-omap.h b/include/linux/platform_data/mailbox-omap.h
new file mode 100644 (file)
index 0000000..676cd64
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * mailbox-omap.h
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _PLAT_MAILBOX_H
+#define _PLAT_MAILBOX_H
+
+/* Interrupt register configuration types */
+#define MBOX_INTR_CFG_TYPE1    (0)
+#define MBOX_INTR_CFG_TYPE2    (1)
+
+/**
+ * struct omap_mbox_dev_info - OMAP mailbox device attribute info
+ * @name:      name of the mailbox device
+ * @tx_id:     mailbox queue id used for transmitting messages
+ * @rx_id:     mailbox queue id on which messages are received
+ * @irq_id:    irq identifier number to use from the hwmod data
+ * @usr_id:    mailbox user id for identifying the interrupt into
+ *                     the MPU interrupt controller.
+ */
+struct omap_mbox_dev_info {
+       const char *name;
+       u32 tx_id;
+       u32 rx_id;
+       u32 irq_id;
+       u32 usr_id;
+};
+
+/**
+ * struct omap_mbox_pdata - OMAP mailbox platform data
+ * @intr_type: type of interrupt configuration registers used
+                       while programming mailbox queue interrupts
+ * @info_cnt:  number of mailbox devices for the platform
+ * @info:      array of mailbox device attributes
+ */
+struct omap_mbox_pdata {
+       u32 intr_type;
+       u32 info_cnt;
+       struct omap_mbox_dev_info *info;
+};
+
+#endif /* _PLAT_MAILBOX_H */
index 5ba6b22ce338e791188b053301e6e768b1556294..69102094093cd283ccbd4cc21e699a8677c2d3c4 100644 (file)
@@ -25,9 +25,6 @@ struct davinci_mmc_config {
 
        /* Version of the MMC/SD controller */
        u8      version;
-
-       /* Number of sg segments */
-       u8      nr_sg;
 };
 void davinci_setup_mmc(int module, struct davinci_mmc_config *config);
 
index 5d298ac10fc2358cbe2774a521f6de0c109f1ec5..8808b7f57b22f4a42633c830ebd357ff194b993b 100644 (file)
@@ -36,6 +36,7 @@ struct omap_abe_twl6040_data {
        bool    has_ep;
        u8      has_aux;
        u8      has_vibra;
+       bool    has_abe;
        bool    has_dmic;
        bool    has_hsmic;
        bool    has_mainmic;
index ff9b0aab5281c2251e93f5a6f56ea09cf2e31289..c860c1b314c0473a7737cd6c7dacd65344fbc656 100644 (file)
@@ -43,8 +43,6 @@ struct omap_uart_port_info {
        int                     DTR_present;
 
        int (*get_context_loss_count)(struct device *);
-       void (*set_forceidle)(struct device *);
-       void (*set_noidle)(struct device *);
        void (*enable_wakeup)(struct device *, bool);
 };
 
index 7af305b3786886ff5059fdccac2a45a92bc42fa4..8dc2fa47a2aa3c095f66f234d2975c5e18e10f53 100644 (file)
@@ -19,7 +19,7 @@
 #ifndef __ARCH_ARM_DAVINCI_SPI_H
 #define __ARCH_ARM_DAVINCI_SPI_H
 
-#include <mach/edma.h>
+#include <linux/platform_data/edma.h>
 
 #define SPI_INTERN_CS  0xFF
 
index ef65b67c56c31bee39e8b4b4f504f5f0797613a4..c82678169ebc6f6d7d194c7a1262dd103d7e273f 100644 (file)
@@ -55,13 +55,15 @@ struct ohci_hcd_omap_platform_data {
 };
 
 struct usbhs_omap_platform_data {
-       enum usbhs_omap_port_mode               port_mode[OMAP3_HS_USB_PORTS];
+       unsigned                        nports;
+       enum usbhs_omap_port_mode       port_mode[OMAP3_HS_USB_PORTS];
 
        struct ehci_hcd_omap_platform_data      *ehci_data;
        struct ohci_hcd_omap_platform_data      *ohci_data;
 
        /* OMAP3 <= ES2.1 have a single ulpi bypass control bit */
-       unsigned                                single_ulpi_bypass:1;
+       unsigned single_ulpi_bypass:1;
+       unsigned es2_compatibility:1;
 };
 
 /*-------------------------------------------------------------------------*/
index 07a9c7a2e088e5baedd31426a1679dab23def26c..afe79d40a99eaf36dd4eea67c5a0234a40eff683 100644 (file)
@@ -45,6 +45,11 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore)
 {
 }
 
+static inline unsigned int ssb_mips_irq(struct ssb_device *dev)
+{
+       return 0;
+}
+
 #endif /* CONFIG_SSB_DRIVER_MIPS */
 
 #endif /* LINUX_SSB_MIPSCORE_H_ */
index fe82022478e7f7ad20affd6c5ce4706c69205375..f0bd7f90a90d45d3aeeb3aed8bc2d2f8295c8be0 100644 (file)
@@ -74,6 +74,8 @@ enum thermal_trend {
        THERMAL_TREND_STABLE, /* temperature is stable */
        THERMAL_TREND_RAISING, /* temperature is raising */
        THERMAL_TREND_DROPPING, /* temperature is dropping */
+       THERMAL_TREND_RAISE_FULL, /* apply highest cooling action */
+       THERMAL_TREND_DROP_FULL, /* apply lowest cooling action */
 };
 
 /* Events supported by Thermal Netlink */
@@ -121,6 +123,7 @@ struct thermal_zone_device_ops {
        int (*set_trip_hyst) (struct thermal_zone_device *, int,
                              unsigned long);
        int (*get_crit_temp) (struct thermal_zone_device *, unsigned long *);
+       int (*set_emul_temp) (struct thermal_zone_device *, unsigned long);
        int (*get_trend) (struct thermal_zone_device *, int,
                          enum thermal_trend *);
        int (*notify) (struct thermal_zone_device *, int,
@@ -163,6 +166,7 @@ struct thermal_zone_device {
        int polling_delay;
        int temperature;
        int last_temperature;
+       int emul_temperature;
        int passive;
        unsigned int forced_passive;
        const struct thermal_zone_device_ops *ops;
@@ -244,9 +248,11 @@ int thermal_register_governor(struct thermal_governor *);
 void thermal_unregister_governor(struct thermal_governor *);
 
 #ifdef CONFIG_NET
-extern int thermal_generate_netlink_event(u32 orig, enum events event);
+extern int thermal_generate_netlink_event(struct thermal_zone_device *tz,
+                                               enum events event);
 #else
-static inline int thermal_generate_netlink_event(u32 orig, enum events event)
+static int thermal_generate_netlink_event(struct thermal_zone_device *tz,
+                                               enum events event)
 {
        return 0;
 }
index 4d22d0f6167aa49b653c8d942c2e00d61be689b8..632c0ce376f37c632c06a1834e93f220329d04ba 100644 (file)
@@ -1391,6 +1391,7 @@ struct urb {
        struct list_head urb_list;      /* list head for use by the urb's
                                         * current owner */
        struct list_head anchor_list;   /* the URB may be anchored */
+       struct list_head giveback_list; /* to postpone the giveback call */
        struct usb_anchor *anchor;
        struct usb_device *dev;         /* (in) pointer to associated device */
        struct usb_host_endpoint *ep;   /* (internal) pointer to endpoint */
diff --git a/include/linux/usb/dwc3-omap.h b/include/linux/usb/dwc3-omap.h
new file mode 100644 (file)
index 0000000..5615f4d
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 by Texas Instruments
+ *
+ * The Inventra Controller Driver for Linux 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 __DWC3_OMAP_H__
+#define __DWC3_OMAP_H__
+
+enum omap_dwc3_vbus_id_status {
+       OMAP_DWC3_UNKNOWN = 0,
+       OMAP_DWC3_ID_GROUND,
+       OMAP_DWC3_ID_FLOAT,
+       OMAP_DWC3_VBUS_VALID,
+       OMAP_DWC3_VBUS_OFF,
+};
+
+#if (defined(CONFIG_USB_DWC3) || defined(CONFIG_USB_DWC3_MODULE))
+extern int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status);
+#else
+static inline int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
+{
+       return -ENODEV;
+}
+#endif
+
+#endif /* __DWC3_OMAP_H__ */
index eb505250940af073cd1059f82c4e48d773df3acb..053c26841cc39bde245acac370e152189f256814 100644 (file)
@@ -99,6 +99,8 @@ 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 28884c7174112d7ede78d0283d9cba35802eddaa..148d35171aac622ab215bb6520f3dc3f0c0b431f 100644 (file)
@@ -5,6 +5,11 @@
 
 struct nop_usb_xceiv_platform_data {
        enum usb_phy_type type;
+       unsigned long clk_rate;
+
+       /* if set fails with -EPROBE_DEFER if can't get regulator */
+       unsigned int needs_vcc:1;
+       unsigned int needs_reset:1;
 };
 
 #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE))
diff --git a/include/linux/usb/omap_control_usb.h b/include/linux/usb/omap_control_usb.h
new file mode 100644 (file)
index 0000000..27b5b8c
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * omap_control_usb.h - Header file for the USB part of control module.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ *
+ */
+
+#ifndef __OMAP_CONTROL_USB_H__
+#define __OMAP_CONTROL_USB_H__
+
+struct omap_control_usb {
+       struct device *dev;
+
+       u32 __iomem *dev_conf;
+       u32 __iomem *otghs_control;
+       u32 __iomem *phy_power;
+
+       struct clk *sys_clk;
+
+       u32 type;
+};
+
+struct omap_control_usb_platform_data {
+       u8 type;
+};
+
+enum omap_control_usb_mode {
+       USB_MODE_UNDEFINED = 0,
+       USB_MODE_HOST,
+       USB_MODE_DEVICE,
+       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)
+#define        OMAP_CTRL_DEV_BVALID            BIT(1)
+#define        OMAP_CTRL_DEV_VBUSVALID         BIT(2)
+#define        OMAP_CTRL_DEV_SESSEND           BIT(3)
+#define        OMAP_CTRL_DEV_IDDIG             BIT(4)
+
+#define        OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK       0x003FC000
+#define        OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT      0xE
+
+#define        OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK      0xFFC00000
+#define        OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT     0x16
+
+#define        OMAP_CTRL_USB3_PHY_TX_RX_POWERON        0x3
+#define        OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF       0x0
+
+#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)
+{
+}
+#endif
+
+#endif /* __OMAP_CONTROL_USB_H__ */
index 0ea17f8ae820a4fcb93061a31905bf9486e67d36..6ae29360e1d2a1be611a6ec188af9addc68cabd5 100644 (file)
 #ifndef __DRIVERS_OMAP_USB2_H
 #define __DRIVERS_OMAP_USB2_H
 
+#include <linux/io.h>
 #include <linux/usb/otg.h>
 
+struct usb_dpll_params {
+       u16     m;
+       u8      n;
+       u8      freq:3;
+       u8      sd;
+       u32     mf;
+};
+
 struct omap_usb {
        struct usb_phy          phy;
        struct phy_companion    *comparator;
+       void __iomem            *pll_ctrl_base;
        struct device           *dev;
-       u32 __iomem             *control_dev;
+       struct device           *control_dev;
        struct clk              *wkupclk;
+       struct clk              *sys_clk;
+       struct clk              *optclk;
        u8                      is_suspended:1;
 };
 
-#define        PHY_PD  0x1
-
 #define        phy_to_omapusb(x)       container_of((x), struct omap_usb, phy)
 
 #if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE)
@@ -43,4 +53,15 @@ static inline int omap_usb2_set_comparator(struct phy_companion *comparator)
 }
 #endif
 
+static inline u32 omap_usb_readl(void __iomem *addr, unsigned offset)
+{
+       return __raw_readl(addr + offset);
+}
+
+static inline void omap_usb_writel(void __iomem *addr, unsigned offset,
+       u32 data)
+{
+       __raw_writel(data, addr + offset);
+}
+
 #endif /* __DRIVERS_OMAP_USB_H */
index a29ae1eb9346c96e13abe34f41fde83137fd5f4f..15847cbdb512b804347b3219d60c7b96563fd9bd 100644 (file)
@@ -106,9 +106,25 @@ struct usb_phy {
                        enum usb_device_speed speed);
 };
 
+/**
+ * struct usb_phy_bind - represent the binding for the phy
+ * @dev_name: the device name of the device that will bind to the phy
+ * @phy_dev_name: the device name of the phy
+ * @index: used if a single controller uses multiple phys
+ * @phy: reference to the phy
+ * @list: to maintain a linked list of the binding information
+ */
+struct usb_phy_bind {
+       const char      *dev_name;
+       const char      *phy_dev_name;
+       u8              index;
+       struct usb_phy  *phy;
+       struct list_head list;
+};
 
 /* for board-specific init logic */
 extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type);
+extern int usb_add_phy_dev(struct usb_phy *);
 extern void usb_remove_phy(struct usb_phy *);
 
 /* helpers for direct access thru low-level io interface */
@@ -149,8 +165,14 @@ usb_phy_shutdown(struct usb_phy *x)
 extern struct usb_phy *usb_get_phy(enum usb_phy_type type);
 extern struct usb_phy *devm_usb_get_phy(struct device *dev,
        enum usb_phy_type type);
+extern struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index);
+extern struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index);
+extern struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
+       const char *phandle, u8 index);
 extern void usb_put_phy(struct usb_phy *);
 extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x);
+extern int usb_bind_phy(const char *dev_name, u8 index,
+                               const char *phy_dev_name);
 #else
 static inline struct usb_phy *usb_get_phy(enum usb_phy_type type)
 {
@@ -163,6 +185,22 @@ static inline struct usb_phy *devm_usb_get_phy(struct device *dev,
        return NULL;
 }
 
+static inline struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index)
+{
+       return NULL;
+}
+
+static inline struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index)
+{
+       return NULL;
+}
+
+static inline struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
+       const char *phandle, u8 index)
+{
+       return NULL;
+}
+
 static inline void usb_put_phy(struct usb_phy *x)
 {
 }
@@ -171,6 +209,11 @@ static inline void devm_usb_put_phy(struct device *dev, struct usb_phy *x)
 {
 }
 
+static inline int usb_bind_phy(const char *dev_name, u8 index,
+                               const char *phy_dev_name)
+{
+       return -EOPNOTSUPP;
+}
 #endif
 
 static inline int
index 0d6373195d32c115a1d0f86b2a8ebe43e9ba7f46..a54fe82e704bc09c5acd6a5f1664a6dfbc982b64 100644 (file)
@@ -24,6 +24,8 @@
 #ifndef _LINUX_WL12XX_H
 #define _LINUX_WL12XX_H
 
+#include <linux/err.h>
+
 /* Reference clock values */
 enum {
        WL12XX_REFCLOCK_19      = 0, /* 19.2 MHz */
@@ -55,17 +57,17 @@ struct wl12xx_platform_data {
        int board_tcxo_clock;
        unsigned long platform_quirks;
        bool pwr_in_suspend;
-
-       struct wl1271_if_operations *ops;
 };
 
 /* Platform does not support level trigger interrupts */
 #define WL12XX_PLATFORM_QUIRK_EDGE_IRQ BIT(0)
 
-#ifdef CONFIG_WL12XX_PLATFORM_DATA
+#ifdef CONFIG_WILINK_PLATFORM_DATA
 
 int wl12xx_set_platform_data(const struct wl12xx_platform_data *data);
 
+struct wl12xx_platform_data *wl12xx_get_platform_data(void);
+
 #else
 
 static inline
@@ -74,8 +76,12 @@ int wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
        return -ENOSYS;
 }
 
-#endif
+static inline
+struct wl12xx_platform_data *wl12xx_get_platform_data(void)
+{
+       return ERR_PTR(-ENODATA);
+}
 
-struct wl12xx_platform_data *wl12xx_get_platform_data(void);
+#endif
 
 #endif
index 42f21766c538b4bfeab6d140aaaa0fcada8e1f16..487b54c1308fdec26ddc48e46b4973200b45022a 100644 (file)
@@ -23,6 +23,7 @@ enum amp_mgr_state {
        READ_LOC_AMP_INFO,
        READ_LOC_AMP_ASSOC,
        READ_LOC_AMP_ASSOC_FINAL,
+       WRITE_REMOTE_AMP_ASSOC,
 };
 
 struct amp_mgr {
@@ -33,7 +34,7 @@ struct amp_mgr {
        struct kref             kref;
        __u8                    ident;
        __u8                    handle;
-       enum amp_mgr_state      state;
+       unsigned long           state;
        unsigned long           flags;
 
        struct list_head        amp_ctrls;
@@ -144,5 +145,6 @@ void a2mp_discover_amp(struct l2cap_chan *chan);
 void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
 void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
 void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
+void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status);
 
 #endif /* __A2MP_H */
index 2554b3f5222ae0fa32b7f6fce650fb65327a65db..9531beee09b55342fec2266e5d9c9bba41248c2d 100644 (file)
@@ -166,6 +166,29 @@ typedef struct {
 #define BDADDR_LE_PUBLIC       0x01
 #define BDADDR_LE_RANDOM       0x02
 
+static inline bool bdaddr_type_is_valid(__u8 type)
+{
+       switch (type) {
+       case BDADDR_BREDR:
+       case BDADDR_LE_PUBLIC:
+       case BDADDR_LE_RANDOM:
+               return true;
+       }
+
+       return false;
+}
+
+static inline bool bdaddr_type_is_le(__u8 type)
+{
+       switch (type) {
+       case BDADDR_LE_PUBLIC:
+       case BDADDR_LE_RANDOM:
+               return true;
+       }
+
+       return false;
+}
+
 #define BDADDR_ANY   (&(bdaddr_t) {{0, 0, 0, 0, 0, 0} })
 #define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff} })
 
index 45eee08157bb926b316519da7cbba8cae49d2e71..7f12c25f1fcaa763a697d80ebf95a06124c3adc8 100644 (file)
@@ -943,6 +943,12 @@ struct hci_rp_le_read_buffer_size {
        __u8     le_max_pkt;
 } __packed;
 
+#define HCI_OP_LE_READ_LOCAL_FEATURES  0x2003
+struct hci_rp_le_read_local_features {
+       __u8     status;
+       __u8     features[8];
+} __packed;
+
 #define HCI_OP_LE_READ_ADV_TX_POWER    0x2007
 struct hci_rp_le_read_adv_tx_power {
        __u8    status;
@@ -995,6 +1001,12 @@ struct hci_cp_le_create_conn {
 
 #define HCI_OP_LE_CREATE_CONN_CANCEL   0x200e
 
+#define HCI_OP_LE_READ_WHITE_LIST_SIZE 0x200f
+struct hci_rp_le_read_white_list_size {
+       __u8    status;
+       __u8    size;
+} __packed;
+
 #define HCI_OP_LE_CONN_UPDATE          0x2013
 struct hci_cp_le_conn_update {
        __le16   handle;
@@ -1033,6 +1045,12 @@ struct hci_rp_le_ltk_neg_reply {
        __le16  handle;
 } __packed;
 
+#define HCI_OP_LE_READ_SUPPORTED_STATES        0x201c
+struct hci_rp_le_read_supported_states {
+       __u8    status;
+       __u8    le_states[8];
+} __packed;
+
 /* ---- HCI Events ---- */
 #define HCI_EV_INQUIRY_COMPLETE                0x01
 
index 014a2eaa53899d40e1d1eb91f54ba3870e09e9f4..90cf75afcb02987165ed4639bcdb169127509d31 100644 (file)
@@ -86,6 +86,7 @@ struct bdaddr_list {
 struct bt_uuid {
        struct list_head list;
        u8 uuid[16];
+       u8 size;
        u8 svc_hint;
 };
 
@@ -152,6 +153,9 @@ struct hci_dev {
        __u8            minor_class;
        __u8            features[8];
        __u8            host_features[8];
+       __u8            le_features[8];
+       __u8            le_white_list_size;
+       __u8            le_states[8];
        __u8            commands[64];
        __u8            hci_ver;
        __u16           hci_rev;
@@ -216,6 +220,7 @@ struct hci_dev {
        unsigned long   le_last_tx;
 
        struct workqueue_struct *workqueue;
+       struct workqueue_struct *req_workqueue;
 
        struct work_struct      power_on;
        struct delayed_work     power_off;
index 7588ef44ebaf22e7cc76a55a40a77166325791d0..cdd33021f831e8ce2eb7dff17992c2f40685aff6 100644 (file)
@@ -496,7 +496,6 @@ struct l2cap_chan {
        __u16           frames_sent;
        __u16           unacked_frames;
        __u8            retry_count;
-       __u16           srej_queue_next;
        __u16           sdu_len;
        struct sk_buff  *sdu;
        struct sk_buff  *sdu_last_frag;
index 8e6a6b73b9c9ecc3f8f221c4ff32a9983940dde4..36e076e374d22b3d3f4fd82495fd49ad591e9207 100644 (file)
@@ -281,9 +281,13 @@ struct ieee80211_supported_band {
 /**
  * struct vif_params - describes virtual interface parameters
  * @use_4addr: use 4-address frames
+ * @macaddr: address to use for this virtual interface. This will only
+ *     be used for non-netdevice interfaces. If this parameter is set
+ *     to zero address the driver may determine the address as needed.
  */
 struct vif_params {
        int use_4addr;
+       u8 macaddr[ETH_ALEN];
 };
 
 /**
@@ -326,7 +330,7 @@ struct cfg80211_chan_def {
  * cfg80211_get_chandef_type - return old channel type from chandef
  * @chandef: the channel definition
  *
- * Returns the old channel type (NOHT, HT20, HT40+/-) from a given
+ * Return: The old channel type (NOHT, HT20, HT40+/-) from a given
  * chandef, which must have a bandwidth allowing this conversion.
  */
 static inline enum nl80211_channel_type
@@ -364,7 +368,7 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
  * @chandef1: first channel definition
  * @chandef2: second channel definition
  *
- * Returns %true if the channels defined by the channel definitions are
+ * Return: %true if the channels defined by the channel definitions are
  * identical, %false otherwise.
  */
 static inline bool
@@ -382,7 +386,7 @@ cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1,
  * @chandef1: first channel definition
  * @chandef2: second channel definition
  *
- * Returns %NULL if the given channel definitions are incompatible,
+ * Return: %NULL if the given channel definitions are incompatible,
  * chandef1 or chandef2 otherwise.
  */
 const struct cfg80211_chan_def *
@@ -392,6 +396,7 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1,
 /**
  * cfg80211_chandef_valid - check if a channel definition is valid
  * @chandef: the channel definition to check
+ * Return: %true if the channel definition is valid. %false otherwise.
  */
 bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef);
 
@@ -399,7 +404,8 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef);
  * cfg80211_chandef_usable - check if secondary channels can be used
  * @wiphy: the wiphy to validate against
  * @chandef: the channel definition to check
- * @prohibited_flags: the regulatory chanenl flags that must not be set
+ * @prohibited_flags: the regulatory channel flags that must not be set
+ * Return: %true if secondary channels are usable. %false otherwise.
  */
 bool cfg80211_chandef_usable(struct wiphy *wiphy,
                             const struct cfg80211_chan_def *chandef,
@@ -521,6 +527,26 @@ struct cfg80211_beacon_data {
        size_t probe_resp_len;
 };
 
+struct mac_address {
+       u8 addr[ETH_ALEN];
+};
+
+/**
+ * struct cfg80211_acl_data - Access control list data
+ *
+ * @acl_policy: ACL policy to be applied on the station's
+       entry specified by mac_addr
+ * @n_acl_entries: Number of MAC address entries passed
+ * @mac_addrs: List of MAC addresses of stations to be used for ACL
+ */
+struct cfg80211_acl_data {
+       enum nl80211_acl_policy acl_policy;
+       int n_acl_entries;
+
+       /* Keep it last */
+       struct mac_address mac_addrs[];
+};
+
 /**
  * struct cfg80211_ap_settings - AP configuration
  *
@@ -540,6 +566,8 @@ struct cfg80211_beacon_data {
  * @inactivity_timeout: time in seconds to determine station's inactivity.
  * @p2p_ctwindow: P2P CT Window
  * @p2p_opp_ps: P2P opportunistic PS
+ * @acl: ACL configuration used by the drivers which has support for
+ *     MAC address based access control
  */
 struct cfg80211_ap_settings {
        struct cfg80211_chan_def chandef;
@@ -556,6 +584,7 @@ struct cfg80211_ap_settings {
        int inactivity_timeout;
        u8 p2p_ctwindow;
        bool p2p_opp_ps;
+       const struct cfg80211_acl_data *acl;
 };
 
 /**
@@ -608,6 +637,8 @@ enum station_parameters_apply_mask {
  * @sta_modify_mask: bitmap indicating which parameters changed
  *     (for those that don't have a natural "no change" value),
  *     see &enum station_parameters_apply_mask
+ * @local_pm: local link-specific mesh power save mode (no change when set
+ *     to unknown)
  */
 struct station_parameters {
        u8 *supported_rates;
@@ -623,6 +654,7 @@ struct station_parameters {
        struct ieee80211_vht_cap *vht_capa;
        u8 uapsd_queues;
        u8 max_sp;
+       enum nl80211_mesh_power_mode local_pm;
 };
 
 /**
@@ -653,6 +685,9 @@ struct station_parameters {
  * @STATION_INFO_STA_FLAGS: @sta_flags filled
  * @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled
  * @STATION_INFO_T_OFFSET: @t_offset filled
+ * @STATION_INFO_LOCAL_PM: @local_pm filled
+ * @STATION_INFO_PEER_PM: @peer_pm filled
+ * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled
  */
 enum station_info_flags {
        STATION_INFO_INACTIVE_TIME      = 1<<0,
@@ -676,6 +711,9 @@ enum station_info_flags {
        STATION_INFO_STA_FLAGS          = 1<<18,
        STATION_INFO_BEACON_LOSS_COUNT  = 1<<19,
        STATION_INFO_T_OFFSET           = 1<<20,
+       STATION_INFO_LOCAL_PM           = 1<<21,
+       STATION_INFO_PEER_PM            = 1<<22,
+       STATION_INFO_NONPEER_PM         = 1<<23,
 };
 
 /**
@@ -789,6 +827,9 @@ struct sta_bss_parameters {
  * @sta_flags: station flags mask & values
  * @beacon_loss_count: Number of times beacon loss event has triggered.
  * @t_offset: Time offset of the station relative to this host.
+ * @local_pm: local mesh STA power save mode
+ * @peer_pm: peer mesh STA power save mode
+ * @nonpeer_pm: non-peer mesh STA power save mode
  */
 struct station_info {
        u32 filled;
@@ -818,6 +859,9 @@ struct station_info {
 
        u32 beacon_loss_count;
        s64 t_offset;
+       enum nl80211_mesh_power_mode local_pm;
+       enum nl80211_mesh_power_mode peer_pm;
+       enum nl80211_mesh_power_mode nonpeer_pm;
 
        /*
         * Note: Add a new enum station_info_flags value for each new field and
@@ -993,6 +1037,10 @@ struct bss_parameters {
  * @dot11MeshHWMPconfirmationInterval: The minimum interval of time (in TUs)
  *     during which a mesh STA can send only one Action frame containing
  *     a PREQ element for root path confirmation.
+ * @power_mode: The default mesh power save mode which will be the initial
+ *     setting for new peer links.
+ * @dot11MeshAwakeWindowDuration: The duration in TUs the STA will remain awake
+ *     after transmitting its beacon.
  */
 struct mesh_config {
        u16 dot11MeshRetryTimeout;
@@ -1020,6 +1068,8 @@ struct mesh_config {
        u32 dot11MeshHWMPactivePathToRootTimeout;
        u16 dot11MeshHWMProotInterval;
        u16 dot11MeshHWMPconfirmationInterval;
+       enum nl80211_mesh_power_mode power_mode;
+       u16 dot11MeshAwakeWindowDuration;
 };
 
 /**
@@ -1034,6 +1084,8 @@ struct mesh_config {
  * @ie_len: length of vendor information elements
  * @is_authenticated: this mesh requires authentication
  * @is_secure: this mesh uses security
+ * @dtim_period: DTIM period to use
+ * @beacon_interval: beacon interval to use
  * @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a]
  *
  * These parameters are fixed when the mesh is created.
@@ -1049,6 +1101,8 @@ struct mesh_setup {
        u8 ie_len;
        bool is_authenticated;
        bool is_secure;
+       u8 dtim_period;
+       u16 beacon_interval;
        int mcast_rate[IEEE80211_NUM_BANDS];
 };
 
@@ -1256,7 +1310,7 @@ struct cfg80211_bss {
 
        u8 bssid[ETH_ALEN];
 
-       u8 priv[0] __attribute__((__aligned__(sizeof(void *))));
+       u8 priv[0] __aligned(sizeof(void *));
 };
 
 /**
@@ -1266,7 +1320,7 @@ struct cfg80211_bss {
  *
  * Note that the return value is an RCU-protected pointer, so
  * rcu_read_lock() must be held when calling this function.
- * Returns %NULL if not found.
+ * Return: %NULL if not found.
  */
 const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie);
 
@@ -1434,6 +1488,7 @@ struct cfg80211_ibss_params {
  * @ie: IEs for association request
  * @ie_len: Length of assoc_ie in octets
  * @privacy: indicates whether privacy-enabled APs should be used
+ * @mfp: indicate whether management frame protection is used
  * @crypto: crypto settings
  * @key_len: length of WEP key for shared key authentication
  * @key_idx: index of WEP key for shared key authentication
@@ -1454,6 +1509,7 @@ struct cfg80211_connect_params {
        u8 *ie;
        size_t ie_len;
        bool privacy;
+       enum nl80211_mfp mfp;
        struct cfg80211_crypto_settings crypto;
        const u8 *key;
        u8 key_len, key_idx;
@@ -1763,6 +1819,13 @@ struct cfg80211_gtk_rekey_data {
  *
  * @start_p2p_device: Start the given P2P device.
  * @stop_p2p_device: Stop the given P2P device.
+ *
+ * @set_mac_acl: Sets MAC address control list in AP and P2P GO mode.
+ *     Parameters include ACL policy, an array of MAC address of stations
+ *     and the number of MAC addresses. If there is already a list in driver
+ *     this new list replaces the existing one. Driver has to clear its ACL
+ *     when number of MAC addresses entries is passed as 0. Drivers which
+ *     advertise the support for MAC based ACL have to implement this callback.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -1983,6 +2046,9 @@ struct cfg80211_ops {
                                    struct wireless_dev *wdev);
        void    (*stop_p2p_device)(struct wiphy *wiphy,
                                   struct wireless_dev *wdev);
+
+       int     (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev,
+                              const struct cfg80211_acl_data *params);
 };
 
 /*
@@ -2092,6 +2158,7 @@ struct ieee80211_iface_limit {
  * @beacon_int_infra_match: In this combination, the beacon intervals
  *     between infrastructure and AP types must match. This is required
  *     only in special cases.
+ * @radar_detect_widths: bitmap of channel widths supported for radar detection
  *
  * These examples can be expressed as follows:
  *
@@ -2144,10 +2211,7 @@ struct ieee80211_iface_combination {
        u16 max_interfaces;
        u8 n_limits;
        bool beacon_int_infra_match;
-};
-
-struct mac_address {
-       u8 addr[ETH_ALEN];
+       u8 radar_detect_widths;
 };
 
 struct ieee80211_txrx_stypes {
@@ -2290,6 +2354,9 @@ struct wiphy_wowlan_support {
  * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
  * @ht_capa_mod_mask:  Specify what ht_cap values can be over-ridden.
  *     If null, then none can be over-ridden.
+ *
+ * @max_acl_mac_addrs: Maximum number of MAC addresses that the device
+ *     supports for ACL.
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -2311,6 +2378,8 @@ struct wiphy {
        /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */
        u16 interface_modes;
 
+       u16 max_acl_mac_addrs;
+
        u32 flags, features;
 
        u32 ap_sme_capa;
@@ -2364,12 +2433,12 @@ struct wiphy {
        struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS];
 
        /* Lets us get back the wiphy on the callback */
-       int (*reg_notifier)(struct wiphy *wiphy,
-                           struct regulatory_request *request);
+       void (*reg_notifier)(struct wiphy *wiphy,
+                            struct regulatory_request *request);
 
        /* fields below are read-only, assigned by cfg80211 */
 
-       const struct ieee80211_regdomain *regd;
+       const struct ieee80211_regdomain __rcu *regd;
 
        /* the item in /sys/class/ieee80211/ points to this,
         * you need use set_wiphy_dev() (see below) */
@@ -2392,7 +2461,7 @@ struct wiphy {
        const struct iw_handler_def *wext;
 #endif
 
-       char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
+       char priv[0] __aligned(NETDEV_ALIGN);
 };
 
 static inline struct net *wiphy_net(struct wiphy *wiphy)
@@ -2409,6 +2478,7 @@ static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net)
  * wiphy_priv - return priv from wiphy
  *
  * @wiphy: the wiphy whose priv pointer to return
+ * Return: The priv of @wiphy.
  */
 static inline void *wiphy_priv(struct wiphy *wiphy)
 {
@@ -2420,6 +2490,7 @@ static inline void *wiphy_priv(struct wiphy *wiphy)
  * priv_to_wiphy - return the wiphy containing the priv
  *
  * @priv: a pointer previously returned by wiphy_priv
+ * Return: The wiphy of @priv.
  */
 static inline struct wiphy *priv_to_wiphy(void *priv)
 {
@@ -2442,6 +2513,7 @@ static inline void set_wiphy_dev(struct wiphy *wiphy, struct device *dev)
  * wiphy_dev - get wiphy dev pointer
  *
  * @wiphy: The wiphy whose device struct to look up
+ * Return: The dev of @wiphy.
  */
 static inline struct device *wiphy_dev(struct wiphy *wiphy)
 {
@@ -2452,6 +2524,7 @@ static inline struct device *wiphy_dev(struct wiphy *wiphy)
  * wiphy_name - get wiphy name
  *
  * @wiphy: The wiphy whose name to return
+ * Return: The name of @wiphy.
  */
 static inline const char *wiphy_name(const struct wiphy *wiphy)
 {
@@ -2467,8 +2540,8 @@ static inline const char *wiphy_name(const struct wiphy *wiphy)
  * Create a new wiphy and associate the given operations with it.
  * @sizeof_priv bytes are allocated for private use.
  *
- * The returned pointer must be assigned to each netdev's
- * ieee80211_ptr for proper operation.
+ * Return: A pointer to the new wiphy. This pointer must be
+ * assigned to each netdev's ieee80211_ptr for proper operation.
  */
 struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv);
 
@@ -2477,7 +2550,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv);
  *
  * @wiphy: The wiphy to register.
  *
- * Returns a non-negative wiphy index or a negative error code.
+ * Return: A non-negative wiphy index or a negative error code.
  */
 extern int wiphy_register(struct wiphy *wiphy);
 
@@ -2626,6 +2699,7 @@ static inline u8 *wdev_address(struct wireless_dev *wdev)
  * wdev_priv - return wiphy priv from wireless_dev
  *
  * @wdev: The wireless device whose wiphy's priv pointer to return
+ * Return: The wiphy priv of @wdev.
  */
 static inline void *wdev_priv(struct wireless_dev *wdev)
 {
@@ -2643,12 +2717,14 @@ static inline void *wdev_priv(struct wireless_dev *wdev)
  * ieee80211_channel_to_frequency - convert channel number to frequency
  * @chan: channel number
  * @band: band, necessary due to channel number overlap
+ * Return: The corresponding frequency (in MHz), or 0 if the conversion failed.
  */
 extern int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band);
 
 /**
  * ieee80211_frequency_to_channel - convert frequency to channel number
  * @freq: center frequency
+ * Return: The corresponding channel, or 0 if the conversion failed.
  */
 extern int ieee80211_frequency_to_channel(int freq);
 
@@ -2665,6 +2741,7 @@ extern struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy,
  * ieee80211_get_channel - get channel struct from wiphy for specified frequency
  * @wiphy: the struct wiphy to get the channel for
  * @freq: the center frequency of the channel
+ * Return: The channel struct from @wiphy at @freq.
  */
 static inline struct ieee80211_channel *
 ieee80211_get_channel(struct wiphy *wiphy, int freq)
@@ -2679,10 +2756,10 @@ ieee80211_get_channel(struct wiphy *wiphy, int freq)
  * @basic_rates: bitmap of basic rates
  * @bitrate: the bitrate for which to find the basic rate
  *
- * This function returns the basic rate corresponding to a given
- * bitrate, that is the next lower bitrate contained in the basic
- * rate map, which is, for this function, given as a bitmap of
- * indices of rates in the band's bitrate table.
+ * Return: The basic rate corresponding to a given bitrate, that
+ * is the next lower bitrate contained in the basic rate map,
+ * which is, for this function, given as a bitmap of indices of
+ * rates in the band's bitrate table.
  */
 struct ieee80211_rate *
 ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
@@ -2775,18 +2852,21 @@ extern const unsigned char bridge_tunnel_header[6];
 /**
  * ieee80211_get_hdrlen_from_skb - get header length from data
  *
+ * @skb: the frame
+ *
  * Given an skb with a raw 802.11 header at the data pointer this function
- * returns the 802.11 header length in bytes (not including encryption
- * headers). If the data in the sk_buff is too short to contain a valid 802.11
- * header the function returns 0.
+ * returns the 802.11 header length.
  *
- * @skb: the frame
+ * Return: The 802.11 header length in bytes (not including encryption
+ * headers). Or 0 if the data in the sk_buff is too short to contain a valid
+ * 802.11 header.
  */
 unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb);
 
 /**
  * ieee80211_hdrlen - get header length in bytes from frame control
  * @fc: frame control field in little-endian format
+ * Return: The header length in bytes.
  */
 unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc);
 
@@ -2794,7 +2874,7 @@ unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc);
  * ieee80211_get_mesh_hdrlen - get mesh extension header length
  * @meshhdr: the mesh extension header, only the flags field
  *     (first byte) will be accessed
- * Returns the length of the extension header, which is always at
+ * Return: The length of the extension header, which is always at
  * least 6 bytes and at most 18 if address 5 and 6 are present.
  */
 unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
@@ -2812,6 +2892,7 @@ unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
  * @skb: the 802.11 data frame
  * @addr: the device MAC address
  * @iftype: the virtual interface type
+ * Return: 0 on success. Non-zero on error.
  */
 int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
                           enum nl80211_iftype iftype);
@@ -2823,6 +2904,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
  * @iftype: the virtual interface type
  * @bssid: the network bssid (used only for iftype STATION and ADHOC)
  * @qos: build 802.11 QoS data frame
+ * Return: 0 on success, or a negative error code.
  */
 int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
                             enum nl80211_iftype iftype, u8 *bssid, bool qos);
@@ -2850,6 +2932,7 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
 /**
  * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame
  * @skb: the data frame
+ * Return: The 802.1p/1d tag.
  */
 unsigned int cfg80211_classify8021d(struct sk_buff *skb);
 
@@ -2860,12 +2943,13 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb);
  * @ies: data consisting of IEs
  * @len: length of data
  *
- * This function will return %NULL if the element ID could
- * not be found or if the element is invalid (claims to be
- * longer than the given data), or a pointer to the first byte
- * of the requested element, that is the byte containing the
- * element ID. There are no checks on the element length
- * other than having to fit into the given data.
+ * Return: %NULL if the element ID could not be found or if
+ * the element is invalid (claims to be longer than the given
+ * data), or a pointer to the first byte of the requested
+ * element, that is the byte containing the element ID.
+ *
+ * Note: There are no checks on the element length other than
+ * having to fit into the given data.
  */
 const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len);
 
@@ -2877,12 +2961,13 @@ const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len);
  * @ies: data consisting of IEs
  * @len: length of data
  *
- * This function will return %NULL if the vendor specific element ID
- * could not be found or if the element is invalid (claims to be
- * longer than the given data), or a pointer to the first byte
- * of the requested element, that is the byte containing the
- * element ID. There are no checks on the element length
- * other than having to fit into the given data.
+ * Return: %NULL if the vendor specific element ID could not be found or if the
+ * element is invalid (claims to be longer than the given data), or a pointer to
+ * the first byte of the requested element, that is the byte containing the
+ * element ID.
+ *
+ * Note: There are no checks on the element length other than having to fit into
+ * the given data.
  */
 const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
                                  const u8 *ies, int len);
@@ -2915,6 +3000,8 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
  *
  * Drivers should check the return value, its possible you can get
  * an -ENOMEM.
+ *
+ * Return: 0 on success. -ENOMEM.
  */
 extern int regulatory_hint(struct wiphy *wiphy, const char *alpha2);
 
@@ -2938,28 +3025,22 @@ extern void wiphy_apply_custom_regulatory(
  * freq_reg_info - get regulatory information for the given frequency
  * @wiphy: the wiphy for which we want to process this rule for
  * @center_freq: Frequency in KHz for which we want regulatory information for
- * @desired_bw_khz: the desired max bandwidth you want to use per
- *     channel. Note that this is still 20 MHz if you want to use HT40
- *     as HT40 makes use of two channels for its 40 MHz width bandwidth.
- *     If set to 0 we'll assume you want the standard 20 MHz.
- * @reg_rule: the regulatory rule which we have for this frequency
  *
  * Use this function to get the regulatory rule for a specific frequency on
  * a given wireless device. If the device has a specific regulatory domain
  * it wants to follow we respect that unless a country IE has been received
  * and processed already.
  *
- * Returns 0 if it was able to find a valid regulatory rule which does
- * apply to the given center_freq otherwise it returns non-zero. It will
- * also return -ERANGE if we determine the given center_freq does not even have
- * a regulatory rule for a frequency range in the center_freq's band. See
- * freq_in_rule_band() for our current definition of a band -- this is purely
- * subjective and right now its 802.11 specific.
+ * Return: A valid pointer, or, when an error occurs, for example if no rule
+ * can be found, the return value is encoded using ERR_PTR(). Use IS_ERR() to
+ * check and PTR_ERR() to obtain the numeric return value. The numeric return
+ * value will be -ERANGE if we determine the given center_freq does not even
+ * have a regulatory rule for a frequency range in the center_freq's band.
+ * See freq_in_rule_band() for our current definition of a band -- this is
+ * purely subjective and right now it's 802.11 specific.
  */
-extern int freq_reg_info(struct wiphy *wiphy,
-                        u32 center_freq,
-                        u32 desired_bw_khz,
-                        const struct ieee80211_reg_rule **reg_rule);
+const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
+                                              u32 center_freq);
 
 /*
  * callbacks for asynchronous cfg80211 methods, notification
@@ -3006,7 +3087,8 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
  * This informs cfg80211 that BSS information was found and
  * the BSS should be updated/added.
  *
- * NOTE: Returns a referenced struct, must be released with cfg80211_put_bss()!
+ * Return: A referenced struct, must be released with cfg80211_put_bss()!
+ * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
 cfg80211_inform_bss_frame(struct wiphy *wiphy,
@@ -3031,7 +3113,8 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
  * This informs cfg80211 that BSS information was found and
  * the BSS should be updated/added.
  *
- * NOTE: Returns a referenced struct, must be released with cfg80211_put_bss()!
+ * Return: A referenced struct, must be released with cfg80211_put_bss()!
+ * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
 cfg80211_inform_bss(struct wiphy *wiphy,
@@ -3308,16 +3391,18 @@ void wiphy_rfkill_stop_polling(struct wiphy *wiphy);
  * the testmode command. Since it is intended for a reply, calling
  * it outside of the @testmode_cmd operation is invalid.
  *
- * The returned skb (or %NULL if any errors happen) is pre-filled
- * with the wiphy index and set up in a way that any data that is
- * put into the skb (with skb_put(), nla_put() or similar) will end
- * up being within the %NL80211_ATTR_TESTDATA attribute, so all that
- * needs to be done with the skb is adding data for the corresponding
- * userspace tool which can then read that data out of the testdata
- * attribute. You must not modify the skb in any other way.
+ * The returned skb is pre-filled with the wiphy index and set up in
+ * a way that any data that is put into the skb (with skb_put(),
+ * nla_put() or similar) will end up being within the
+ * %NL80211_ATTR_TESTDATA attribute, so all that needs to be done
+ * with the skb is adding data for the corresponding userspace tool
+ * which can then read that data out of the testdata attribute. You
+ * must not modify the skb in any other way.
  *
  * When done, call cfg80211_testmode_reply() with the skb and return
  * its error code as the result of the @testmode_cmd operation.
+ *
+ * Return: An allocated and pre-filled skb. %NULL if any errors happen.
  */
 struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
                                                  int approxlen);
@@ -3327,11 +3412,12 @@ struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
  * @skb: The skb, must have been allocated with
  *     cfg80211_testmode_alloc_reply_skb()
  *
- * Returns an error code or 0 on success, since calling this
- * function will usually be the last thing before returning
- * from the @testmode_cmd you should return the error code.
- * Note that this function consumes the skb regardless of the
- * return value.
+ * Since calling this function will usually be the last thing
+ * before returning from the @testmode_cmd you should return
+ * the error code.  Note that this function consumes the skb
+ * regardless of the return value.
+ *
+ * Return: An error code or 0 on success.
  */
 int cfg80211_testmode_reply(struct sk_buff *skb);
 
@@ -3345,14 +3431,16 @@ int cfg80211_testmode_reply(struct sk_buff *skb);
  * This function allocates and pre-fills an skb for an event on the
  * testmode multicast group.
  *
- * The returned skb (or %NULL if any errors happen) is set up in the
- * same way as with cfg80211_testmode_alloc_reply_skb() but prepared
- * for an event. As there, you should simply add data to it that will
- * then end up in the %NL80211_ATTR_TESTDATA attribute. Again, you must
- * not modify the skb in any other way.
+ * The returned skb is set up in the same way as with
+ * cfg80211_testmode_alloc_reply_skb() but prepared for an event. As
+ * there, you should simply add data to it that will then end up in the
+ * %NL80211_ATTR_TESTDATA attribute. Again, you must not modify the skb
+ * in any other way.
  *
  * When done filling the skb, call cfg80211_testmode_event() with the
  * skb to send the event.
+ *
+ * Return: An allocated and pre-filled skb. %NULL if any errors happen.
  */
 struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
                                                  int approxlen, gfp_t gfp);
@@ -3533,13 +3621,13 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
  * @len: length of the frame data
  * @gfp: context flags
  *
- * Returns %true if a user space application has registered for this frame.
+ * This function is called whenever an Action frame is received for a station
+ * mode interface, but is not processed in kernel.
+ *
+ * Return: %true if a user space application has registered for this frame.
  * For action frames, that makes it responsible for rejecting unrecognized
  * action frames; %false otherwise, in which case for action frames the
  * driver is responsible for rejecting the frame.
- *
- * This function is called whenever an Action frame is received for a station
- * mode interface, but is not processed in kernel.
  */
 bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_dbm,
                      const u8 *buf, size_t len, gfp_t gfp);
@@ -3631,7 +3719,7 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
  * This function is used in AP mode (only!) to inform userspace that
  * a spurious class 3 frame was received, to be able to deauth the
  * sender.
- * Returns %true if the frame was passed to userspace (or this failed
+ * Return: %true if the frame was passed to userspace (or this failed
  * for a reason other than not having a subscription.)
  */
 bool cfg80211_rx_spurious_frame(struct net_device *dev,
@@ -3647,7 +3735,7 @@ bool cfg80211_rx_spurious_frame(struct net_device *dev,
  * an associated station sent a 4addr frame but that wasn't expected.
  * It is allowed and desirable to send this event only once for each
  * station to avoid event flooding.
- * Returns %true if the frame was passed to userspace (or this failed
+ * Return: %true if the frame was passed to userspace (or this failed
  * for a reason other than not having a subscription.)
  */
 bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
@@ -3685,8 +3773,8 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
  * @wiphy: the wiphy
  * @chandef: the channel definition
  *
- * This function returns true if there is no secondary channel or the secondary
- * channel(s) can be used for beaconing (i.e. is not a radar channel etc.)
+ * Return: %true if there is no secondary channel or the secondary channel(s)
+ * can be used for beaconing (i.e. is not a radar channel etc.)
  */
 bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
                             struct cfg80211_chan_def *chandef);
@@ -3756,9 +3844,9 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev);
  * The function finds a given P2P attribute in the (vendor) IEs and
  * copies its contents to the given buffer.
  *
- * The return value is a negative error code (-%EILSEQ or -%ENOENT) if
- * the data is malformed or the attribute can't be found (respectively),
- * or the length of the found attribute (which can be zero).
+ * Return: A negative error code (-%EILSEQ or -%ENOENT) if the data is
+ * malformed or the attribute can't be found (respectively), or the
+ * length of the found attribute (which can be zero).
  */
 int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
                          enum ieee80211_p2p_attr_id attr,
index ee50c5eba50c027aed710c7fded8616b9461b96a..5c98d654fc75ba3f3d0974f0f16cc5e9b10d9c99 100644 (file)
@@ -173,7 +173,7 @@ struct ieee80211_chanctx_conf {
 
        u8 rx_chains_static, rx_chains_dynamic;
 
-       u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
+       u8 drv_priv[0] __aligned(sizeof(void *));
 };
 
 /**
@@ -297,11 +297,9 @@ enum ieee80211_rssi_event {
  *     may filter ARP queries targeted for other addresses than listed here.
  *     The driver must allow ARP queries targeted for all address listed here
  *     to pass through. An empty list implies no ARP queries need to pass.
- * @arp_addr_cnt: Number of addresses currently on the list.
- * @arp_filter_enabled: Enable ARP filtering - if enabled, the hardware may
- *     filter ARP queries based on the @arp_addr_list, if disabled, the
- *     hardware must not perform any ARP filtering. Note, that the filter will
- *     be enabled also in promiscuous mode.
+ * @arp_addr_cnt: Number of addresses currently on the list. Note that this
+ *     may be larger than %IEEE80211_BSS_ARP_ADDR_LIST_LEN (the arp_addr_list
+ *     array size), it's up to the driver what to do in that case.
  * @qos: This is a QoS-enabled BSS.
  * @idle: This interface is idle. There's also a global idle flag in the
  *     hardware config which may be more appropriate depending on what
@@ -338,8 +336,7 @@ struct ieee80211_bss_conf {
        u32 cqm_rssi_hyst;
        struct cfg80211_chan_def chandef;
        __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN];
-       u8 arp_addr_cnt;
-       bool arp_filter_enabled;
+       int arp_addr_cnt;
        bool qos;
        bool idle;
        bool ps;
@@ -1059,7 +1056,7 @@ struct ieee80211_vif {
        u32 driver_flags;
 
        /* must be last */
-       u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
+       u8 drv_priv[0] __aligned(sizeof(void *));
 };
 
 static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
@@ -1209,7 +1206,7 @@ struct ieee80211_sta {
        u8 max_sp;
 
        /* must be last */
-       u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
+       u8 drv_priv[0] __aligned(sizeof(void *));
 };
 
 /**
@@ -1522,6 +1519,8 @@ struct ieee80211_hw {
  * structure can then access it via hw->priv. Note that mac802111 drivers should
  * not use wiphy_priv() to try to get their private driver structure as this
  * is already used internally by mac80211.
+ *
+ * Return: The mac80211 driver hw struct of @wiphy.
  */
 struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy);
 
@@ -1628,6 +1627,10 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
  * rekeying), it will not include a valid phase 1 key. The valid phase 1 key is
  * provided by update_tkip_key only. The trigger that makes mac80211 call this
  * handler is software decryption with wrap around of iv16.
+ *
+ * The set_default_unicast_key() call updates the default WEP key index
+ * configured to the hardware for WEP encryption type. This is required
+ * for devices that support offload of data packets (e.g. ARP responses).
  */
 
 /**
@@ -2033,17 +2036,29 @@ enum ieee80211_filter_flags {
  * calling ieee80211_start_tx_ba_cb_irqsafe, because the peer
  * might receive the addBA frame and send a delBA right away!
  *
- * @IEEE80211_AMPDU_RX_START: start Rx aggregation
- * @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation
- * @IEEE80211_AMPDU_TX_START: start Tx aggregation
- * @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation
+ * @IEEE80211_AMPDU_RX_START: start RX aggregation
+ * @IEEE80211_AMPDU_RX_STOP: stop RX aggregation
+ * @IEEE80211_AMPDU_TX_START: start TX aggregation
  * @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational
+ * @IEEE80211_AMPDU_TX_STOP_CONT: stop TX aggregation but continue transmitting
+ *     queued packets, now unaggregated. After all packets are transmitted the
+ *     driver has to call ieee80211_stop_tx_ba_cb_irqsafe().
+ * @IEEE80211_AMPDU_TX_STOP_FLUSH: stop TX aggregation and flush all packets,
+ *     called when the station is removed. There's no need or reason to call
+ *     ieee80211_stop_tx_ba_cb_irqsafe() in this case as mac80211 assumes the
+ *     session is gone and removes the station.
+ * @IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: called when TX aggregation is stopped
+ *     but the driver hasn't called ieee80211_stop_tx_ba_cb_irqsafe() yet and
+ *     now the connection is dropped and the station will be removed. Drivers
+ *     should clean up and drop remaining packets when this is called.
  */
 enum ieee80211_ampdu_mlme_action {
        IEEE80211_AMPDU_RX_START,
        IEEE80211_AMPDU_RX_STOP,
        IEEE80211_AMPDU_TX_START,
-       IEEE80211_AMPDU_TX_STOP,
+       IEEE80211_AMPDU_TX_STOP_CONT,
+       IEEE80211_AMPDU_TX_STOP_FLUSH,
+       IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
        IEEE80211_AMPDU_TX_OPERATIONAL,
 };
 
@@ -2194,6 +2209,10 @@ enum ieee80211_rate_control_changed {
  *     After rekeying was done it should (for example during resume) notify
  *     userspace of the new replay counter using ieee80211_gtk_rekey_notify().
  *
+ * @set_default_unicast_key: Set the default (unicast) key index, useful for
+ *     WEP when the device sends data packets autonomously, e.g. for ARP
+ *     offloading. The index can be 0-3, or -1 for unsetting it.
+ *
  * @hw_scan: Ask the hardware to service the scan request, no need to start
  *     the scan state machine in stack. The scan must honour the channel
  *     configuration done by the regulatory agent in the wiphy's
@@ -2474,7 +2493,13 @@ enum ieee80211_rate_control_changed {
  *
  * @restart_complete: Called after a call to ieee80211_restart_hw(), when the
  *     reconfiguration has completed. This can help the driver implement the
- *     reconfiguration step. This callback may sleep.
+ *     reconfiguration step. Also called when reconfiguring because the
+ *     driver's resume function returned 1, as this is just like an "inline"
+ *     hardware restart. This callback may sleep.
+ *
+ * @ipv6_addr_change: IPv6 address assignment on the given interface changed.
+ *     Currently, this is only called for managed or P2P client interfaces.
+ *     This callback is optional; it must not sleep.
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw,
@@ -2522,6 +2547,8 @@ struct ieee80211_ops {
        void (*set_rekey_data)(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               struct cfg80211_gtk_rekey_data *data);
+       void (*set_default_unicast_key)(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif, int idx);
        int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                       struct cfg80211_scan_request *req);
        void (*cancel_hw_scan)(struct ieee80211_hw *hw,
@@ -2606,6 +2633,7 @@ struct ieee80211_ops {
        int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                                const struct cfg80211_bitrate_mask *mask);
        void (*rssi_callback)(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
                              enum ieee80211_rssi_event rssi_event);
 
        void (*allow_buffered_frames)(struct ieee80211_hw *hw,
@@ -2648,6 +2676,12 @@ struct ieee80211_ops {
                                     struct ieee80211_chanctx_conf *ctx);
 
        void (*restart_complete)(struct ieee80211_hw *hw);
+
+#if IS_ENABLED(CONFIG_IPV6)
+       void (*ipv6_addr_change)(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif,
+                                struct inet6_dev *idev);
+#endif
 };
 
 /**
@@ -2661,6 +2695,8 @@ struct ieee80211_ops {
  *
  * @priv_data_len: length of private data
  * @ops: callbacks for this device
+ *
+ * Return: A pointer to the new hardware device, or %NULL on error.
  */
 struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
                                        const struct ieee80211_ops *ops);
@@ -2673,6 +2709,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
  * need to fill the contained wiphy's information.
  *
  * @hw: the device to register as returned by ieee80211_alloc_hw()
+ *
+ * Return: 0 on success. An error code otherwise.
  */
 int ieee80211_register_hw(struct ieee80211_hw *hw);
 
@@ -2719,6 +2757,8 @@ extern char *__ieee80211_create_tpt_led_trigger(
  * of the trigger so you can automatically link the LED device.
  *
  * @hw: the hardware to get the LED trigger name for
+ *
+ * Return: The name of the LED trigger. %NULL if not configured for LEDs.
  */
 static inline char *ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
 {
@@ -2738,6 +2778,8 @@ static inline char *ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
  * of the trigger so you can automatically link the LED device.
  *
  * @hw: the hardware to get the LED trigger name for
+ *
+ * Return: The name of the LED trigger. %NULL if not configured for LEDs.
  */
 static inline char *ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
 {
@@ -2757,6 +2799,8 @@ static inline char *ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
  * of the trigger so you can automatically link the LED device.
  *
  * @hw: the hardware to get the LED trigger name for
+ *
+ * Return: The name of the LED trigger. %NULL if not configured for LEDs.
  */
 static inline char *ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
 {
@@ -2776,6 +2820,8 @@ static inline char *ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
  * of the trigger so you can automatically link the LED device.
  *
  * @hw: the hardware to get the LED trigger name for
+ *
+ * Return: The name of the LED trigger. %NULL if not configured for LEDs.
  */
 static inline char *ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
 {
@@ -2793,9 +2839,10 @@ static inline char *ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
  * @blink_table: the blink table -- needs to be ordered by throughput
  * @blink_table_len: size of the blink table
  *
- * This function returns %NULL (in case of error, or if no LED
- * triggers are configured) or the name of the new trigger.
- * This function must be called before ieee80211_register_hw().
+ * Return: %NULL (in case of error, or if no LED triggers are
+ * configured) or the name of the new trigger.
+ *
+ * Note: This function must be called before ieee80211_register_hw().
  */
 static inline char *
 ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, unsigned int flags,
@@ -2928,10 +2975,10 @@ static inline void ieee80211_rx_ni(struct ieee80211_hw *hw,
  * Calls to this function for a single hardware must be synchronized against
  * each other.
  *
- * The function returns -EINVAL when the requested PS mode is already set.
- *
  * @sta: currently connected sta
  * @start: start or stop PS
+ *
+ * Return: 0 on success. -EINVAL when the requested PS mode is already set.
  */
 int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start);
 
@@ -2945,6 +2992,8 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start);
  *
  * @sta: currently connected sta
  * @start: start or stop PS
+ *
+ * Return: Like ieee80211_sta_ps_transition().
  */
 static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta,
                                                  bool start)
@@ -3082,6 +3131,8 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets);
  * according to the current DTIM parameters/TIM bitmap.
  *
  * The driver is responsible for freeing the returned skb.
+ *
+ * Return: The beacon template. %NULL on error.
  */
 struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                                         struct ieee80211_vif *vif,
@@ -3093,6 +3144,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  *
  * See ieee80211_beacon_get_tim().
+ *
+ * Return: See ieee80211_beacon_get_tim().
  */
 static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
                                                   struct ieee80211_vif *vif)
@@ -3109,6 +3162,8 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
  * hardware. The destination address should be set by the caller.
  *
  * Can only be called in AP mode.
+ *
+ * Return: The Probe Response template. %NULL on error.
  */
 struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw,
                                        struct ieee80211_vif *vif);
@@ -3124,6 +3179,8 @@ struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw,
  *
  * Note: Caller (or hardware) is responsible for setting the
  * &IEEE80211_FCTL_PM bit.
+ *
+ * Return: The PS Poll template. %NULL on error.
  */
 struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif);
@@ -3139,6 +3196,8 @@ struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw,
  *
  * Note: Caller (or hardware) is responsible for setting the
  * &IEEE80211_FCTL_PM bit as well as Duration and Sequence Control fields.
+ *
+ * Return: The nullfunc template. %NULL on error.
  */
 struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif);
@@ -3153,6 +3212,8 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
  *
  * Creates a Probe Request template which can, for example, be uploaded to
  * hardware.
+ *
+ * Return: The Probe Request template. %NULL on error.
  */
 struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
@@ -3188,6 +3249,8 @@ void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
  * If the RTS is generated in firmware, but the host system must provide
  * the duration field, the low-level driver uses this function to receive
  * the duration field value in little-endian byteorder.
+ *
+ * Return: The duration.
  */
 __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif, size_t frame_len,
@@ -3223,6 +3286,8 @@ void ieee80211_ctstoself_get(struct ieee80211_hw *hw,
  * If the CTS-to-self is generated in firmware, but the host system must provide
  * the duration field, the low-level driver uses this function to receive
  * the duration field value in little-endian byteorder.
+ *
+ * Return: The duration.
  */
 __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif,
@@ -3239,6 +3304,8 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
  *
  * Calculate the duration field of some generic frame, given its
  * length and transmission rate (in 100kbps).
+ *
+ * Return: The duration.
  */
 __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
                                        struct ieee80211_vif *vif,
@@ -3255,9 +3322,10 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
  * hardware/firmware does not implement buffering of broadcast/multicast
  * frames when power saving is used, 802.11 code buffers them in the host
  * memory. The low-level driver uses this function to fetch next buffered
- * frame. In most cases, this is used when generating beacon frame. This
- * function returns a pointer to the next buffered skb or NULL if no more
- * buffered frames are available.
+ * frame. In most cases, this is used when generating beacon frame.
+ *
+ * Return: A pointer to the next buffered skb or NULL if no more buffered
+ * frames are available.
  *
  * Note: buffered frames are returned only after DTIM beacon frame was
  * generated with ieee80211_beacon_get() and the low-level driver must thus
@@ -3437,6 +3505,8 @@ void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue);
  * @queue: queue number (counted from zero).
  *
  * Drivers should use this function instead of netif_stop_queue.
+ *
+ * Return: %true if the queue is stopped. %false otherwise.
  */
 
 int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue);
@@ -3634,7 +3704,9 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra,
  * @vif: virtual interface to look for station on
  * @addr: station's address
  *
- * This function must be called under RCU lock and the
+ * Return: The station, if found. %NULL otherwise.
+ *
+ * Note: This function must be called under RCU lock and the
  * resulting pointer is only valid under RCU lock as well.
  */
 struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
@@ -3647,7 +3719,9 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
  * @addr: remote station's address
  * @localaddr: local address (vif->sdata->vif.addr). Use NULL for 'any'.
  *
- * This function must be called under RCU lock and the
+ * Return: The station, if found. %NULL otherwise.
+ *
+ * Note: This function must be called under RCU lock and the
  * resulting pointer is only valid under RCU lock as well.
  *
  * NOTE: You may pass NULL for localaddr, but then you will just get
@@ -3754,6 +3828,11 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
  * The iterator will not find a context that's being added (during
  * the driver callback to add it) but will find it while it's being
  * removed.
+ *
+ * Note that during hardware restart, all contexts that existed
+ * before the restart are considered already present so will be
+ * found while iterating, whether they've been re-added already
+ * or not.
  */
 void ieee80211_iter_chan_contexts_atomic(
        struct ieee80211_hw *hw,
@@ -3772,7 +3851,9 @@ void ieee80211_iter_chan_contexts_atomic(
  * information. This function must only be called from within the
  * .bss_info_changed callback function and only in managed mode. The function
  * is only useful when the interface is associated, otherwise it will return
- * NULL.
+ * %NULL.
+ *
+ * Return: The Probe Request template. %NULL on error.
  */
 struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
                                          struct ieee80211_vif *vif);
@@ -4119,12 +4200,14 @@ void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif,
 void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif);
 
 /**
- * ieee80211_ave_rssi - report the average rssi for the specified interface
+ * ieee80211_ave_rssi - report the average RSSI for the specified interface
  *
  * @vif: the specified virtual interface
  *
- * This function return the average rssi value for the requested interface.
- * It assumes that the given vif is valid.
+ * Note: This function assumes that the given vif is valid.
+ *
+ * Return: The average RSSI value for the requested interface, or 0 if not
+ * applicable.
  */
 int ieee80211_ave_rssi(struct ieee80211_vif *vif);
 
index 671953e11575449b30a10081d5b7aa7b26c0fa0a..b87a1692b0864f436a9185447cbfe74cce6ec7e1 100644 (file)
@@ -57,8 +57,10 @@ struct nfc_hci_ops {
        int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_hci_dev *hdev,
                              struct nfc_target *target);
-       void (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
-                               struct sk_buff *skb);
+       int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
+                             struct sk_buff *skb);
+       int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
+       int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
 };
 
 /* Pipes */
@@ -82,11 +84,23 @@ typedef int (*xmit) (struct sk_buff *skb, void *cb_data);
 
 #define NFC_HCI_MAX_GATES              256
 
+/*
+ * These values can be specified by a driver to indicate it requires some
+ * adaptation of the HCI standard.
+ *
+ * NFC_HCI_QUIRK_SHORT_CLEAR - send HCI_ADM_CLEAR_ALL_PIPE cmd with no params
+ */
+enum {
+       NFC_HCI_QUIRK_SHORT_CLEAR       = 0,
+};
+
 struct nfc_hci_dev {
        struct nfc_dev *ndev;
 
        u32 max_data_link_payload;
 
+       bool shutting_down;
+
        struct mutex msg_tx_mutex;
 
        struct list_head msg_tx_queue;
@@ -129,12 +143,16 @@ struct nfc_hci_dev {
 
        u8 *gb;
        size_t gb_len;
+
+       unsigned long quirks;
 };
 
 /* hci device allocation */
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
+                                           unsigned long quirks,
                                            u32 protocols,
+                                           u32 supported_se,
                                            const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
index d705d867494987b30da4272fe4c1967a7884412a..5bc0c460edc0189b56633a111159a15f7a430131 100644 (file)
@@ -147,6 +147,7 @@ struct nci_dev {
 /* ----- NCI Devices ----- */
 struct nci_dev *nci_allocate_device(struct nci_ops *ops,
                                    __u32 supported_protocols,
+                                   __u32 supported_se,
                                    int tx_headroom,
                                    int tx_tailroom);
 void nci_free_device(struct nci_dev *ndev);
index fce80b2f9be7e5eaf50e5fc5ccf55fde0440f793..87a6417fc934487acb2c734f53af3b78fb09d013 100644 (file)
@@ -68,6 +68,8 @@ struct nfc_ops {
                             void *cb_context);
        int (*tm_send)(struct nfc_dev *dev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target);
+       int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
+       int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
 };
 
 #define NFC_TARGET_IDX_ANY -1
@@ -109,12 +111,17 @@ struct nfc_dev {
        struct nfc_genl_data genl_data;
        u32 supported_protocols;
 
+       u32 supported_se;
+       u32 active_se;
+
        int tx_headroom;
        int tx_tailroom;
 
        struct timer_list check_pres_timer;
        struct work_struct check_pres_work;
 
+       bool shutting_down;
+
        struct nfc_ops *ops;
 };
 #define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev)
@@ -123,6 +130,7 @@ extern struct class nfc_class;
 
 struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                                    u32 supported_protocols,
+                                   u32 supported_se,
                                    int tx_headroom,
                                    int tx_tailroom);
 
index 7dcaa2794fde2e5c753c49ce9fb7763cfd9d78e6..f17ed590d64a35b314c8c9c42dc7dc153e0f1619 100644 (file)
@@ -18,6 +18,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <linux/rcupdate.h>
 
 /**
  * enum environment_cap - Environment parsed from country IE
@@ -35,6 +36,7 @@ enum environment_cap {
 /**
  * struct regulatory_request - used to keep track of regulatory requests
  *
+ * @rcu_head: RCU head struct used to free the request
  * @wiphy_idx: this is set if this request's initiator is
  *     %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This
  *     can be used by the wireless core to deal with conflicts
@@ -72,6 +74,7 @@ enum environment_cap {
  * @list: used to insert into the reg_requests_list linked list
  */
 struct regulatory_request {
+       struct rcu_head rcu_head;
        int wiphy_idx;
        enum nl80211_reg_initiator initiator;
        enum nl80211_user_reg_hint_type user_reg_hint_type;
@@ -101,6 +104,7 @@ struct ieee80211_reg_rule {
 };
 
 struct ieee80211_regdomain {
+       struct rcu_head rcu_head;
        u32 n_reg_rules;
        char alpha2[2];
        u8 dfs_region;
diff --git a/include/sound/aess.h b/include/sound/aess.h
new file mode 100644 (file)
index 0000000..cee0d09
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * AESS IP block reset
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ * Paul Walmsley
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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 __SOUND_AESS_H__
+#define __SOUND_AESS_H__
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+/*
+ * AESS_AUTO_GATING_ENABLE_OFFSET: offset in bytes of the AESS IP
+ *     block's AESS_AUTO_GATING_ENABLE__1 register from the IP block's
+ *     base address
+ */
+#define AESS_AUTO_GATING_ENABLE_OFFSET                 0x07c
+
+/* Register bitfields in the AESS_AUTO_GATING_ENABLE__1 register */
+#define AESS_AUTO_GATING_ENABLE_SHIFT                  0
+
+/**
+ * aess_enable_autogating - enable AESS internal autogating
+ * @oh: struct omap_hwmod *
+ *
+ * Enable internal autogating on the AESS.  This allows the AESS to
+ * indicate that it is idle to the OMAP PRCM.  Returns 0.
+ */
+static inline void aess_enable_autogating(void __iomem *base)
+{
+       u32 v;
+
+       /* Set AESS_AUTO_GATING_ENABLE__1.ENABLE to allow idle entry */
+       v = 1 << AESS_AUTO_GATING_ENABLE_SHIFT;
+       writel(v, base + AESS_AUTO_GATING_ENABLE_OFFSET);
+}
+
+#endif /* __SOUND_AESS_H__ */
index dd8c48d14ed9637b3d1264ac5b88811e9be2dae6..70f45355acaa299eed01ecdc2ecc1f967a77e114 100644 (file)
 struct cs4271_platform_data {
        int gpio_nreset;        /* GPIO driving Reset pin, if any */
        bool amutec_eq_bmutec;  /* flag to enable AMUTEC=BMUTEC */
+
+       /*
+        * The CS4271 requires its LRCLK and MCLK to be stable before its RESET
+        * line is de-asserted. That also means that clocks cannot be changed
+        * without putting the chip back into hardware reset, which also requires
+        * a complete re-initialization of all registers.
+        *
+        * One (undocumented) workaround is to assert and de-assert the PDN bit
+        * in the MODE2 register. This workaround can be enabled with the
+        * following flag.
+        *
+        * Note that this is not needed in case the clocks are stable
+        * throughout the entire runtime of the codec.
+        */
+       bool enable_soft_reset;
 };
 
 #endif /* __CS4271_H */
index 45c1981c9ca2b67fa7fb5ce76f609a87bc5a08a4..5bc7cd24d1d42f49e91759ce86755ec8d6e23f88 100644 (file)
@@ -416,6 +416,7 @@ struct snd_pcm_substream {
 #endif
        /* misc flags */
        unsigned int hw_opened: 1;
+       unsigned int hw_no_buffer: 1; /* substream may not have a buffer */
 };
 
 #define SUBSTREAM_BUSY(substream) ((substream)->ref_count > 0)
@@ -791,6 +792,8 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
                          const struct snd_interval *b, struct snd_interval *c);
 int snd_interval_list(struct snd_interval *i, unsigned int count,
                      const unsigned int *list, unsigned int mask);
+int snd_interval_step(struct snd_interval *i,
+                       unsigned int min, unsigned int step);
 int snd_interval_ratnum(struct snd_interval *i,
                        unsigned int rats_count, struct snd_ratnum *rats,
                        unsigned int *nump, unsigned int *denp);
diff --git a/include/sound/saif.h b/include/sound/saif.h
deleted file mode 100644 (file)
index f22f3e1..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __SOUND_SAIF_H__
-#define __SOUND_SAIF_H__
-
-struct mxs_saif_platform_data {
-       bool master_mode;       /* if true use master mode */
-       int master_id;          /* id of the master if in slave mode */
-};
-#endif
index cc1c919c64365898c47f7f6871b906dce0e5e1d4..7a9710b4b799cd057ec2319e16a9237586d1411a 100644 (file)
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-
-#define FSI_PORT_A     0
-#define FSI_PORT_B     1
-
 #include <linux/clk.h>
 #include <sound/soc.h>
 
 /*
- * flags format
- *
- * 0x00000CBA
- *
- * A:  inversion
- * B:  format mode
- * C:  chip specific
- * D:  clock selecter if master mode
+ * flags
  */
-
-/* A: clock inversion */
-#define SH_FSI_INVERSION_MASK  0x0000000F
-#define SH_FSI_LRM_INV         (1 << 0)
-#define SH_FSI_BRM_INV         (1 << 1)
-#define SH_FSI_LRS_INV         (1 << 2)
-#define SH_FSI_BRS_INV         (1 << 3)
-
-/* B: format mode */
-#define SH_FSI_FMT_MASK                0x000000F0
-#define SH_FSI_FMT_DAI         (0 << 4)
-#define SH_FSI_FMT_SPDIF       (1 << 4)
-
-/* C: chip specific */
-#define SH_FSI_OPTION_MASK     0x00000F00
-#define SH_FSI_ENABLE_STREAM_MODE      (1 << 8) /* for 16bit data */
-
-/* D:  clock selecter if master mode */
-#define SH_FSI_CLK_MASK                0x0000F000
-#define SH_FSI_CLK_EXTERNAL    (0 << 12)
-#define SH_FSI_CLK_CPG         (1 << 12) /* FSIxCK + FSI-DIV */
-
-/*
- * set_rate return value
- *
- * see ACKMD/BPFMD on
- *     ACK_MD (FSI2)
- *     CKG1   (FSI)
- *
- * err         : return value <  0
- * no change   : return value == 0
- * change xMD  : return value >  0
- *
- * 0x-00000AB
- *
- * A:  ACKMD value
- * B:  BPFMD value
- */
-
-#define SH_FSI_ACKMD_MASK      (0xF << 0)
-#define SH_FSI_ACKMD_512       (1 << 0)
-#define SH_FSI_ACKMD_256       (2 << 0)
-#define SH_FSI_ACKMD_128       (3 << 0)
-#define SH_FSI_ACKMD_64                (4 << 0)
-#define SH_FSI_ACKMD_32                (5 << 0)
-
-#define SH_FSI_BPFMD_MASK      (0xF << 4)
-#define SH_FSI_BPFMD_512       (1 << 4)
-#define SH_FSI_BPFMD_256       (2 << 4)
-#define SH_FSI_BPFMD_128       (3 << 4)
-#define SH_FSI_BPFMD_64                (4 << 4)
-#define SH_FSI_BPFMD_32                (5 << 4)
-#define SH_FSI_BPFMD_16                (6 << 4)
+#define SH_FSI_FMT_SPDIF               (1 << 0) /* spdif for HDMI */
+#define SH_FSI_ENABLE_STREAM_MODE      (1 << 1) /* for 16bit data */
+#define SH_FSI_CLK_CPG                 (1 << 2) /* FSIxCK + FSI-DIV */
 
 struct sh_fsi_port_info {
        unsigned long flags;
        int tx_id;
        int rx_id;
-       int (*set_rate)(struct device *dev, int rate, int enable);
 };
 
 struct sh_fsi_platform_info {
index 4b62b8dc6a4fd2baddef3e1c7f60b24c9ed2a1ae..6c74527d4926b3e476428fb6327652d9ca22dfbb 100644 (file)
 
 #include <sound/soc.h>
 
-struct asoc_simple_dai_init_info {
+struct asoc_simple_dai {
+       const char *name;
        unsigned int fmt;
-       unsigned int cpu_daifmt;
-       unsigned int codec_daifmt;
        unsigned int sysclk;
 };
 
 struct asoc_simple_card_info {
        const char *name;
        const char *card;
-       const char *cpu_dai;
        const char *codec;
        const char *platform;
-       const char *codec_dai;
-       struct asoc_simple_dai_init_info *init; /* for snd_link.init */
+
+       unsigned int daifmt;
+       struct asoc_simple_dai cpu_dai;
+       struct asoc_simple_dai codec_dai;
 
        /* used in simple-card.c */
        struct snd_soc_dai_link snd_link;
index 3953cea0ecfbf4255525bc00e821d5b031564070..689d0490d50d6781aeeaec847626be78449a9986 100644 (file)
@@ -53,7 +53,7 @@ struct snd_compr_stream;
  * Specifies whether the DAI can also support inverted clocks for the specified
  * format.
  */
-#define SND_SOC_DAIFMT_NB_NF           (1 << 8) /* normal bit clock + frame */
+#define SND_SOC_DAIFMT_NB_NF           (0 << 8) /* normal bit clock + frame */
 #define SND_SOC_DAIFMT_NB_IF           (2 << 8) /* normal BCLK + inv FRM */
 #define SND_SOC_DAIFMT_IB_NF           (3 << 8) /* invert BCLK + nor FRM */
 #define SND_SOC_DAIFMT_IB_IF           (4 << 8) /* invert BCLK + FRM */
@@ -91,6 +91,24 @@ struct snd_compr_stream;
                                SNDRV_PCM_FMTBIT_S32_LE |\
                                SNDRV_PCM_FMTBIT_S32_BE)
 
+#define SND_SOC_DAI_CONNECT(xname, xcodec, xplatform, xcodec_dai, xcpu_dai) \
+       .name = xname, .codec_name = xcodec, \
+       .platform_name = xplatform, .codec_dai_name = xcodec_dai,\
+       .cpu_dai_name = xcpu_dai
+#define SND_SOC_DAI_OPS(xops, xinit) \
+       .ops = xops, .init = xinit
+#define SND_SOC_DAI_IGNORE_SUSPEND .ignore_suspend = 1
+#define SND_SOC_DAI_IGNORE_PMDOWN .ignore_pmdown_time = 1
+#define SND_SOC_DAI_BE_LINK(xid, xfixup) \
+       .be_id = xid, .be_hw_params_fixup = xfixup, .no_pcm = 1
+#define SND_SOC_DAI_FE_LINK(xname, xplatform, xcpu_dai) \
+       .name = xname, .platform_name = xplatform, \
+       .cpu_dai_name = xcpu_dai, .dynamic = 1, \
+       .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai"
+#define SND_SOC_DAI_FE_TRIGGER(xplay, xcapture) \
+       .trigger = {xplay, xcapture}
+#define SND_SOC_DAI_LINK_NO_HOST       .no_host_mode = 1
+
 struct snd_soc_dai_driver;
 struct snd_soc_dai;
 struct snd_ac97_bus_ops;
@@ -102,6 +120,7 @@ void snd_soc_unregister_dai(struct device *dev);
 int snd_soc_register_dais(struct device *dev,
                struct snd_soc_dai_driver *dai_drv, size_t count);
 void snd_soc_unregister_dais(struct device *dev, size_t count);
+void snd_soc_card_reset_dai_links(struct snd_soc_card *card);
 
 /* Digital Audio Interface clocking API.*/
 int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
@@ -293,4 +312,22 @@ static inline void *snd_soc_dai_get_drvdata(struct snd_soc_dai *dai)
        return dev_get_drvdata(dai->dev);
 }
 
+/* Backend DAI PCM ops */
+int snd_soc_dai_startup(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai);
+
+void snd_soc_dai_shutdown(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai);
+
+int snd_soc_dai_hw_params(struct snd_pcm_substream * substream,
+               struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai);
+
+int snd_soc_dai_hw_free(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai);
+
+int snd_soc_dai_prepare(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai);
+
+int snd_soc_dai_trigger(struct snd_pcm_substream *substream,
+       int cmd, struct snd_soc_dai *dai);
 #endif
index e1ef63d4a5c453b17630b279d5780b32f1e722c3..d17b1eab64aa825c26f0e16a2881a204230cc638 100644 (file)
 
 #include <linux/types.h>
 #include <sound/control.h>
+#include <uapi/sound/asoc.h>
 
 struct device;
-
-/* widget has no PM register bit */
-#define SND_SOC_NOPM   -1
-
-/*
- * SoC dynamic audio power management
- *
- * We can have up to 4 power domains
- *  1. Codec domain - VREF, VMID
- *     Usually controlled at codec probe/remove, although can be set
- *     at stream time if power is not needed for sidetone, etc.
- *  2. Platform/Machine domain - physically connected inputs and outputs
- *     Is platform/machine and user action specific, is set in the machine
- *     driver and by userspace e.g when HP are inserted
- *  3. Path domain - Internal codec path mixers
- *     Are automatically set when mixer and mux settings are
- *     changed by the user.
- *  4. Stream domain - DAC's and ADC's.
- *     Enabled when stream playback/capture is started.
- */
-
-/* codec domain */
-#define SND_SOC_DAPM_VMID(wname) \
-{      .id = snd_soc_dapm_vmid, .name = wname, .kcontrol_news = NULL, \
-       .num_kcontrols = 0}
-
-/* platform domain */
-#define SND_SOC_DAPM_SIGGEN(wname) \
-{      .id = snd_soc_dapm_siggen, .name = wname, .kcontrol_news = NULL, \
-       .num_kcontrols = 0, .reg = SND_SOC_NOPM }
-#define SND_SOC_DAPM_INPUT(wname) \
-{      .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \
-       .num_kcontrols = 0, .reg = SND_SOC_NOPM }
-#define SND_SOC_DAPM_OUTPUT(wname) \
-{      .id = snd_soc_dapm_output, .name = wname, .kcontrol_news = NULL, \
-       .num_kcontrols = 0, .reg = SND_SOC_NOPM }
-#define SND_SOC_DAPM_MIC(wname, wevent) \
-{      .id = snd_soc_dapm_mic, .name = wname, .kcontrol_news = NULL, \
-       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
-       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
-#define SND_SOC_DAPM_HP(wname, wevent) \
-{      .id = snd_soc_dapm_hp, .name = wname, .kcontrol_news = NULL, \
-       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
-       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
-#define SND_SOC_DAPM_SPK(wname, wevent) \
-{      .id = snd_soc_dapm_spk, .name = wname, .kcontrol_news = NULL, \
-       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
-       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
-#define SND_SOC_DAPM_LINE(wname, wevent) \
-{      .id = snd_soc_dapm_line, .name = wname, .kcontrol_news = NULL, \
-       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
-       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
-
-/* path domain */
-#define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\
-        wcontrols, wncontrols) \
-{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
-#define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\
-        wcontrols, wncontrols) \
-{      .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
-#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
-        wcontrols, wncontrols)\
-{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
-#define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \
-        wcontrols, wncontrols)\
-{       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
-       .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
-       .num_kcontrols = wncontrols}
-#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
-{      .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0}
-#define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \
-{      .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
-#define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
-{      .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
-#define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \
-{      .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
-#define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
-{      .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \
-       .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
-       .num_kcontrols = 1}
-
-/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
-#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
-        wcontrols) \
-{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
-#define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \
-        wcontrols)\
-{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
-#define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \
-        wcontrols)\
-{       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
-       .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
-       .num_kcontrols = ARRAY_SIZE(wcontrols)}
-
-/* path domain with event - event handler must return 0 for success */
-#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
-       wncontrols, wevent, wflags) \
-{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
-       .event = wevent, .event_flags = wflags}
-#define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \
-       wncontrols, wevent, wflags) \
-{      .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
-       .event = wevent, .event_flags = wflags}
-#define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
-       wncontrols, wevent, wflags) \
-{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
-       .event = wevent, .event_flags = wflags}
-#define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \
-       wcontrols, wncontrols, wevent, wflags) \
-{       .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, \
-       .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags}
-#define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \
-       wevent, wflags) \
-{      .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
-       .event = wevent, .event_flags = wflags}
-#define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
-       wevent, wflags) \
-{      .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
-       .event = wevent, .event_flags = wflags}
-#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
-       wevent, wflags) \
-{      .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
-       .event = wevent, .event_flags = wflags}
-
-/* additional sequencing control within an event type */
-#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
-       wevent, wflags) \
-{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .event = wevent, .event_flags = wflags, \
-       .subseq = wsubseq}
-#define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \
-       wflags) \
-{      .id = snd_soc_dapm_supply, .name = wname, .reg = wreg,  \
-       .shift = wshift, .invert = winvert, .event = wevent, \
-       .event_flags = wflags, .subseq = wsubseq}
-
-/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
-#define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
-       wevent, wflags) \
-{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
-       .event = wevent, .event_flags = wflags}
-#define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
-       wevent, wflags) \
-{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
-       .event = wevent, .event_flags = wflags}
-#define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \
-       wcontrols, wevent, wflags) \
-{       .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, \
-       .num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags}
-
-/* events that are pre and post DAPM */
-#define SND_SOC_DAPM_PRE(wname, wevent) \
-{      .id = snd_soc_dapm_pre, .name = wname, .kcontrol_news = NULL, \
-       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
-       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD}
-#define SND_SOC_DAPM_POST(wname, wevent) \
-{      .id = snd_soc_dapm_post, .name = wname, .kcontrol_news = NULL, \
-       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
-       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
-
-/* stream domain */
-#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
-{      .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
-       .reg = wreg, .shift = wshift, .invert = winvert }
-#define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \
-                             wevent, wflags)                           \
-{      .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
-       .reg = wreg, .shift = wshift, .invert = winvert, \
-       .event = wevent, .event_flags = wflags }
-#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \
-{      .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
-       .reg = wreg, .shift = wshift, .invert = winvert }
-#define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \
-                            wevent, wflags)                            \
-{      .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
-       .reg = wreg, .shift = wshift, .invert = winvert, \
-       .event = wevent, .event_flags = wflags }
-#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
-{      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
-       .shift = wshift, .invert = winvert}
-#define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \
-                          wevent, wflags)                              \
-{      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
-       .shift = wshift, .invert = winvert, \
-       .event = wevent, .event_flags = wflags}
-#define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \
-{      .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
-       .shift = wshift, .invert = winvert}
-#define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \
-                          wevent, wflags)                              \
-{      .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
-       .shift = wshift, .invert = winvert, \
-       .event = wevent, .event_flags = wflags}
-#define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \
-{      .id = snd_soc_dapm_clock_supply, .name = wname, \
-       .reg = SND_SOC_NOPM, .event = dapm_clock_event, \
-       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }
-
-/* generic widgets */
-#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
-{      .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
-       .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
-       .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
-       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
-#define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \
-{      .id = snd_soc_dapm_supply, .name = wname, .reg = wreg,  \
-       .shift = wshift, .invert = winvert, .event = wevent, \
-       .event_flags = wflags}
-#define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay, wflags)       \
-{      .id = snd_soc_dapm_regulator_supply, .name = wname, \
-       .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \
-       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
-       .invert = wflags}
-
-
-/* dapm kcontrol types */
-#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .info = snd_soc_info_volsw, \
-       .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
-       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
-#define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .info = snd_soc_info_volsw, \
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
-       .tlv.p = (tlv_array), \
-       .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
-       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
-#define SOC_DAPM_ENUM(xname, xenum) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .info = snd_soc_info_enum_double, \
-       .get = snd_soc_dapm_get_enum_double, \
-       .put = snd_soc_dapm_put_enum_double, \
-       .private_value = (unsigned long)&xenum }
-#define SOC_DAPM_ENUM_VIRT(xname, xenum)                   \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .info = snd_soc_info_enum_double, \
-       .get = snd_soc_dapm_get_enum_virt, \
-       .put = snd_soc_dapm_put_enum_virt, \
-       .private_value = (unsigned long)&xenum }
-#define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .info = snd_soc_info_enum_double, \
-       .get = xget, \
-       .put = xput, \
-       .private_value = (unsigned long)&xenum }
-#define SOC_DAPM_VALUE_ENUM(xname, xenum) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .info = snd_soc_info_enum_double, \
-       .get = snd_soc_dapm_get_value_enum_double, \
-       .put = snd_soc_dapm_put_value_enum_double, \
-       .private_value = (unsigned long)&xenum }
-#define SOC_DAPM_PIN_SWITCH(xname) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \
-       .info = snd_soc_dapm_info_pin_switch, \
-       .get = snd_soc_dapm_get_pin_switch, \
-       .put = snd_soc_dapm_put_pin_switch, \
-       .private_value = (unsigned long)xname }
-
 /* dapm stream operations */
 #define SND_SOC_DAPM_STREAM_NOP                        0x0
 #define SND_SOC_DAPM_STREAM_START              0x1
@@ -386,6 +109,8 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
 /* dapm events */
 void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
        int event);
+void snd_soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
+       int event);
 void snd_soc_dapm_shutdown(struct snd_soc_card *card);
 
 /* external DAPM widget events */
@@ -422,38 +147,6 @@ void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm);
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
        struct snd_soc_dapm_widget_list **list);
 
-/* dapm widget types */
-enum snd_soc_dapm_type {
-       snd_soc_dapm_input = 0,         /* input pin */
-       snd_soc_dapm_output,            /* output pin */
-       snd_soc_dapm_mux,                       /* selects 1 analog signal from many inputs */
-       snd_soc_dapm_virt_mux,                  /* virtual version of snd_soc_dapm_mux */
-       snd_soc_dapm_value_mux,                 /* selects 1 analog signal from many inputs */
-       snd_soc_dapm_mixer,                     /* mixes several analog signals together */
-       snd_soc_dapm_mixer_named_ctl,           /* mixer with named controls */
-       snd_soc_dapm_pga,                       /* programmable gain/attenuation (volume) */
-       snd_soc_dapm_out_drv,                   /* output driver */
-       snd_soc_dapm_adc,                       /* analog to digital converter */
-       snd_soc_dapm_dac,                       /* digital to analog converter */
-       snd_soc_dapm_micbias,           /* microphone bias (power) */
-       snd_soc_dapm_mic,                       /* microphone */
-       snd_soc_dapm_hp,                        /* headphones */
-       snd_soc_dapm_spk,                       /* speaker */
-       snd_soc_dapm_line,                      /* line input/output */
-       snd_soc_dapm_switch,            /* analog switch */
-       snd_soc_dapm_vmid,                      /* codec bias/vmid - to minimise pops */
-       snd_soc_dapm_pre,                       /* machine specific pre widget - exec first */
-       snd_soc_dapm_post,                      /* machine specific post widget - exec last */
-       snd_soc_dapm_supply,            /* power/clock supply */
-       snd_soc_dapm_regulator_supply,  /* external regulator */
-       snd_soc_dapm_clock_supply,      /* external clock */
-       snd_soc_dapm_aif_in,            /* audio interface input */
-       snd_soc_dapm_aif_out,           /* audio interface output */
-       snd_soc_dapm_siggen,            /* signal generator */
-       snd_soc_dapm_dai,               /* link to DAI structure */
-       snd_soc_dapm_dai_link,          /* link between two DAI structures */
-};
-
 enum snd_soc_dapm_subclass {
        SND_SOC_DAPM_CLASS_INIT         = 0,
        SND_SOC_DAPM_CLASS_RUNTIME      = 1,
@@ -496,6 +189,8 @@ struct snd_soc_dapm_path {
        struct list_head list_source;
        struct list_head list_sink;
        struct list_head list;
+
+       u32 index;
 };
 
 /* dapm widget */
@@ -505,6 +200,7 @@ struct snd_soc_dapm_widget {
        const char *sname;      /* stream name */
        struct snd_soc_codec *codec;
        struct snd_soc_platform *platform;
+       struct snd_soc_dai *dai;
        struct list_head list;
        struct snd_soc_dapm_context *dapm;
 
@@ -529,6 +225,7 @@ struct snd_soc_dapm_widget {
        unsigned char ignore_suspend:1;         /* kept enabled over suspend */
        unsigned char new_power:1;              /* power from this run */
        unsigned char power_checked:1;          /* power checked this run */
+       unsigned char dai_endpoint:1;   /* DAI end point for current stream event */
        int subseq;                             /* sort within widget type */
 
        int (*power_check)(struct snd_soc_dapm_widget *w);
@@ -541,6 +238,8 @@ struct snd_soc_dapm_widget {
        int num_kcontrols;
        const struct snd_kcontrol_new *kcontrol_news;
        struct snd_kcontrol **kcontrols;
+       u32 index;
+       unsigned char kcontrol_enum:1;  /* this widget is an enum kcontrol*/
 
        /* widget input and outputs */
        struct list_head sources;
diff --git a/include/sound/soc-fw.h b/include/sound/soc-fw.h
new file mode 100644 (file)
index 0000000..546b7eb
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * linux/sound/soc-fw.h -- ALSA SoC Firmware Controls and DAPM
+ *
+ * Copyright:  2012 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Simple file API to load FW that includes mixers, coefficients, DAPM graphs,
+ * algorithms, equalisers, DAIs, widgets etc.
+ */
+
+#ifndef __LINUX_SND_SOC_FW_H
+#define __LINUX_SND_SOC_FW_H
+
+#include <uapi/sound/asoc.h>
+
+struct firmware;
+
+/*
+ * Kcontrol operations - used to map handlers onto firmware based controls.
+ */
+struct snd_soc_fw_kcontrol_ops {
+       u32 id;
+       int (*get)(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol);
+       int (*put)(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol);
+       int (*info)(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo);
+};
+
+/*
+ * Public API - Used by component drivers to load new mixers, DAPM, vendor
+ * specific data.
+ */
+struct snd_soc_fw_codec_ops {
+
+       /* external kcontrol init - can be used to set ext funcs + pdata */
+       int (*control_load) (struct snd_soc_codec *, struct snd_kcontrol_new *);
+
+       /* external widget init - can be used to set ext funcs + pdata */
+       int (*widget_load) (struct snd_soc_codec *, struct snd_soc_dapm_widget *);
+
+       /* callback to handle vendor data */
+       int (*vendor_load) (struct snd_soc_codec *, struct snd_soc_fw_hdr *);
+       int (*vendor_unload) (struct snd_soc_codec *, struct snd_soc_fw_hdr *);
+
+       /* completion - called at completion of firmware loading */
+       void (*complete) (struct snd_soc_codec *);
+
+       /* kcontrols operations */
+       const struct snd_soc_fw_kcontrol_ops *io_ops;
+       int io_ops_count;
+};
+
+struct snd_soc_fw_platform_ops {
+
+       /* external kcontrol init - can be used to set ext funcs + pdata */
+       int (*control_load) (struct snd_soc_platform *, struct snd_kcontrol_new *);
+
+       /* external widget init - can be used to set ext funcs + pdata */
+       int (*widget_load) (struct snd_soc_platform *, struct snd_soc_dapm_widget *);
+
+       /* callback to handle vendor data */
+       int (*vendor_load) (struct snd_soc_platform *, struct snd_soc_fw_hdr *);
+       int (*vendor_unload) (struct snd_soc_platform *, struct snd_soc_fw_hdr *);
+
+       /* completion - called at completion of firmware loading */
+       void (*complete) (struct snd_soc_platform *);
+
+       /* kcontrols operations */
+       const struct snd_soc_fw_kcontrol_ops *io_ops;
+       int io_ops_count;
+};
+
+struct snd_soc_fw_card_ops {
+
+       /* external kcontrol init - can be used to set ext funcs + pdata */
+       int (*control_load) (struct snd_soc_card *, struct snd_kcontrol_new *);
+
+       /* external widget init - can be used to set ext funcs + pdata */
+       int (*widget_load) (struct snd_soc_card *, struct snd_soc_dapm_widget *);
+
+       /* callback to handle vendor data */
+       int (*vendor_load) (struct snd_soc_card *, struct snd_soc_fw_hdr *);
+       int (*vendor_unload) (struct snd_soc_card *, struct snd_soc_fw_hdr *);
+
+       /* completion */
+       void (*complete) (struct snd_soc_card *);
+
+       /* kcontrols operations */
+       const struct snd_soc_fw_kcontrol_ops *io_ops;
+       int io_ops_count;
+};
+
+/* gets a pointer to data from the firmware block header */
+static inline const void *snd_soc_fw_get_data(struct snd_soc_fw_hdr *hdr)
+{
+       const void *ptr = hdr;
+
+       return ptr + sizeof(*hdr);
+}
+
+/* Firmware loading for component drivers */
+int snd_soc_fw_load_card(struct snd_soc_card *card,
+       struct snd_soc_fw_card_ops *ops, const struct firmware *fw,
+       u32 index);
+int snd_soc_fw_load_platform(struct snd_soc_platform *platform,
+       struct snd_soc_fw_platform_ops *ops, const struct firmware *fw,
+       u32 index);
+int snd_soc_fw_load_codec(struct snd_soc_codec *codec,
+       struct snd_soc_fw_codec_ops *ops, const struct firmware *fw,
+       u32 index);
+
+#define SND_SOC_FW_INDEX_ALL   0       /* index that matches all FW objects */
+
+/* Firmware based dynamic widget and assoc kcontrol removal */
+void snd_soc_fw_dcontrols_remove_widgets(struct snd_soc_dapm_context *dapm,
+       u32 index);
+void snd_soc_fw_dcontrols_remove_widget(struct snd_soc_dapm_widget *w);
+
+/* Firmware based dynamic kcontrol removal for components */
+void snd_soc_fw_dcontrols_remove_codec(struct snd_soc_codec *codec, u32 index);
+void snd_soc_fw_dcontrols_remove_platform(struct snd_soc_platform *platform, u32 index);
+void snd_soc_fw_dcontrols_remove_card(struct snd_soc_card *soc_card, u32 index);
+int snd_soc_fw_dcontrols_remove_all(struct snd_soc_card *soc_card, u32 index);
+
+#endif
index bc56738cb1091b8319b2413fe7e6b2f105ba0ac8..8671076f56710fd9266dd2494a48cfb3c6e79d30 100644 (file)
 #include <sound/compress_driver.h>
 #include <sound/control.h>
 #include <sound/ac97_codec.h>
-
-/*
- * Convenience kcontrol builders
- */
-#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert) \
-       ((unsigned long)&(struct soc_mixer_control) \
-       {.reg = xreg, .rreg = xreg, .shift = shift_left, \
-       .rshift = shift_right, .max = xmax, .platform_max = xmax, \
-       .invert = xinvert})
-#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \
-       SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert)
-#define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \
-       ((unsigned long)&(struct soc_mixer_control) \
-       {.reg = xreg, .max = xmax, .platform_max = xmax, .invert = xinvert})
-#define SOC_DOUBLE_R_VALUE(xlreg, xrreg, xshift, xmax, xinvert) \
-       ((unsigned long)&(struct soc_mixer_control) \
-       {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
-       .max = xmax, .platform_max = xmax, .invert = xinvert})
-#define SOC_DOUBLE_R_RANGE_VALUE(xlreg, xrreg, xshift, xmin, xmax, xinvert) \
-       ((unsigned long)&(struct soc_mixer_control) \
-       {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
-       .min = xmin, .max = xmax, .platform_max = xmax, .invert = xinvert})
-#define SOC_SINGLE(xname, reg, shift, max, invert) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
-       .put = snd_soc_put_volsw, \
-       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
-#define SOC_SINGLE_RANGE(xname, xreg, xshift, xmin, xmax, xinvert) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
-       .info = snd_soc_info_volsw_range, .get = snd_soc_get_volsw_range, \
-       .put = snd_soc_put_volsw_range, \
-       .private_value = (unsigned long)&(struct soc_mixer_control) \
-               {.reg = xreg, .rreg = xreg, .shift = xshift, \
-                .rshift = xshift,  .min = xmin, .max = xmax, \
-                .platform_max = xmax, .invert = xinvert} }
-#define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
-                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
-       .tlv.p = (tlv_array), \
-       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
-       .put = snd_soc_put_volsw, \
-       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
-#define SOC_SINGLE_SX_TLV(xname, xreg, xshift, xmin, xmax, tlv_array) \
-{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
-       SNDRV_CTL_ELEM_ACCESS_READWRITE, \
-       .tlv.p  = (tlv_array),\
-       .info = snd_soc_info_volsw, \
-       .get = snd_soc_get_volsw_sx,\
-       .put = snd_soc_put_volsw_sx, \
-       .private_value = (unsigned long)&(struct soc_mixer_control) \
-               {.reg = xreg, .rreg = xreg, \
-               .shift = xshift, .rshift = xshift, \
-               .max = xmax, .min = xmin} }
-#define SOC_SINGLE_RANGE_TLV(xname, xreg, xshift, xmin, xmax, xinvert, tlv_array) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
-                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
-       .tlv.p = (tlv_array), \
-       .info = snd_soc_info_volsw_range, \
-       .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \
-       .private_value = (unsigned long)&(struct soc_mixer_control) \
-               {.reg = xreg, .rreg = xreg, .shift = xshift, \
-                .rshift = xshift, .min = xmin, .max = xmax, \
-                .platform_max = xmax, .invert = xinvert} }
-#define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
-       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
-       .put = snd_soc_put_volsw, \
-       .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
-                                         max, invert) }
-#define SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
-       .info = snd_soc_info_volsw, \
-       .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
-       .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
-                                           xmax, xinvert) }
-#define SOC_DOUBLE_R_RANGE(xname, reg_left, reg_right, xshift, xmin, \
-                          xmax, xinvert)               \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
-       .info = snd_soc_info_volsw_range, \
-       .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \
-       .private_value = SOC_DOUBLE_R_RANGE_VALUE(reg_left, reg_right, \
-                                           xshift, xmin, xmax, xinvert) }
-#define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
-                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
-       .tlv.p = (tlv_array), \
-       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
-       .put = snd_soc_put_volsw, \
-       .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
-                                         max, invert) }
-#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
-                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
-       .tlv.p = (tlv_array), \
-       .info = snd_soc_info_volsw, \
-       .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
-       .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
-                                           xmax, xinvert) }
-#define SOC_DOUBLE_R_RANGE_TLV(xname, reg_left, reg_right, xshift, xmin, \
-                              xmax, xinvert, tlv_array)                \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
-                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
-       .tlv.p = (tlv_array), \
-       .info = snd_soc_info_volsw_range, \
-       .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \
-       .private_value = SOC_DOUBLE_R_RANGE_VALUE(reg_left, reg_right, \
-                                           xshift, xmin, xmax, xinvert) }
-#define SOC_DOUBLE_R_SX_TLV(xname, xreg, xrreg, xshift, xmin, xmax, tlv_array) \
-{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
-       SNDRV_CTL_ELEM_ACCESS_READWRITE, \
-       .tlv.p  = (tlv_array), \
-       .info = snd_soc_info_volsw, \
-       .get = snd_soc_get_volsw_sx, \
-       .put = snd_soc_put_volsw_sx, \
-       .private_value = (unsigned long)&(struct soc_mixer_control) \
-               {.reg = xreg, .rreg = xrreg, \
-               .shift = xshift, .rshift = xshift, \
-               .max = xmax, .min = xmin} }
-#define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
-{      .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
-                 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
-       .tlv.p  = (tlv_array), \
-       .info   = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \
-       .put    = snd_soc_put_volsw_s8, \
-       .private_value = (unsigned long)&(struct soc_mixer_control) \
-               {.reg = xreg, .min = xmin, .max = xmax, \
-                .platform_max = xmax} }
-#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmax, xtexts) \
-{      .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
-       .max = xmax, .texts = xtexts, \
-       .mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0}
-#define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) \
-       SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts)
-#define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \
-{      .max = xmax, .texts = xtexts }
-#define SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xmax, xtexts, xvalues) \
-{      .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
-       .mask = xmask, .max = xmax, .texts = xtexts, .values = xvalues}
-#define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xmax, xtexts, xvalues) \
-       SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xmax, xtexts, xvalues)
-#define SOC_ENUM(xname, xenum) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
-       .info = snd_soc_info_enum_double, \
-       .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
-       .private_value = (unsigned long)&xenum }
-#define SOC_VALUE_ENUM(xname, xenum) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
-       .info = snd_soc_info_enum_double, \
-       .get = snd_soc_get_value_enum_double, \
-       .put = snd_soc_put_value_enum_double, \
-       .private_value = (unsigned long)&xenum }
-#define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\
-        xhandler_get, xhandler_put) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .info = snd_soc_info_volsw, \
-       .get = xhandler_get, .put = xhandler_put, \
-       .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
-#define SOC_DOUBLE_EXT(xname, reg, shift_left, shift_right, max, invert,\
-        xhandler_get, xhandler_put) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
-       .info = snd_soc_info_volsw, \
-       .get = xhandler_get, .put = xhandler_put, \
-       .private_value = \
-               SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert) }
-#define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\
-        xhandler_get, xhandler_put, tlv_array) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
-                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
-       .tlv.p = (tlv_array), \
-       .info = snd_soc_info_volsw, \
-       .get = xhandler_get, .put = xhandler_put, \
-       .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
-#define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\
-        xhandler_get, xhandler_put, tlv_array) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
-                SNDRV_CTL_ELEM_ACCESS_READWRITE, \
-       .tlv.p = (tlv_array), \
-       .info = snd_soc_info_volsw, \
-       .get = xhandler_get, .put = xhandler_put, \
-       .private_value = SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, \
-                                         xmax, xinvert) }
-#define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\
-        xhandler_get, xhandler_put, tlv_array) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
-       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
-                SNDRV_CTL_ELEM_ACCESS_READWRITE, \
-       .tlv.p = (tlv_array), \
-       .info = snd_soc_info_volsw, \
-       .get = xhandler_get, .put = xhandler_put, \
-       .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
-                                           xmax, xinvert) }
-#define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .info = snd_soc_info_bool_ext, \
-       .get = xhandler_get, .put = xhandler_put, \
-       .private_value = xdata }
-#define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .info = snd_soc_info_enum_ext, \
-       .get = xhandler_get, .put = xhandler_put, \
-       .private_value = (unsigned long)&xenum }
-
-#define SND_SOC_BYTES(xname, xbase, xregs)                   \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,   \
-       .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
-       .put = snd_soc_bytes_put, .private_value =            \
-               ((unsigned long)&(struct soc_bytes)           \
-               {.base = xbase, .num_regs = xregs }) }
-
-#define SND_SOC_BYTES_MASK(xname, xbase, xregs, xmask)       \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,   \
-       .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
-       .put = snd_soc_bytes_put, .private_value =            \
-               ((unsigned long)&(struct soc_bytes)           \
-               {.base = xbase, .num_regs = xregs,            \
-                .mask = xmask }) }
-
-#define SOC_SINGLE_XR_SX(xname, xregbase, xregcount, xnbits, \
-               xmin, xmax, xinvert) \
-{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
-       .info = snd_soc_info_xr_sx, .get = snd_soc_get_xr_sx, \
-       .put = snd_soc_put_xr_sx, \
-       .private_value = (unsigned long)&(struct soc_mreg_control) \
-               {.regbase = xregbase, .regcount = xregcount, .nbits = xnbits, \
-               .invert = xinvert, .min = xmin, .max = xmax} }
-
-#define SOC_SINGLE_STROBE(xname, xreg, xshift, xinvert) \
-       SOC_SINGLE_EXT(xname, xreg, xshift, 1, xinvert, \
-               snd_soc_get_strobe, snd_soc_put_strobe)
-
-/*
- * Simplified versions of above macros, declaring a struct and calculating
- * ARRAY_SIZE internally
- */
-#define SOC_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xtexts) \
-       struct soc_enum name = SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, \
-                                               ARRAY_SIZE(xtexts), xtexts)
-#define SOC_ENUM_SINGLE_DECL(name, xreg, xshift, xtexts) \
-       SOC_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xtexts)
-#define SOC_ENUM_SINGLE_EXT_DECL(name, xtexts) \
-       struct soc_enum name = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(xtexts), xtexts)
-#define SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xmask, xtexts, xvalues) \
-       struct soc_enum name = SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, \
-                                                       ARRAY_SIZE(xtexts), xtexts, xvalues)
-#define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \
-       SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues)
+#include <uapi/sound/asoc.h>
 
 /*
  * Component probe and remove ordering levels for components with runtime
 #define SND_SOC_COMP_ORDER_LATE                1
 #define SND_SOC_COMP_ORDER_LAST                2
 
+#define snd_soc_get_enum_text(soc_enum, idx) \
+       (soc_enum->texts ? soc_enum->texts[idx] : soc_enum->dtexts[idx])
+
+#define snd_soc_get_enum_text(soc_enum, idx) \
+       (soc_enum->texts ? soc_enum->texts[idx] : soc_enum->dtexts[idx])
+
 /*
  * Bias levels
  *
@@ -311,6 +62,7 @@ enum snd_soc_bias_level {
        SND_SOC_BIAS_ON = 3,
 };
 
+
 struct device_node;
 struct snd_jack;
 struct snd_soc_card;
@@ -367,6 +119,8 @@ int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
 
 int snd_soc_register_card(struct snd_soc_card *card);
 int snd_soc_unregister_card(struct snd_soc_card *card);
+int snd_soc_card_new_dai_links(struct snd_soc_card *card,
+       struct snd_soc_dai_link *new, int count);
 int snd_soc_suspend(struct device *dev);
 int snd_soc_resume(struct device *dev);
 int snd_soc_poweroff(struct device *dev);
@@ -709,6 +463,10 @@ struct snd_soc_codec {
        struct snd_soc_dapm_context dapm;
        unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */
 
+       /* dynamic mixer and enum controls */
+       struct list_head dmixers;
+       struct list_head denums;
+
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_codec_root;
        struct dentry *debugfs_reg;
@@ -833,6 +591,10 @@ struct snd_soc_platform {
        struct list_head list;
        struct list_head card_list;
 
+       /* dynamic mixer and enum controls */
+       struct list_head dmixers;
+       struct list_head denums;
+
        struct snd_soc_dapm_context dapm;
 
 #ifdef CONFIG_DEBUG_FS
@@ -898,6 +660,9 @@ struct snd_soc_dai_link {
        /* pmdown_time is ignored at stop */
        unsigned int ignore_pmdown_time:1;
 
+       /* This DAI can support no host IO (no pcm data is copied to from host) */
+       unsigned int no_host_mode:2;
+
        /* codec/machine specific init - e.g. add machine controls */
        int (*init)(struct snd_soc_pcm_runtime *rtd);
 
@@ -906,8 +671,8 @@ struct snd_soc_dai_link {
                        struct snd_pcm_hw_params *params);
 
        /* machine stream operations */
-       struct snd_soc_ops *ops;
-       struct snd_soc_compr_ops *compr_ops;
+       const struct snd_soc_ops *ops;
+       const struct snd_soc_compr_ops *compr_ops;
 };
 
 struct snd_soc_codec_conf {
@@ -1013,6 +778,10 @@ struct snd_soc_card {
        struct list_head dapm_list;
        struct list_head dapm_dirty;
 
+       /* dynamic mixer and enum controls */
+       struct list_head dmixers;
+       struct list_head denums;
+
        /* Generic DAPM context for the card */
        struct snd_soc_dapm_context dapm;
        struct snd_soc_dapm_stats dapm_stats;
@@ -1062,6 +831,11 @@ struct snd_soc_pcm_runtime {
 struct soc_mixer_control {
        int min, max, platform_max;
        unsigned int reg, rreg, shift, rshift, invert;
+
+       /* dynamic controls */
+       struct list_head list;
+       struct snd_kcontrol *dcontrol;
+       int index;
 };
 
 struct soc_bytes {
@@ -1086,6 +860,14 @@ struct soc_enum {
        unsigned int mask;
        const char * const *texts;
        const unsigned int *values;
+
+       /* dynamic enum controls */
+       char **dtexts;
+       unsigned int *dvalues;
+       struct list_head list;
+       struct snd_kcontrol *dcontrol;
+       int index;
+
        void *dapm;
 };
 
@@ -1171,6 +953,8 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card,
                               const char *propname);
 int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                                   const char *propname);
+unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
+                                    const char *prefix);
 
 #include <sound/soc-dai.h>
 
index aa388ca9ec64739f0c8d717be02c164f331d621b..4de81f41c90ffb40f9ce6a6b61d8b83591398d9f 100644 (file)
@@ -15,9 +15,6 @@ struct wm2000_platform_data {
        /** Filename for system-specific image to download to device. */
        const char *download_file;
 
-       /** Divide MCLK by 2 for system clock? */
-       unsigned int mclkdiv2:1;
-
        /** Disable speech clarity enhancement, for use when an
         * external algorithm is used. */
        unsigned int speech_enh_disable:1;
index 0e63cee8d810b7ef33832d6f030e4c2dbe246c63..7969f46f1bb344f55d704ae06b5bcdc609b07b85 100644 (file)
@@ -5,20 +5,17 @@
  *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
  *    Aloisio Almeida Jr <aloisio.almeida@openbossa.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.
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef __LINUX_NFC_H
  *     subsequent CONNECT and CC messages.
  *     If one of the passed parameters is wrong none is set and -EINVAL is
  *     returned.
+ * @NFC_CMD_ENABLE_SE: Enable the physical link to a specific secure element.
+ *     Once enabled a secure element will handle card emulation mode, i.e.
+ *     starting a poll from a device which has a secure element enabled means
+ *     we want to do SE based card emulation.
+ * @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element.
  */
 enum nfc_commands {
        NFC_CMD_UNSPEC,
@@ -86,6 +88,8 @@ enum nfc_commands {
        NFC_EVENT_TM_DEACTIVATED,
        NFC_CMD_LLC_GET_PARAMS,
        NFC_CMD_LLC_SET_PARAMS,
+       NFC_CMD_ENABLE_SE,
+       NFC_CMD_DISABLE_SE,
 /* private: internal use only */
        __NFC_CMD_AFTER_LAST
 };
@@ -114,6 +118,7 @@ enum nfc_commands {
  * @NFC_ATTR_LLC_PARAM_LTO: Link TimeOut parameter
  * @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter
  * @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter
+ * @NFC_ATTR_SE: Available Secure Elements
  */
 enum nfc_attrs {
        NFC_ATTR_UNSPEC,
@@ -134,6 +139,7 @@ enum nfc_attrs {
        NFC_ATTR_LLC_PARAM_LTO,
        NFC_ATTR_LLC_PARAM_RW,
        NFC_ATTR_LLC_PARAM_MIUX,
+       NFC_ATTR_SE,
 /* private: internal use only */
        __NFC_ATTR_AFTER_LAST
 };
@@ -172,6 +178,11 @@ enum nfc_attrs {
 #define NFC_PROTO_NFC_DEP_MASK   (1 << NFC_PROTO_NFC_DEP)
 #define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B)
 
+/* NFC Secure Elements */
+#define NFC_SE_NONE     0x0
+#define NFC_SE_UICC     0x1
+#define NFC_SE_EMBEDDED 0x2
+
 struct sockaddr_nfc {
        sa_family_t sa_family;
        __u32 dev_idx;
index e3e19f8b16f2de81107fd672476e6496632c8524..5b7dbc1ea966b052899ec7b3ae2713da678903e2 100644 (file)
  *     %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
  *     %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
  *     %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
- *     %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT.
+ *     %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
+ *     %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
  *     The channel to use can be set on the interface or be given using the
  *     %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width.
  * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
  *     requests to connect to a specified network but without separating
  *     auth and assoc steps. For this, you need to specify the SSID in a
  *     %NL80211_ATTR_SSID attribute, and can optionally specify the association
- *     IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC,
- *     %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+ *     IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
+ *     %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
  *     %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
  *     %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
  *     Background scan period can optionally be
  * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames
  *     for IBSS or MESH vif.
  *
+ * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control.
+ *     This is to be used with the drivers advertising the support of MAC
+ *     address based access control. List of MAC addresses is passed in
+ *     %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in
+ *     %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it
+ *     is not already done. The new list will replace any existing list. Driver
+ *     will clear its ACL when the list of MAC addresses passed is empty. This
+ *     command is used in AP/P2P GO mode. Driver has to make sure to clear its
+ *     ACL list during %NL80211_CMD_STOP_AP.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -736,6 +747,8 @@ enum nl80211_commands {
 
        NL80211_CMD_SET_MCAST_RATE,
 
+       NL80211_CMD_SET_MAC_ACL,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -958,7 +971,7 @@ enum nl80211_commands {
  * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
  *     used for the association (&enum nl80211_mfp, represented as a u32);
  *     this attribute can be used
- *     with %NL80211_CMD_ASSOCIATE request
+ *     with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests
  *
  * @NL80211_ATTR_STA_FLAGS2: Attribute containing a
  *     &struct nl80211_sta_flag_update.
@@ -1310,6 +1323,19 @@ enum nl80211_commands {
  *     if not given in START_AP 0 is assumed, if not given in SET_BSS
  *     no change is made.
  *
+ * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode
+ *     defined in &enum nl80211_mesh_power_mode.
+ *
+ * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy,
+ *     carried in a u32 attribute
+ *
+ * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for
+ *     MAC ACL.
+ *
+ * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum
+ *     number of MAC addresses that a device can support for MAC
+ *     ACL.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1580,6 +1606,14 @@ enum nl80211_attrs {
        NL80211_ATTR_P2P_CTWINDOW,
        NL80211_ATTR_P2P_OPPPS,
 
+       NL80211_ATTR_LOCAL_MESH_POWER_MODE,
+
+       NL80211_ATTR_ACL_POLICY,
+
+       NL80211_ATTR_MAC_ADDRS,
+
+       NL80211_ATTR_MAC_ACL_MAX,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -1697,6 +1731,9 @@ enum nl80211_iftype {
  *     flag can't be changed, it is only valid while adding a station, and
  *     attempts to change it will silently be ignored (rather than rejected
  *     as errors.)
+ * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
+ *     that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
+ *     previously added station into associated state
  * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
  * @__NL80211_STA_FLAG_AFTER_LAST: internal use
  */
@@ -1708,6 +1745,7 @@ enum nl80211_sta_flags {
        NL80211_STA_FLAG_MFP,
        NL80211_STA_FLAG_AUTHENTICATED,
        NL80211_STA_FLAG_TDLS_PEER,
+       NL80211_STA_FLAG_ASSOCIATED,
 
        /* keep last */
        __NL80211_STA_FLAG_AFTER_LAST,
@@ -1834,6 +1872,10 @@ enum nl80211_sta_bss_param {
  * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update.
  * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32)
  * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64)
+ * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode
+ * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode
+ * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards
+ *     non-peer STA
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -1858,6 +1900,9 @@ enum nl80211_sta_info {
        NL80211_STA_INFO_STA_FLAGS,
        NL80211_STA_INFO_BEACON_LOSS,
        NL80211_STA_INFO_T_OFFSET,
+       NL80211_STA_INFO_LOCAL_PM,
+       NL80211_STA_INFO_PEER_PM,
+       NL80211_STA_INFO_NONPEER_PM,
 
        /* keep last */
        __NL80211_STA_INFO_AFTER_LAST,
@@ -2248,6 +2293,34 @@ enum nl80211_mntr_flags {
        NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1
 };
 
+/**
+ * enum nl80211_mesh_power_mode - mesh power save modes
+ *
+ * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is
+ *     not known or has not been set yet.
+ * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is
+ *     in Awake state all the time.
+ * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will
+ *     alternate between Active and Doze states, but will wake up for
+ *     neighbor's beacons.
+ * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will
+ *     alternate between Active and Doze states, but may not wake up
+ *     for neighbor's beacons.
+ *
+ * @__NL80211_MESH_POWER_AFTER_LAST - internal use
+ * @NL80211_MESH_POWER_MAX - highest possible power save level
+ */
+
+enum nl80211_mesh_power_mode {
+       NL80211_MESH_POWER_UNKNOWN,
+       NL80211_MESH_POWER_ACTIVE,
+       NL80211_MESH_POWER_LIGHT_SLEEP,
+       NL80211_MESH_POWER_DEEP_SLEEP,
+
+       __NL80211_MESH_POWER_AFTER_LAST,
+       NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1
+};
+
 /**
  * enum nl80211_meshconf_params - mesh configuration parameters
  *
@@ -2342,6 +2415,11 @@ enum nl80211_mntr_flags {
  *     (in TUs) during which a mesh STA can send only one Action frame
  *     containing a PREQ element for root path confirmation.
  *
+ * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links.
+ *     type &enum nl80211_mesh_power_mode (u32)
+ *
+ * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
+ *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_meshconf_params {
@@ -2371,6 +2449,8 @@ enum nl80211_meshconf_params {
        NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
        NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
        NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
+       NL80211_MESHCONF_POWER_MODE,
+       NL80211_MESHCONF_AWAKE_WINDOW,
 
        /* keep last */
        __NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -2933,6 +3013,8 @@ enum nl80211_iface_limit_attrs {
  *     the infrastructure network's beacon interval.
  * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many
  *     different channels may be used within this group.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap
+ *     of supported channel widths for radar detection.
  * @NUM_NL80211_IFACE_COMB: number of attributes
  * @MAX_NL80211_IFACE_COMB: highest attribute number
  *
@@ -2965,6 +3047,7 @@ enum nl80211_if_combination_attrs {
        NL80211_IFACE_COMB_MAXNUM,
        NL80211_IFACE_COMB_STA_AP_BI_MATCH,
        NL80211_IFACE_COMB_NUM_CHANNELS,
+       NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
 
        /* keep last */
        NUM_NL80211_IFACE_COMB,
@@ -3140,6 +3223,17 @@ enum nl80211_ap_sme_features {
  *     setting
  * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic
  *     powersave
+ * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state
+ *     transitions for AP clients. Without this flag (and if the driver
+ *     doesn't have the AP SME in the device) the driver supports adding
+ *     stations only when they're associated and adds them in associated
+ *     state (to later be transitioned into authorized), with this flag
+ *     they should be added before even sending the authentication reply
+ *     and then transitioned into authenticated, associated and authorized
+ *     states using station flags.
+ *     Note that even for drivers that support this, the default is to add
+ *     stations in authenticated/associated state, so to add unauthenticated
+ *     stations the authenticated/associated bits have to be set in the mask.
  */
 enum nl80211_feature_flags {
        NL80211_FEATURE_SK_TX_STATUS                    = 1 << 0,
@@ -3155,6 +3249,7 @@ enum nl80211_feature_flags {
        NL80211_FEATURE_NEED_OBSS_SCAN                  = 1 << 10,
        NL80211_FEATURE_P2P_GO_CTWIN                    = 1 << 11,
        NL80211_FEATURE_P2P_GO_OPPPS                    = 1 << 12,
+       NL80211_FEATURE_FULL_AP_CLIENT_STATE            = 1 << 13,
 };
 
 /**
@@ -3182,7 +3277,7 @@ enum nl80211_probe_resp_offload_support_attr {
  * enum nl80211_connect_failed_reason - connection request failed reasons
  * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
  *     handled by the AP is reached.
- * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist.
+ * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL.
  */
 enum nl80211_connect_failed_reason {
        NL80211_CONN_FAIL_MAX_CLIENTS,
@@ -3210,4 +3305,22 @@ enum nl80211_scan_flags {
        NL80211_SCAN_FLAG_AP                            = 1<<2,
 };
 
+/**
+ * enum nl80211_acl_policy - access control policy
+ *
+ * Access control policy is applied on a MAC list set by
+ * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to
+ * be used with %NL80211_ATTR_ACL_POLICY.
+ *
+ * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are
+ *     listed in ACL, i.e. allow all the stations which are not listed
+ *     in ACL to authenticate.
+ * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed
+ *     in ACL, i.e. deny all the stations which are not listed in ACL.
+ */
+enum nl80211_acl_policy {
+       NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED,
+       NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h
new file mode 100644 (file)
index 0000000..9ee4bb8
--- /dev/null
@@ -0,0 +1,848 @@
+/*
+ * linux/uapi/sound/asoc.h -- ALSA SoC Firmware Controls and DAPM
+ *
+ * Author:             Liam Girdwood
+ * Created:            Aug 11th 2005
+ * Copyright:  Wolfson Microelectronics. PLC.
+ *              2012 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Simple file API to load FW that includes mixers, coefficients, DAPM graphs,
+ * algorithms, equalisers, DAIs, widgets etc.
+ */
+
+#ifndef __LINUX_UAPI_SND_ASOC_H
+#define __LINUX_UAPI_SND_ASOC_H
+
+#include <linux/types.h>
+
+/*
+ * Convenience kcontrol builders
+ */
+#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert) \
+       ((unsigned long)&(struct soc_mixer_control) \
+       {.reg = xreg, .rreg = xreg, .shift = shift_left, \
+       .rshift = shift_right, .max = xmax, .platform_max = xmax, \
+       .invert = xinvert})
+#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \
+       SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert)
+#define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \
+       ((unsigned long)&(struct soc_mixer_control) \
+       {.reg = xreg, .max = xmax, .platform_max = xmax, .invert = xinvert})
+#define SOC_DOUBLE_R_VALUE(xlreg, xrreg, xshift, xmax, xinvert) \
+       ((unsigned long)&(struct soc_mixer_control) \
+       {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
+       .max = xmax, .platform_max = xmax, .invert = xinvert})
+#define SOC_DOUBLE_R_RANGE_VALUE(xlreg, xrreg, xshift, xmin, xmax, xinvert) \
+       ((unsigned long)&(struct soc_mixer_control) \
+       {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
+       .min = xmin, .max = xmax, .platform_max = xmax, .invert = xinvert})
+#define SOC_SINGLE(xname, reg, shift, max, invert) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+       .put = snd_soc_put_volsw, .index = SOC_CONTROL_IO_VOLSW, \
+       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+#define SOC_SINGLE_RANGE(xname, xreg, xshift, xmin, xmax, xinvert) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .info = snd_soc_info_volsw_range, .get = snd_soc_get_volsw_range, \
+       .put = snd_soc_put_volsw_range, .index = SOC_CONTROL_IO_RANGE, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = xreg, .shift = xshift, .min = xmin,\
+                .max = xmax, .platform_max = xmax, .invert = xinvert} }
+#define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+       .put = snd_soc_put_volsw, .index = SOC_CONTROL_IO_VOLSW, \
+       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+#define SOC_SINGLE_SX_TLV(xname, xreg, xshift, xmin, xmax, tlv_array) \
+{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+       SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p  = (tlv_array),\
+       .info = snd_soc_info_volsw, \
+       .get = snd_soc_get_volsw_sx,\
+       .put = snd_soc_put_volsw_sx, \
+       .index = SOC_CONTROL_IO_VOLSW_SX, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = xreg, .rreg = xreg, \
+               .shift = xshift, .rshift = xshift, \
+               .max = xmax, .min = xmin} }
+#define SOC_SINGLE_RANGE_TLV(xname, xreg, xshift, xmin, xmax, xinvert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw_range, .index = SOC_CONTROL_IO_RANGE, \
+       .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = xreg, .shift = xshift, .min = xmin,\
+                .max = xmax, .platform_max = xmax, .invert = xinvert} }
+#define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
+       .put = snd_soc_put_volsw, .index = SOC_CONTROL_IO_VOLSW, \
+       .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
+                                         max, invert) }
+#define SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .info = snd_soc_info_volsw, .index = SOC_CONTROL_IO_VOLSW, \
+       .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
+       .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
+                                           xmax, xinvert) }
+#define SOC_DOUBLE_R_RANGE(xname, reg_left, reg_right, xshift, xmin, \
+                          xmax, xinvert)               \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .info = snd_soc_info_volsw_range, .index = SOC_CONTROL_IO_RANGE, \
+       .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \
+       .private_value = SOC_DOUBLE_R_RANGE_VALUE(reg_left, reg_right, \
+                                           xshift, xmin, xmax, xinvert) }
+#define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
+       .put = snd_soc_put_volsw, .index = SOC_CONTROL_IO_VOLSW, \
+       .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
+                                         max, invert) }
+#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, .index = SOC_CONTROL_IO_VOLSW, \
+       .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
+       .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
+                                           xmax, xinvert) }
+#define SOC_DOUBLE_R_RANGE_TLV(xname, reg_left, reg_right, xshift, xmin, \
+                              xmax, xinvert, tlv_array)                \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw_range, .index = SOC_CONTROL_IO_RANGE, \
+       .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \
+       .private_value = SOC_DOUBLE_R_RANGE_VALUE(reg_left, reg_right, \
+                                           xshift, xmin, xmax, xinvert) }
+#define SOC_DOUBLE_R_SX_TLV(xname, xreg, xrreg, xshift, xmin, xmax, tlv_array) \
+{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+       SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p  = (tlv_array), \
+       .info = snd_soc_info_volsw, \
+       .get = snd_soc_get_volsw_sx, \
+       .put = snd_soc_put_volsw_sx, \
+       .index = SOC_CONTROL_IO_VOLSW, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = xreg, .rreg = xrreg, \
+               .shift = xshift, .rshift = xshift, \
+               .max = xmax, .min = xmin} }
+#define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
+{      .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p  = (tlv_array), \
+       .info   = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \
+       .put    = snd_soc_put_volsw_s8, .index = SOC_CONTROL_IO_VOLSW_S8, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = xreg, .min = xmin, .max = xmax, \
+                .platform_max = xmax} }
+#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmax, xtexts) \
+{      .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
+       .max = xmax, .texts = xtexts, \
+       .mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0}
+#define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) \
+       SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts)
+#define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \
+{      .max = xmax, .texts = xtexts }
+#define SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xmax, xtexts, xvalues) \
+{      .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
+       .mask = xmask, .max = xmax, .texts = xtexts, .values = xvalues}
+#define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xmax, xtexts, xvalues) \
+       SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xmax, xtexts, xvalues)
+#define SOC_ENUM(xname, xenum) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
+       .info = snd_soc_info_enum_double, .index = SOC_CONTROL_IO_ENUM, \
+       .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
+       .private_value = (unsigned long)&xenum }
+#define SOC_VALUE_ENUM(xname, xenum) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
+       .info = snd_soc_info_enum_double, \
+       .get = snd_soc_get_value_enum_double, \
+       .put = snd_soc_put_value_enum_double, \
+       .index = SOC_CONTROL_IO_ENUM_VALUE, \
+       .private_value = (unsigned long)&xenum }
+#define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\
+        xhandler_get, xhandler_put) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, .index = SOC_CONTROL_IO_EXT, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
+#define SOC_DOUBLE_EXT(xname, reg, shift_left, shift_right, max, invert,\
+        xhandler_get, xhandler_put) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .info = snd_soc_info_volsw, .index = SOC_CONTROL_IO_EXT, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = \
+               SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert) }
+#define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\
+        xhandler_get, xhandler_put, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, .index = SOC_CONTROL_IO_EXT, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
+#define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\
+        xhandler_get, xhandler_put, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, .index = SOC_CONTROL_IO_EXT,\
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, \
+                                         xmax, xinvert) }
+#define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\
+        xhandler_get, xhandler_put, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, .index = SOC_CONTROL_IO_EXT, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
+                                           xmax, xinvert) }
+#define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_bool_ext, .index = SOC_CONTROL_IO_BOOL_EXT, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = xdata }
+#define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_enum_ext, .index = SOC_CONTROL_IO_ENUM_EXT, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&xenum }
+
+#define SND_SOC_BYTES(xname, xbase, xregs)                   \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,   \
+       .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+       .index = SOC_CONTROL_IO_BYTES, \
+       .put = snd_soc_bytes_put, .private_value =            \
+               ((unsigned long)&(struct soc_bytes)           \
+               {.base = xbase, .num_regs = xregs }) }
+
+#define SND_SOC_BYTES_MASK(xname, xbase, xregs, xmask)       \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,   \
+       .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+       .index = SOC_CONTROL_IO_BYTES, \
+       .put = snd_soc_bytes_put, .private_value =            \
+               ((unsigned long)&(struct soc_bytes)           \
+               {.base = xbase, .num_regs = xregs,            \
+                .mask = xmask }) }
+
+#define SOC_SINGLE_XR_SX(xname, xregbase, xregcount, xnbits, \
+               xmin, xmax, xinvert) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .info = snd_soc_info_xr_sx, .get = snd_soc_get_xr_sx, \
+       .put = snd_soc_put_xr_sx, \
+       .index = SOC_CONTROL_IO_VOLSW_XR_SX, \
+       .private_value = (unsigned long)&(struct soc_mreg_control) \
+               {.regbase = xregbase, .regcount = xregcount, .nbits = xnbits, \
+               .invert = xinvert, .min = xmin, .max = xmax} }
+
+#define SOC_SINGLE_STROBE(xname, xreg, xshift, xinvert) \
+       SOC_SINGLE_EXT(xname, xreg, xshift, 1, xinvert, \
+               snd_soc_get_strobe, snd_soc_put_strobe)
+
+/*
+ * Simplified versions of above macros, declaring a struct and calculating
+ * ARRAY_SIZE internally
+ */
+#define SOC_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xtexts) \
+       struct soc_enum name = SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, \
+                                               ARRAY_SIZE(xtexts), xtexts)
+#define SOC_ENUM_SINGLE_DECL(name, xreg, xshift, xtexts) \
+       SOC_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xtexts)
+#define SOC_ENUM_SINGLE_EXT_DECL(name, xtexts) \
+       struct soc_enum name = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(xtexts), xtexts)
+#define SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xmask, xtexts, xvalues) \
+       struct soc_enum name = SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, \
+                                                       ARRAY_SIZE(xtexts), xtexts, xvalues)
+#define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \
+       SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues)
+
+
+/*
+ * Numeric IDs for stock mixer types that are used to enumerate FW based mixers.
+ */
+
+#define SOC_CONTROL_ID_PUT(p)  ((p & 0xff) << 16)
+#define SOC_CONTROL_ID_GET(g)  ((g & 0xff) << 8)
+#define SOC_CONTROL_ID_INFO(i) ((i & 0xff) << 0)
+#define SOC_CONTROL_ID(g, p, i)        \
+       (SOC_CONTROL_ID_PUT(p) | SOC_CONTROL_ID_GET(g) |\
+       SOC_CONTROL_ID_INFO(i))
+
+#define SOC_CONTROL_GET_ID_PUT(id)     ((id & 0xff0000) >> 16)
+#define SOC_CONTROL_GET_ID_GET(id)     ((id & 0x00ff00) >> 8)
+#define SOC_CONTROL_GET_ID_INFO(id)    ((id & 0x0000ff) >> 0)
+
+/* individual kcontrol info types - can be mixed with other types */
+#define SOC_CONTROL_TYPE_EXT           0       /* driver defined */
+#define SOC_CONTROL_TYPE_VOLSW         1
+#define SOC_CONTROL_TYPE_VOLSW_SX      2
+#define SOC_CONTROL_TYPE_VOLSW_S8      3
+#define SOC_CONTROL_TYPE_VOLSW_XR_SX   4
+#define SOC_CONTROL_TYPE_ENUM          6
+#define SOC_CONTROL_TYPE_ENUM_EXT      7
+#define SOC_CONTROL_TYPE_BYTES         8
+#define SOC_CONTROL_TYPE_BOOL_EXT      9
+#define SOC_CONTROL_TYPE_ENUM_VALUE    10
+#define SOC_CONTROL_TYPE_RANGE         11
+#define SOC_CONTROL_TYPE_STROBE                12
+
+/* compound control IDs */
+#define SOC_CONTROL_IO_VOLSW \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_VOLSW, \
+               SOC_CONTROL_TYPE_VOLSW, \
+               SOC_CONTROL_TYPE_VOLSW)
+#define SOC_CONTROL_IO_VOLSW_SX \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_VOLSW_SX, \
+               SOC_CONTROL_TYPE_VOLSW_SX, \
+               SOC_CONTROL_TYPE_VOLSW)
+#define SOC_CONTROL_IO_VOLSW_S8 \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_VOLSW_S8, \
+               SOC_CONTROL_TYPE_VOLSW_S8, \
+               SOC_CONTROL_TYPE_VOLSW_S8)
+#define SOC_CONTROL_IO_VOLSW_XR_SX \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_VOLSW_XR_SX, \
+               SOC_CONTROL_TYPE_VOLSW_XR_SX, \
+               SOC_CONTROL_TYPE_VOLSW_XR_SX)
+#define SOC_CONTROL_IO_EXT \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_EXT, \
+               SOC_CONTROL_TYPE_EXT, \
+               SOC_CONTROL_TYPE_VOLSW)
+#define SOC_CONTROL_IO_ENUM \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_ENUM, \
+               SOC_CONTROL_TYPE_ENUM, \
+               SOC_CONTROL_TYPE_ENUM)
+#define SOC_CONTROL_IO_ENUM_EXT \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_EXT, \
+               SOC_CONTROL_TYPE_EXT, \
+               SOC_CONTROL_TYPE_ENUM_EXT)
+#define SOC_CONTROL_IO_BYTES \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_BYTES, \
+               SOC_CONTROL_TYPE_BYTES, \
+               SOC_CONTROL_TYPE_BYTES)
+#define SOC_CONTROL_IO_BOOL_EXT \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_EXT, \
+               SOC_CONTROL_TYPE_EXT, \
+               SOC_CONTROL_TYPE_BOOL_EXT)
+#define SOC_CONTROL_IO_ENUM_VALUE \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_ENUM_VALUE, \
+               SOC_CONTROL_TYPE_ENUM_VALUE, \
+               SOC_CONTROL_TYPE_ENUM)
+#define SOC_CONTROL_IO_RANGE \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_RANGE, \
+               SOC_CONTROL_TYPE_RANGE, \
+               SOC_CONTROL_TYPE_RANGE)
+#define SOC_CONTROL_IO_STROBE \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_STROBE, \
+               SOC_CONTROL_TYPE_STROBE, \
+               SOC_CONTROL_TYPE_STROBE)
+
+/* widget has no PM register bit */
+#define SND_SOC_NOPM   -1
+
+/*
+ * SoC dynamic audio power management
+ *
+ * We can have up to 4 power domains
+ *  1. Codec domain - VREF, VMID
+ *     Usually controlled at codec probe/remove, although can be set
+ *     at stream time if power is not needed for sidetone, etc.
+ *  2. Platform/Machine domain - physically connected inputs and outputs
+ *     Is platform/machine and user action specific, is set in the machine
+ *     driver and by userspace e.g when HP are inserted
+ *  3. Path domain - Internal codec path mixers
+ *     Are automatically set when mixer and mux settings are
+ *     changed by the user.
+ *  4. Stream domain - DAC's and ADC's.
+ *     Enabled when stream playback/capture is started.
+ */
+
+/* codec domain */
+#define SND_SOC_DAPM_VMID(wname) \
+{      .id = snd_soc_dapm_vmid, .name = wname, .kcontrol_news = NULL, \
+       .num_kcontrols = 0}
+
+/* platform domain */
+#define SND_SOC_DAPM_SIGGEN(wname) \
+{      .id = snd_soc_dapm_siggen, .name = wname, .kcontrol_news = NULL, \
+       .num_kcontrols = 0, .reg = SND_SOC_NOPM }
+#define SND_SOC_DAPM_INPUT(wname) \
+{      .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \
+       .num_kcontrols = 0, .reg = SND_SOC_NOPM }
+#define SND_SOC_DAPM_OUTPUT(wname) \
+{      .id = snd_soc_dapm_output, .name = wname, .kcontrol_news = NULL, \
+       .num_kcontrols = 0, .reg = SND_SOC_NOPM }
+#define SND_SOC_DAPM_MIC(wname, wevent) \
+{      .id = snd_soc_dapm_mic, .name = wname, .kcontrol_news = NULL, \
+       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
+       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
+#define SND_SOC_DAPM_HP(wname, wevent) \
+{      .id = snd_soc_dapm_hp, .name = wname, .kcontrol_news = NULL, \
+       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
+       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
+#define SND_SOC_DAPM_SPK(wname, wevent) \
+{      .id = snd_soc_dapm_spk, .name = wname, .kcontrol_news = NULL, \
+       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
+       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
+#define SND_SOC_DAPM_LINE(wname, wevent) \
+{      .id = snd_soc_dapm_line, .name = wname, .kcontrol_news = NULL, \
+       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
+       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
+
+/* path domain */
+#define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\
+        wcontrols, wncontrols) \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
+#define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\
+        wcontrols, wncontrols) \
+{      .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
+#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
+        wcontrols, wncontrols)\
+{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
+#define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \
+        wcontrols, wncontrols)\
+{       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
+       .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
+       .num_kcontrols = wncontrols}
+#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
+{      .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0}
+#define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \
+{      .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
+#define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
+{      .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
+#define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \
+{      .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
+#define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
+{      .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \
+       .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
+       .num_kcontrols = 1}
+
+/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
+#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
+        wcontrols) \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
+#define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \
+        wcontrols)\
+{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
+#define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \
+        wcontrols)\
+{       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
+       .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
+       .num_kcontrols = ARRAY_SIZE(wcontrols)}
+
+/* path domain with event - event handler must return 0 for success */
+#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
+       wncontrols, wevent, wflags) \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
+       .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \
+       wncontrols, wevent, wflags) \
+{      .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
+       .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
+       wncontrols, wevent, wflags) \
+{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
+       .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \
+       wcontrols, wncontrols, wevent, wflags) \
+{       .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, \
+       .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \
+       wevent, wflags) \
+{      .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
+       .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
+       wevent, wflags) \
+{      .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
+       .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
+       wevent, wflags) \
+{      .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
+       .event = wevent, .event_flags = wflags}
+
+/* additional sequencing control within an event type */
+#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
+       wevent, wflags) \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .event = wevent, .event_flags = wflags, \
+       .subseq = wsubseq}
+#define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \
+       wflags) \
+{      .id = snd_soc_dapm_supply, .name = wname, .reg = wreg,  \
+       .shift = wshift, .invert = winvert, .event = wevent, \
+       .event_flags = wflags, .subseq = wsubseq}
+
+/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
+#define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
+       wevent, wflags) \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
+       .event = wevent, .event_flags = wflags}
+#define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
+       wevent, wflags) \
+{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
+       .event = wevent, .event_flags = wflags}
+#define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \
+       wcontrols, wevent, wflags) \
+{       .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .kcontrol_news = wcontrols, \
+       .num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags}
+
+/* events that are pre and post DAPM */
+#define SND_SOC_DAPM_PRE(wname, wevent) \
+{      .id = snd_soc_dapm_pre, .name = wname, .kcontrol_news = NULL, \
+       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
+       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD}
+#define SND_SOC_DAPM_POST(wname, wevent) \
+{      .id = snd_soc_dapm_post, .name = wname, .kcontrol_news = NULL, \
+       .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
+       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
+
+/* stream domain */
+#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
+{      .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
+       .reg = wreg, .shift = wshift, .invert = winvert }
+#define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \
+                             wevent, wflags)                           \
+{      .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
+       .reg = wreg, .shift = wshift, .invert = winvert, \
+       .event = wevent, .event_flags = wflags }
+#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \
+{      .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
+       .reg = wreg, .shift = wshift, .invert = winvert }
+#define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \
+                            wevent, wflags)                            \
+{      .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
+       .reg = wreg, .shift = wshift, .invert = winvert, \
+       .event = wevent, .event_flags = wflags }
+#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
+{      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
+       .shift = wshift, .invert = winvert}
+#define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \
+                          wevent, wflags)                              \
+{      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
+       .shift = wshift, .invert = winvert, \
+       .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \
+{      .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
+       .shift = wshift, .invert = winvert}
+#define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \
+                          wevent, wflags)                              \
+{      .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
+       .shift = wshift, .invert = winvert, \
+       .event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \
+{      .id = snd_soc_dapm_clock_supply, .name = wname, \
+       .reg = SND_SOC_NOPM, .event = dapm_clock_event, \
+       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }
+
+/* generic widgets */
+#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
+{      .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
+       .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
+       .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
+       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
+#define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \
+{      .id = snd_soc_dapm_supply, .name = wname, .reg = wreg,  \
+       .shift = wshift, .invert = winvert, .event = wevent, \
+       .event_flags = wflags}
+#define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay, wflags)       \
+{      .id = snd_soc_dapm_regulator_supply, .name = wname, \
+       .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \
+       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+       .invert = wflags}
+
+
+/* dapm kcontrol types */
+#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, .index = SOC_DAPM_IO_VOLSW, \
+       .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+#define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, .index = SOC_DAPM_IO_VOLSW, \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+#define SOC_DAPM_ENUM(xname, xenum) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_enum_double, \
+       .get = snd_soc_dapm_get_enum_double, \
+       .put = snd_soc_dapm_put_enum_double, \
+       .index = SOC_DAPM_IO_ENUM_DOUBLE, \
+       .private_value = (unsigned long)&xenum }
+#define SOC_DAPM_ENUM_VIRT(xname, xenum)                   \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_enum_double, \
+       .get = snd_soc_dapm_get_enum_virt, \
+       .put = snd_soc_dapm_put_enum_virt, \
+       .index = SOC_DAPM_IO_ENUM_VIRT, \
+       .private_value = (unsigned long)&xenum }
+#define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_enum_double, \
+       .index = SOC_DAPM_IO_ENUM_EXT, \
+       .get = xget, \
+       .put = xput, \
+       .private_value = (unsigned long)&xenum }
+#define SOC_DAPM_VALUE_ENUM(xname, xenum) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_enum_double, \
+       .get = snd_soc_dapm_get_value_enum_double, \
+       .put = snd_soc_dapm_put_value_enum_double, \
+       .index = SOC_DAPM_IO_ENUM_VALUE, \
+       .private_value = (unsigned long)&xenum }
+#define SOC_DAPM_PIN_SWITCH(xname) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \
+       .info = snd_soc_dapm_info_pin_switch, \
+       .get = snd_soc_dapm_get_pin_switch, \
+       .put = snd_soc_dapm_put_pin_switch, \
+       .index = SOC_DAPM_IO_PIN, \
+       .private_value = (unsigned long)xname }
+
+#define SOC_DAPM_TYPE_VOLSW            64
+#define SOC_DAPM_TYPE_ENUM_DOUBLE      65
+#define SOC_DAPM_TYPE_ENUM_VIRT                66
+#define SOC_DAPM_TYPE_ENUM_VALUE       67
+#define SOC_DAPM_TYPE_PIN              68
+#define SOC_DAPM_TYPE_ENUM_EXT         69
+
+#define SOC_DAPM_IO_VOLSW \
+       SOC_CONTROL_ID(SOC_DAPM_TYPE_VOLSW, \
+               SOC_DAPM_TYPE_VOLSW, \
+               SOC_DAPM_TYPE_VOLSW)
+#define SOC_DAPM_IO_ENUM_DOUBLE \
+       SOC_CONTROL_ID(SOC_DAPM_TYPE_ENUM_DOUBLE, \
+               SOC_DAPM_TYPE_ENUM_DOUBLE, \
+               SOC_CONTROL_TYPE_ENUM)
+#define SOC_DAPM_IO_ENUM_VIRT \
+       SOC_CONTROL_ID(SOC_DAPM_TYPE_ENUM_VIRT, \
+               SOC_DAPM_TYPE_ENUM_VIRT, \
+               SOC_CONTROL_TYPE_ENUM)
+#define SOC_DAPM_IO_ENUM_VALUE \
+       SOC_CONTROL_ID(SOC_DAPM_TYPE_ENUM_VALUE, \
+               SOC_DAPM_TYPE_ENUM_VALUE, \
+               SOC_CONTROL_TYPE_ENUM)
+#define SOC_DAPM_IO_PIN \
+       SOC_CONTROL_ID(SOC_DAPM_TYPE_PIN, \
+               SOC_DAPM_TYPE_PIN, \
+               SOC_DAPM_TYPE_PIN)
+#define SOC_DAPM_IO_ENUM_EXT \
+       SOC_CONTROL_ID(SOC_CONTROL_TYPE_EXT, \
+               SOC_CONTROL_TYPE_EXT, \
+               SOC_CONTROL_TYPE_ENUM)
+
+/* dapm widget types */
+enum snd_soc_dapm_type {
+       snd_soc_dapm_input = 0,         /* input pin */
+       snd_soc_dapm_output,            /* output pin */
+       snd_soc_dapm_mux,                       /* selects 1 analog signal from many inputs */
+       snd_soc_dapm_virt_mux,                  /* virtual version of snd_soc_dapm_mux */
+       snd_soc_dapm_value_mux,                 /* selects 1 analog signal from many inputs */
+       snd_soc_dapm_mixer,                     /* mixes several analog signals together */
+       snd_soc_dapm_mixer_named_ctl,           /* mixer with named controls */
+       snd_soc_dapm_pga,                       /* programmable gain/attenuation (volume) */
+       snd_soc_dapm_out_drv,                   /* output driver */
+       snd_soc_dapm_adc,                       /* analog to digital converter */
+       snd_soc_dapm_dac,                       /* digital to analog converter */
+       snd_soc_dapm_micbias,           /* microphone bias (power) */
+       snd_soc_dapm_mic,                       /* microphone */
+       snd_soc_dapm_hp,                        /* headphones */
+       snd_soc_dapm_spk,                       /* speaker */
+       snd_soc_dapm_line,                      /* line input/output */
+       snd_soc_dapm_switch,            /* analog switch */
+       snd_soc_dapm_vmid,                      /* codec bias/vmid - to minimise pops */
+       snd_soc_dapm_pre,                       /* machine specific pre widget - exec first */
+       snd_soc_dapm_post,                      /* machine specific post widget - exec last */
+       snd_soc_dapm_supply,            /* power/clock supply */
+       snd_soc_dapm_regulator_supply,  /* external regulator */
+       snd_soc_dapm_clock_supply,      /* external clock */
+       snd_soc_dapm_aif_in,            /* audio interface input */
+       snd_soc_dapm_aif_out,           /* audio interface output */
+       snd_soc_dapm_siggen,            /* signal generator */
+       snd_soc_dapm_dai,               /* link to DAI structure */
+       snd_soc_dapm_dai_link,          /* link between two DAI structures */
+};
+
+/* Header magic number and string sizes */
+#define SND_SOC_FW_MAGIC       0x41536F43 /* ASoC */
+
+/* string sizes */
+#define SND_SOC_FW_TEXT_SIZE   32
+#define SND_SOC_FW_NUM_TEXTS   16
+
+/* ABI version */
+#define SND_SOC_FW_ABI_VERSION         0x1
+
+/*
+ * File and Block header data types.
+ * Add new generic and vendor types to end of list.
+ * Generic types are handled by the core whilst vendors types are passed
+ * to the component drivers for handling.
+ */
+#define SND_SOC_FW_MIXER               1
+#define SND_SOC_FW_DAPM_GRAPH          2
+#define SND_SOC_FW_DAPM_WIDGET         3
+#define SND_SOC_FW_DAI_LINK            4
+#define SND_SOC_FW_COEFF               5
+
+#define SND_SOC_FW_VENDOR_FW           1000
+#define SND_SOC_FW_VENDOR_CONFIG       1001
+#define SND_SOC_FW_VENDOR_COEFF        1002
+#define SND_SOC_FW_VENDOR_CODEC        1003
+
+/*
+ * File and Block Header
+ */
+struct snd_soc_fw_hdr {
+       __le32 magic;
+       __le32 abi;             /* ABI version */
+       __le32 type;
+       __le32 vendor_type;     /* optional vendor specific type info */
+       __le32 version;         /* optional vendor specific version details */
+       __le32 size;            /* data bytes, excluding this header */
+} __attribute__((packed));
+
+
+struct snd_soc_fw_ctl_tlv {
+       __le32 numid;   /* control element numeric identification */
+       __le32 length;  /* in bytes aligned to 4 */
+       /* tlv data starts here */
+} __attribute__((packed));
+
+struct snd_soc_fw_control_hdr {
+       char name[SND_SOC_FW_TEXT_SIZE];
+       __le32 index;
+       __le32 access;
+       __le32 tlv_size;
+} __attribute__((packed));
+
+/*
+ * Mixer kcontrol.
+ */
+struct snd_soc_fw_mixer_control {
+       struct snd_soc_fw_control_hdr hdr;
+       __s32 min;
+       __s32 max;
+       __s32 platform_max;
+       __le32 reg;
+       __le32 rreg;
+       __le32 shift;
+       __le32 rshift;
+       __le32 invert;
+} __attribute__((packed));
+
+/*
+ * Enumerated kcontrol
+ */
+struct snd_soc_fw_enum_control {
+       struct snd_soc_fw_control_hdr hdr;
+       __le32 reg;
+       __le32 reg2;
+       __le32 shift_l;
+       __le32 shift_r;
+       __le32 max;
+       __le32 mask;
+       __le32 count;
+       char texts[SND_SOC_FW_NUM_TEXTS][SND_SOC_FW_TEXT_SIZE];
+       __le32 values[SND_SOC_FW_NUM_TEXTS * SND_SOC_FW_TEXT_SIZE / 4];
+} __attribute__((packed));
+
+/*
+ * kcontrol Header
+ */
+struct snd_soc_fw_kcontrol {
+       __le32 count; /* in kcontrols (based on type) */
+       /* kcontrols here */
+} __attribute__((packed));
+
+/*
+ * DAPM Graph Element
+ */
+struct snd_soc_fw_dapm_graph_elem {
+       char sink[SND_SOC_FW_TEXT_SIZE];
+       char control[SND_SOC_FW_TEXT_SIZE];
+       char source[SND_SOC_FW_TEXT_SIZE];
+} __attribute__((packed));
+
+
+/*
+ * DAPM Widget.
+ */
+struct snd_soc_fw_dapm_widget {
+       __le32 id;              /* snd_soc_dapm_type */
+       char name[SND_SOC_FW_TEXT_SIZE];
+       char sname[SND_SOC_FW_TEXT_SIZE];
+
+       __s32 reg;              /* negative reg = no direct dapm */
+       __le32 shift;           /* bits to shift */
+       __le32 mask;            /* non-shifted mask */
+       __u8 invert;            /* invert the power bit */
+       __u8 ignore_suspend;    /* kept enabled over suspend */
+       __u8 padding[2];
+
+       /* kcontrols that relate to this widget */
+       struct snd_soc_fw_kcontrol kcontrol;
+       /* controls follow here */
+} __attribute__((packed));
+
+/*
+ * DAPM Graph and Pins.
+ */
+struct snd_soc_fw_dapm_elems {
+       __le32 count; /* in elements */
+       /* elements here */
+} __attribute__((packed));
+
+/*
+ * Coeffcient File Data.
+ */
+struct snd_soc_file_coeff_data {
+       __le32 count; /* in elems */
+       __le32 size;    /* total data size */
+       __le32 id; /* associated mixer ID */
+       /* data here */
+} __attribute__((packed));
+
+#endif
index caefa093337d9da1d0ba440cdc98665d91bb1801..7fd3d65065622a8bc0cb4f41645de417a32e3df6 100644 (file)
@@ -221,6 +221,53 @@ enum omap_dss_output_id {
        OMAP_DSS_OUTPUT_HDMI    = 1 << 6,
 };
 
+/* Stereoscopic Panel types
+ * row, column, overunder, sidebyside options
+ * are with respect to native scan order
+*/
+enum s3d_disp_type {
+       S3D_DISP_NONE = 0,
+       S3D_DISP_FRAME_SEQ,
+       S3D_DISP_ROW_IL,
+       S3D_DISP_COL_IL,
+       S3D_DISP_PIX_IL,
+       S3D_DISP_CHECKB,
+       S3D_DISP_OVERUNDER,
+       S3D_DISP_SIDEBYSIDE,
+};
+
+/* Subsampling direction is based on native panel scan order.
+*/
+enum s3d_disp_sub_sampling {
+       S3D_DISP_SUB_SAMPLE_NONE = 0,
+       S3D_DISP_SUB_SAMPLE_V,
+       S3D_DISP_SUB_SAMPLE_H,
+};
+
+/* Indicates if display expects left view first followed by right or viceversa
+ * For row interlaved displays, defines first row view
+ * For column interleaved displays, defines first column view
+ * For checkerboard, defines first pixel view
+ * For overunder, defines top view
+ * For sidebyside, defines west view
+*/
+enum s3d_disp_order {
+       S3D_DISP_ORDER_L = 0,
+       S3D_DISP_ORDER_R = 1,
+};
+
+/* S3D information */
+struct s3d_disp_info {
+       enum s3d_disp_type type;
+       enum s3d_disp_sub_sampling sub_samp;
+       enum s3d_disp_order order;
+       /* Gap between left and right views
+        * For over/under units are lines
+        * For sidebyside units are pixels
+         *For other types ignored*/
+       unsigned int gap;
+};
+
 /* RFBI */
 
 struct rfbi_timings {
@@ -625,6 +672,8 @@ struct omap_dss_device {
                enum omap_dss_dsi_pixel_format dsi_pix_fmt;
                enum omap_dss_dsi_mode dsi_mode;
                struct omap_dss_dsi_videomode_timings dsi_vm_timings;
+               struct s3d_disp_info s3d_info;
+
        } panel;
 
        struct {
@@ -720,7 +769,6 @@ struct omap_dss_driver {
 
        int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len);
        bool (*detect)(struct omap_dss_device *dssdev);
-
        /*
         * For display drivers that support audio. This encompasses
         * HDMI and DisplayPort at the moment.
@@ -738,6 +786,8 @@ struct omap_dss_driver {
        int (*audio_start)(struct omap_dss_device *dssdev);
        void (*audio_stop)(struct omap_dss_device *dssdev);
 
+       int (*s3d_enable)(struct omap_dss_device *dssdev,
+                               struct s3d_disp_info *info, int code);
 };
 
 enum omapdss_version omapdss_get_version(void);
index 8832b879c6b8d7e468cfcc1ea444c2874495ba8c..23caaa3ae33a63636bc20c09b62827fd7299319d 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2289,7 +2289,7 @@ static void unmap_region(struct mm_struct *mm,
        update_hiwater_rss(mm);
        unmap_vmas(&tlb, vma, start, end);
        free_pgtables(&tlb, vma, prev ? prev->vm_end : FIRST_USER_ADDRESS,
-                                next ? next->vm_start : 0);
+                                next ? next->vm_start : USER_PGTABLES_CEILING);
        tlb_finish_mmu(&tlb, start, end);
 }
 
@@ -2667,7 +2667,7 @@ void exit_mmap(struct mm_struct *mm)
        /* Use -1 here to ensure all VMAs in the mm are unmapped */
        unmap_vmas(&tlb, vma, 0, -1);
 
-       free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, 0);
+       free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, USER_PGTABLES_CEILING);
        tlb_finish_mmu(&tlb, 0, -1);
 
        /*
index 2f67d5ecc907e6dfc0ca74fcc21fbf12a527b175..eb0f4b16ff099611fb6ea0aa43add29bd1c1173f 100644 (file)
@@ -290,7 +290,7 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
                goto done;
        }
 
-       mgr->state = READ_LOC_AMP_INFO;
+       set_bit(READ_LOC_AMP_INFO, &mgr->state);
        hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
 
 done:
@@ -499,8 +499,16 @@ send_rsp:
        if (hdev)
                hci_dev_put(hdev);
 
-       a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp),
-                 &rsp);
+       /* Reply error now and success after HCI Write Remote AMP Assoc
+          command complete with success status
+        */
+       if (rsp.status != A2MP_STATUS_SUCCESS) {
+               a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident,
+                         sizeof(rsp), &rsp);
+       } else {
+               set_bit(WRITE_REMOTE_AMP_ASSOC, &mgr->state);
+               mgr->ident = hdr->ident;
+       }
 
        skb_pull(skb, le16_to_cpu(hdr->len));
        return 0;
@@ -840,7 +848,7 @@ struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
 
        mutex_lock(&amp_mgr_list_lock);
        list_for_each_entry(mgr, &amp_mgr_list, list) {
-               if (mgr->state == state) {
+               if (test_and_clear_bit(state, &mgr->state)) {
                        amp_mgr_get(mgr);
                        mutex_unlock(&amp_mgr_list_lock);
                        return mgr;
@@ -949,6 +957,32 @@ clean:
        kfree(req);
 }
 
+void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status)
+{
+       struct amp_mgr *mgr;
+       struct a2mp_physlink_rsp rsp;
+       struct hci_conn *hs_hcon;
+
+       mgr = amp_mgr_lookup_by_state(WRITE_REMOTE_AMP_ASSOC);
+       if (!mgr)
+               return;
+
+       hs_hcon = hci_conn_hash_lookup_state(hdev, AMP_LINK, BT_CONNECT);
+       if (!hs_hcon) {
+               rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION;
+       } else {
+               rsp.remote_id = hs_hcon->remote_id;
+               rsp.status = A2MP_STATUS_SUCCESS;
+       }
+
+       BT_DBG("%s mgr %p hs_hcon %p status %u", hdev->name, mgr, hs_hcon,
+              status);
+
+       rsp.local_id = hdev->id;
+       a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, mgr->ident, sizeof(rsp), &rsp);
+       amp_mgr_put(mgr);
+}
+
 void a2mp_discover_amp(struct l2cap_chan *chan)
 {
        struct l2cap_conn *conn = chan->conn;
index 1b0d92c0643a97595ea2ad44e69f2cf45b96a852..d459ed43c779d776e453634db290d08f48d5a7c7 100644 (file)
@@ -236,7 +236,7 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr)
 
        cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
 
-       mgr->state = READ_LOC_AMP_ASSOC;
+       set_bit(READ_LOC_AMP_ASSOC, &mgr->state);
        hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
 }
 
@@ -250,7 +250,7 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
        cp.len_so_far = cpu_to_le16(0);
        cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
 
-       mgr->state = READ_LOC_AMP_ASSOC_FINAL;
+       set_bit(READ_LOC_AMP_ASSOC_FINAL, &mgr->state);
 
        /* Read Local AMP Assoc final link information data */
        hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
@@ -317,7 +317,9 @@ void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle)
        if (!hcon)
                return;
 
-       amp_write_rem_assoc_frag(hdev, hcon);
+       /* Send A2MP create phylink rsp when all fragments are written */
+       if (amp_write_rem_assoc_frag(hdev, hcon))
+               a2mp_send_create_phy_link_rsp(hdev, 0);
 }
 
 void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle)
@@ -403,26 +405,20 @@ void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon)
 
 void amp_create_logical_link(struct l2cap_chan *chan)
 {
+       struct hci_conn *hs_hcon = chan->hs_hcon;
        struct hci_cp_create_accept_logical_link cp;
-       struct hci_conn *hcon;
        struct hci_dev *hdev;
 
-       BT_DBG("chan %p", chan);
+       BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon, chan->conn->dst);
 
-       if (!chan->hs_hcon)
+       if (!hs_hcon)
                return;
 
        hdev = hci_dev_hold(chan->hs_hcon->hdev);
        if (!hdev)
                return;
 
-       BT_DBG("chan %p dst %pMR", chan, chan->conn->dst);
-
-       hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst);
-       if (!hcon)
-               goto done;
-
-       cp.phy_handle = hcon->handle;
+       cp.phy_handle = hs_hcon->handle;
 
        cp.tx_flow_spec.id = chan->local_id;
        cp.tx_flow_spec.stype = chan->local_stype;
@@ -438,14 +434,13 @@ void amp_create_logical_link(struct l2cap_chan *chan)
        cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat);
        cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to);
 
-       if (hcon->out)
+       if (hs_hcon->out)
                hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp),
                             &cp);
        else
                hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp),
                             &cp);
 
-done:
        hci_dev_put(hdev);
 }
 
index a5b639702637404d5c1955b36163a3c459fe174c..e430b1abcd2fabf102d15cd4f99399c983fcf9f2 100644 (file)
@@ -33,7 +33,6 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/l2cap.h>
 
 #include "bnep.h"
 
index 0f78e34220c9025aae08f3b38b49924b6c397ff6..22e77a7865458d550346ab708a270d4be40e891b 100644 (file)
@@ -1146,7 +1146,8 @@ static void hci_power_on(struct work_struct *work)
                return;
 
        if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
-               schedule_delayed_work(&hdev->power_off, HCI_AUTO_OFF_TIMEOUT);
+               queue_delayed_work(hdev->req_workqueue, &hdev->power_off,
+                                  HCI_AUTO_OFF_TIMEOUT);
 
        if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags))
                mgmt_index_added(hdev);
@@ -1182,14 +1183,10 @@ static void hci_discov_off(struct work_struct *work)
 
 int hci_uuids_clear(struct hci_dev *hdev)
 {
-       struct list_head *p, *n;
-
-       list_for_each_safe(p, n, &hdev->uuids) {
-               struct bt_uuid *uuid;
+       struct bt_uuid *uuid, *tmp;
 
-               uuid = list_entry(p, struct bt_uuid, list);
-
-               list_del(p);
+       list_for_each_entry_safe(uuid, tmp, &hdev->uuids, list) {
+               list_del(&uuid->list);
                kfree(uuid);
        }
 
@@ -1621,8 +1618,8 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,
        if (err < 0)
                return err;
 
-       schedule_delayed_work(&hdev->le_scan_disable,
-                             msecs_to_jiffies(timeout));
+       queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
+                          msecs_to_jiffies(timeout));
 
        return 0;
 }
@@ -1799,6 +1796,15 @@ int hci_register_dev(struct hci_dev *hdev)
                goto err;
        }
 
+       hdev->req_workqueue = alloc_workqueue(hdev->name,
+                                             WQ_HIGHPRI | WQ_UNBOUND |
+                                             WQ_MEM_RECLAIM, 1);
+       if (!hdev->req_workqueue) {
+               destroy_workqueue(hdev->workqueue);
+               error = -ENOMEM;
+               goto err;
+       }
+
        error = hci_add_sysfs(hdev);
        if (error < 0)
                goto err_wqueue;
@@ -1821,12 +1827,13 @@ int hci_register_dev(struct hci_dev *hdev)
        hci_notify(hdev, HCI_DEV_REG);
        hci_dev_hold(hdev);
 
-       schedule_work(&hdev->power_on);
+       queue_work(hdev->req_workqueue, &hdev->power_on);
 
        return id;
 
 err_wqueue:
        destroy_workqueue(hdev->workqueue);
+       destroy_workqueue(hdev->req_workqueue);
 err:
        ida_simple_remove(&hci_index_ida, hdev->id);
        write_lock(&hci_dev_list_lock);
@@ -1880,6 +1887,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
        hci_del_sysfs(hdev);
 
        destroy_workqueue(hdev->workqueue);
+       destroy_workqueue(hdev->req_workqueue);
 
        hci_dev_lock(hdev);
        hci_blacklist_clear(hdev);
index 81b44481d0d93a8dd8513dd15f97f9a21b8a32d6..477726a63512e0160fb550f7c83861266ad585b7 100644 (file)
@@ -609,8 +609,17 @@ static void le_setup(struct hci_dev *hdev)
        /* Read LE Buffer Size */
        hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
 
+       /* Read LE Local Supported Features */
+       hci_send_cmd(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL);
+
        /* Read LE Advertising Channel TX Power */
        hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
+
+       /* Read LE White List Size */
+       hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL);
+
+       /* Read LE Supported States */
+       hci_send_cmd(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL);
 }
 
 static void hci_setup(struct hci_dev *hdev)
@@ -1090,6 +1099,19 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
        hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
 }
 
+static void hci_cc_le_read_local_features(struct hci_dev *hdev,
+                                         struct sk_buff *skb)
+{
+       struct hci_rp_le_read_local_features *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (!rp->status)
+               memcpy(hdev->le_features, rp->features, 8);
+
+       hci_req_complete(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, rp->status);
+}
+
 static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
                                        struct sk_buff *skb)
 {
@@ -1290,6 +1312,19 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
        }
 }
 
+static void hci_cc_le_read_white_list_size(struct hci_dev *hdev,
+                                          struct sk_buff *skb)
+{
+       struct hci_rp_le_read_white_list_size *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x size %u", hdev->name, rp->status, rp->size);
+
+       if (!rp->status)
+               hdev->le_white_list_size = rp->size;
+
+       hci_req_complete(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, rp->status);
+}
+
 static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_rp_le_ltk_reply *rp = (void *) skb->data;
@@ -1314,6 +1349,19 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
        hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status);
 }
 
+static void hci_cc_le_read_supported_states(struct hci_dev *hdev,
+                                           struct sk_buff *skb)
+{
+       struct hci_rp_le_read_supported_states *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (!rp->status)
+               memcpy(hdev->le_states, rp->le_states, 8);
+
+       hci_req_complete(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, rp->status);
+}
+
 static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
                                           struct sk_buff *skb)
 {
@@ -2628,6 +2676,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_le_read_buffer_size(hdev, skb);
                break;
 
+       case HCI_OP_LE_READ_LOCAL_FEATURES:
+               hci_cc_le_read_local_features(hdev, skb);
+               break;
+
        case HCI_OP_LE_READ_ADV_TX_POWER:
                hci_cc_le_read_adv_tx_power(hdev, skb);
                break;
@@ -2664,6 +2716,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_le_set_scan_enable(hdev, skb);
                break;
 
+       case HCI_OP_LE_READ_WHITE_LIST_SIZE:
+               hci_cc_le_read_white_list_size(hdev, skb);
+               break;
+
        case HCI_OP_LE_LTK_REPLY:
                hci_cc_le_ltk_reply(hdev, skb);
                break;
@@ -2672,6 +2728,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_le_ltk_neg_reply(hdev, skb);
                break;
 
+       case HCI_OP_LE_READ_SUPPORTED_STATES:
+               hci_cc_le_read_supported_states(hdev, skb);
+               break;
+
        case HCI_OP_WRITE_LE_HOST_SUPPORTED:
                hci_cc_write_le_host_supported(hdev, skb);
                break;
@@ -3928,8 +3988,6 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
        void *ptr = &skb->data[1];
        s8 rssi;
 
-       hci_dev_lock(hdev);
-
        while (num_reports--) {
                struct hci_ev_le_advertising_info *ev = ptr;
 
@@ -3939,8 +3997,6 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
                ptr += sizeof(*ev) + ev->length + 1;
        }
-
-       hci_dev_unlock(hdev);
 }
 
 static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
index 55cceee02a840598d8b8f49d9f6b0ad989719ba8..23b4e242a31a9703cf27c206a1bc66cf8feb0c6b 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <linux/debugfs.h>
 #include <linux/module.h>
+#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -461,19 +462,18 @@ static const struct file_operations blacklist_fops = {
 
 static void print_bt_uuid(struct seq_file *f, u8 *uuid)
 {
-       __be32 data0, data4;
-       __be16 data1, data2, data3, data5;
+       u32 data0, data5;
+       u16 data1, data2, data3, data4;
 
-       memcpy(&data0, &uuid[0], 4);
-       memcpy(&data1, &uuid[4], 2);
-       memcpy(&data2, &uuid[6], 2);
-       memcpy(&data3, &uuid[8], 2);
-       memcpy(&data4, &uuid[10], 4);
-       memcpy(&data5, &uuid[14], 2);
+       data5 = get_unaligned_le32(uuid);
+       data4 = get_unaligned_le16(uuid + 4);
+       data3 = get_unaligned_le16(uuid + 6);
+       data2 = get_unaligned_le16(uuid + 8);
+       data1 = get_unaligned_le16(uuid + 10);
+       data0 = get_unaligned_le32(uuid + 12);
 
-       seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x\n",
-                  ntohl(data0), ntohs(data1), ntohs(data2), ntohs(data3),
-                  ntohl(data4), ntohs(data5));
+       seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n",
+                  data0, data1, data2, data3, data4, data5);
 }
 
 static int uuids_show(struct seq_file *f, void *p)
index 22e658322845b9f16bcf8de52c1ba7c401bfef87..7c7e9321f1ea4263e0c51e792a14e0454d4a0e12 100644 (file)
@@ -1527,17 +1527,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
        BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
 
        switch (hcon->type) {
-       case AMP_LINK:
-               conn->mtu = hcon->hdev->block_mtu;
-               break;
-
        case LE_LINK:
                if (hcon->hdev->le_mtu) {
                        conn->mtu = hcon->hdev->le_mtu;
                        break;
                }
                /* fall through */
-
        default:
                conn->mtu = hcon->hdev->acl_mtu;
                break;
index f559b966279c13e8d1e0eb36afbcc465067c4e8d..39395c7144aa16d0402ae9a71ecd2cd248a1affa 100644 (file)
@@ -35,7 +35,7 @@
 bool enable_hs;
 
 #define MGMT_VERSION   1
-#define MGMT_REVISION  2
+#define MGMT_REVISION  3
 
 static const u16 mgmt_commands[] = {
        MGMT_OP_READ_INDEX_LIST,
@@ -435,35 +435,117 @@ static u32 get_current_settings(struct hci_dev *hdev)
 
 #define PNP_INFO_SVCLASS_ID            0x1200
 
-static u8 bluetooth_base_uuid[] = {
-                       0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
-                       0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
+static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
+{
+       u8 *ptr = data, *uuids_start = NULL;
+       struct bt_uuid *uuid;
+
+       if (len < 4)
+               return ptr;
+
+       list_for_each_entry(uuid, &hdev->uuids, list) {
+               u16 uuid16;
+
+               if (uuid->size != 16)
+                       continue;
+
+               uuid16 = get_unaligned_le16(&uuid->uuid[12]);
+               if (uuid16 < 0x1100)
+                       continue;
+
+               if (uuid16 == PNP_INFO_SVCLASS_ID)
+                       continue;
 
-static u16 get_uuid16(u8 *uuid128)
+               if (!uuids_start) {
+                       uuids_start = ptr;
+                       uuids_start[0] = 1;
+                       uuids_start[1] = EIR_UUID16_ALL;
+                       ptr += 2;
+               }
+
+               /* Stop if not enough space to put next UUID */
+               if ((ptr - data) + sizeof(u16) > len) {
+                       uuids_start[1] = EIR_UUID16_SOME;
+                       break;
+               }
+
+               *ptr++ = (uuid16 & 0x00ff);
+               *ptr++ = (uuid16 & 0xff00) >> 8;
+               uuids_start[0] += sizeof(uuid16);
+       }
+
+       return ptr;
+}
+
+static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
 {
-       u32 val;
-       int i;
+       u8 *ptr = data, *uuids_start = NULL;
+       struct bt_uuid *uuid;
+
+       if (len < 6)
+               return ptr;
 
-       for (i = 0; i < 12; i++) {
-               if (bluetooth_base_uuid[i] != uuid128[i])
-                       return 0;
+       list_for_each_entry(uuid, &hdev->uuids, list) {
+               if (uuid->size != 32)
+                       continue;
+
+               if (!uuids_start) {
+                       uuids_start = ptr;
+                       uuids_start[0] = 1;
+                       uuids_start[1] = EIR_UUID32_ALL;
+                       ptr += 2;
+               }
+
+               /* Stop if not enough space to put next UUID */
+               if ((ptr - data) + sizeof(u32) > len) {
+                       uuids_start[1] = EIR_UUID32_SOME;
+                       break;
+               }
+
+               memcpy(ptr, &uuid->uuid[12], sizeof(u32));
+               ptr += sizeof(u32);
+               uuids_start[0] += sizeof(u32);
        }
 
-       val = get_unaligned_le32(&uuid128[12]);
-       if (val > 0xffff)
-               return 0;
+       return ptr;
+}
+
+static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
+{
+       u8 *ptr = data, *uuids_start = NULL;
+       struct bt_uuid *uuid;
+
+       if (len < 18)
+               return ptr;
 
-       return (u16) val;
+       list_for_each_entry(uuid, &hdev->uuids, list) {
+               if (uuid->size != 128)
+                       continue;
+
+               if (!uuids_start) {
+                       uuids_start = ptr;
+                       uuids_start[0] = 1;
+                       uuids_start[1] = EIR_UUID128_ALL;
+                       ptr += 2;
+               }
+
+               /* Stop if not enough space to put next UUID */
+               if ((ptr - data) + 16 > len) {
+                       uuids_start[1] = EIR_UUID128_SOME;
+                       break;
+               }
+
+               memcpy(ptr, uuid->uuid, 16);
+               ptr += 16;
+               uuids_start[0] += 16;
+       }
+
+       return ptr;
 }
 
 static void create_eir(struct hci_dev *hdev, u8 *data)
 {
        u8 *ptr = data;
-       u16 eir_len = 0;
-       u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
-       int i, truncated = 0;
-       struct bt_uuid *uuid;
        size_t name_len;
 
        name_len = strlen(hdev->dev_name);
@@ -481,7 +563,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
 
                memcpy(ptr + 2, hdev->dev_name, name_len);
 
-               eir_len += (name_len + 2);
                ptr += (name_len + 2);
        }
 
@@ -490,7 +571,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
                ptr[1] = EIR_TX_POWER;
                ptr[2] = (u8) hdev->inq_tx_power;
 
-               eir_len += 3;
                ptr += 3;
        }
 
@@ -503,60 +583,12 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
                put_unaligned_le16(hdev->devid_product, ptr + 6);
                put_unaligned_le16(hdev->devid_version, ptr + 8);
 
-               eir_len += 10;
                ptr += 10;
        }
 
-       memset(uuid16_list, 0, sizeof(uuid16_list));
-
-       /* Group all UUID16 types */
-       list_for_each_entry(uuid, &hdev->uuids, list) {
-               u16 uuid16;
-
-               uuid16 = get_uuid16(uuid->uuid);
-               if (uuid16 == 0)
-                       return;
-
-               if (uuid16 < 0x1100)
-                       continue;
-
-               if (uuid16 == PNP_INFO_SVCLASS_ID)
-                       continue;
-
-               /* Stop if not enough space to put next UUID */
-               if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
-                       truncated = 1;
-                       break;
-               }
-
-               /* Check for duplicates */
-               for (i = 0; uuid16_list[i] != 0; i++)
-                       if (uuid16_list[i] == uuid16)
-                               break;
-
-               if (uuid16_list[i] == 0) {
-                       uuid16_list[i] = uuid16;
-                       eir_len += sizeof(u16);
-               }
-       }
-
-       if (uuid16_list[0] != 0) {
-               u8 *length = ptr;
-
-               /* EIR Data type */
-               ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
-
-               ptr += 2;
-               eir_len += 2;
-
-               for (i = 0; uuid16_list[i] != 0; i++) {
-                       *ptr++ = (uuid16_list[i] & 0x00ff);
-                       *ptr++ = (uuid16_list[i] & 0xff00) >> 8;
-               }
-
-               /* EIR Data length */
-               *length = (i * sizeof(u16)) + 1;
-       }
+       ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
+       ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
+       ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
 }
 
 static int update_eir(struct hci_dev *hdev)
@@ -728,13 +760,9 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
                                            void *data),
                                 void *data)
 {
-       struct list_head *p, *n;
-
-       list_for_each_safe(p, n, &hdev->mgmt_pending) {
-               struct pending_cmd *cmd;
-
-               cmd = list_entry(p, struct pending_cmd, list);
+       struct pending_cmd *cmd, *tmp;
 
+       list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
                if (opcode > 0 && cmd->opcode != opcode)
                        continue;
 
@@ -777,14 +805,19 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("request for %s", hdev->name);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        hci_dev_lock(hdev);
 
        if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
                cancel_delayed_work(&hdev->power_off);
 
                if (cp->val) {
-                       err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
-                       mgmt_powered(hdev, 1);
+                       mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev,
+                                        data, len);
+                       err = mgmt_powered(hdev, 1);
                        goto failed;
                }
        }
@@ -807,9 +840,9 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
        }
 
        if (cp->val)
-               schedule_work(&hdev->power_on);
+               queue_work(hdev->req_workqueue, &hdev->power_on);
        else
-               schedule_work(&hdev->power_off.work);
+               queue_work(hdev->req_workqueue, &hdev->power_off.work);
 
        err = 0;
 
@@ -872,6 +905,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
                                 MGMT_STATUS_NOT_SUPPORTED);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        timeout = __le16_to_cpu(cp->timeout);
        if (!cp->val && timeout > 0)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
@@ -971,6 +1008,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
                return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        hci_dev_lock(hdev);
 
        if (!hdev_is_powered(hdev)) {
@@ -1041,6 +1082,10 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("request for %s", hdev->name);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_PAIRABLE,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        hci_dev_lock(hdev);
 
        if (cp->val)
@@ -1073,6 +1118,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
                return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        hci_dev_lock(hdev);
 
        if (!hdev_is_powered(hdev)) {
@@ -1133,13 +1182,15 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        BT_DBG("request for %s", hdev->name);
 
-       hci_dev_lock(hdev);
+       if (!lmp_ssp_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
+                                 MGMT_STATUS_NOT_SUPPORTED);
 
-       if (!lmp_ssp_capable(hdev)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
-                                MGMT_STATUS_NOT_SUPPORTED);
-               goto failed;
-       }
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
 
        val = !!cp->val;
 
@@ -1199,6 +1250,10 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        if (cp->val)
                set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
        else
@@ -1217,13 +1272,15 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        BT_DBG("request for %s", hdev->name);
 
-       hci_dev_lock(hdev);
+       if (!lmp_le_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
+                                 MGMT_STATUS_NOT_SUPPORTED);
 
-       if (!lmp_le_capable(hdev)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
-                                MGMT_STATUS_NOT_SUPPORTED);
-               goto unlock;
-       }
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
 
        val = !!cp->val;
        enabled = lmp_host_le_capable(hdev);
@@ -1275,6 +1332,25 @@ unlock:
        return err;
 }
 
+static const u8 bluetooth_base_uuid[] = {
+                       0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+                       0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static u8 get_uuid_size(const u8 *uuid)
+{
+       u32 val;
+
+       if (memcmp(uuid, bluetooth_base_uuid, 12))
+               return 128;
+
+       val = get_unaligned_le32(&uuid[12]);
+       if (val > 0xffff)
+               return 32;
+
+       return 16;
+}
+
 static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 {
        struct mgmt_cp_add_uuid *cp = data;
@@ -1300,8 +1376,9 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        memcpy(uuid->uuid, cp->uuid, 16);
        uuid->svc_hint = cp->svc_hint;
+       uuid->size = get_uuid_size(cp->uuid);
 
-       list_add(&uuid->list, &hdev->uuids);
+       list_add_tail(&uuid->list, &hdev->uuids);
 
        err = update_class(hdev);
        if (err < 0)
@@ -1332,7 +1409,8 @@ static bool enable_service_cache(struct hci_dev *hdev)
                return false;
 
        if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
-               schedule_delayed_work(&hdev->service_cache, CACHE_TIMEOUT);
+               queue_delayed_work(hdev->workqueue, &hdev->service_cache,
+                                  CACHE_TIMEOUT);
                return true;
        }
 
@@ -1344,7 +1422,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
 {
        struct mgmt_cp_remove_uuid *cp = data;
        struct pending_cmd *cmd;
-       struct list_head *p, *n;
+       struct bt_uuid *match, *tmp;
        u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int err, found;
 
@@ -1372,9 +1450,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
 
        found = 0;
 
-       list_for_each_safe(p, n, &hdev->uuids) {
-               struct bt_uuid *match = list_entry(p, struct bt_uuid, list);
-
+       list_for_each_entry_safe(match, tmp, &hdev->uuids, list) {
                if (memcmp(match->uuid, cp->uuid, 16) != 0)
                        continue;
 
@@ -1422,13 +1498,19 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("request for %s", hdev->name);
 
-       hci_dev_lock(hdev);
+       if (!lmp_bredr_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
 
-       if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
-                                MGMT_STATUS_BUSY);
-               goto unlock;
-       }
+       if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+                                 MGMT_STATUS_BUSY);
+
+       if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
 
        hdev->major_class = cp->major;
        hdev->minor_class = cp->minor;
@@ -1483,9 +1565,21 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
                                  MGMT_STATUS_INVALID_PARAMS);
        }
 
+       if (cp->debug_keys != 0x00 && cp->debug_keys != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys,
               key_count);
 
+       for (i = 0; i < key_count; i++) {
+               struct mgmt_link_key_info *key = &cp->keys[i];
+
+               if (key->addr.type != BDADDR_BREDR)
+                       return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+                                         MGMT_STATUS_INVALID_PARAMS);
+       }
+
        hci_dev_lock(hdev);
 
        hci_link_keys_clear(hdev);
@@ -1533,12 +1627,22 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
        struct hci_conn *conn;
        int err;
 
-       hci_dev_lock(hdev);
-
        memset(&rp, 0, sizeof(rp));
        bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
        rp.addr.type = cp->addr.type;
 
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &rp, sizeof(rp));
+
+       if (cp->disconnect != 0x00 && cp->disconnect != 0x01)
+               return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &rp, sizeof(rp));
+
+       hci_dev_lock(hdev);
+
        if (!hdev_is_powered(hdev)) {
                err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
                                   MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
@@ -1596,6 +1700,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
                      u16 len)
 {
        struct mgmt_cp_disconnect *cp = data;
+       struct mgmt_rp_disconnect rp;
        struct hci_cp_disconnect dc;
        struct pending_cmd *cmd;
        struct hci_conn *conn;
@@ -1603,17 +1708,26 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("");
 
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+       rp.addr.type = cp->addr.type;
+
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &rp, sizeof(rp));
+
        hci_dev_lock(hdev);
 
        if (!test_bit(HCI_UP, &hdev->flags)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
-                                MGMT_STATUS_NOT_POWERED);
+               err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+                                  MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
                goto failed;
        }
 
        if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
-                                MGMT_STATUS_BUSY);
+               err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+                                  MGMT_STATUS_BUSY, &rp, sizeof(rp));
                goto failed;
        }
 
@@ -1624,8 +1738,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
                conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
 
        if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
-                                MGMT_STATUS_NOT_CONNECTED);
+               err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+                                  MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp));
                goto failed;
        }
 
@@ -1903,11 +2017,20 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("");
 
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+       rp.addr.type = cp->addr.type;
+
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &rp, sizeof(rp));
+
        hci_dev_lock(hdev);
 
        if (!hdev_is_powered(hdev)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
-                                MGMT_STATUS_NOT_POWERED);
+               err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+                                  MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
                goto unlock;
        }
 
@@ -1924,10 +2047,6 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr,
                                   cp->addr.type, sec_level, auth_type);
 
-       memset(&rp, 0, sizeof(rp));
-       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
-       rp.addr.type = cp->addr.type;
-
        if (IS_ERR(conn)) {
                int status;
 
@@ -2254,24 +2373,16 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
 
        hci_dev_lock(hdev);
 
-       if (!hdev_is_powered(hdev)) {
-               err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
-                                  MGMT_STATUS_NOT_POWERED, &cp->addr,
-                                  sizeof(cp->addr));
-               goto unlock;
-       }
-
        err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
                                      cp->randomizer);
        if (err < 0)
                status = MGMT_STATUS_FAILED;
        else
-               status = 0;
+               status = MGMT_STATUS_SUCCESS;
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status,
                           &cp->addr, sizeof(cp->addr));
 
-unlock:
        hci_dev_unlock(hdev);
        return err;
 }
@@ -2287,24 +2398,15 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
 
        hci_dev_lock(hdev);
 
-       if (!hdev_is_powered(hdev)) {
-               err = cmd_complete(sk, hdev->id,
-                                  MGMT_OP_REMOVE_REMOTE_OOB_DATA,
-                                  MGMT_STATUS_NOT_POWERED, &cp->addr,
-                                  sizeof(cp->addr));
-               goto unlock;
-       }
-
        err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
        if (err < 0)
                status = MGMT_STATUS_INVALID_PARAMS;
        else
-               status = 0;
+               status = MGMT_STATUS_SUCCESS;
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
                           status, &cp->addr, sizeof(cp->addr));
 
-unlock:
        hci_dev_unlock(hdev);
        return err;
 }
@@ -2365,31 +2467,45 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
 
        switch (hdev->discovery.type) {
        case DISCOV_TYPE_BREDR:
-               if (lmp_bredr_capable(hdev))
-                       err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
-               else
-                       err = -ENOTSUPP;
+               if (!lmp_bredr_capable(hdev)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                        MGMT_STATUS_NOT_SUPPORTED);
+                       mgmt_pending_remove(cmd);
+                       goto failed;
+               }
+
+               err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
                break;
 
        case DISCOV_TYPE_LE:
-               if (lmp_host_le_capable(hdev))
-                       err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
-                                         LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
-               else
-                       err = -ENOTSUPP;
+               if (!lmp_host_le_capable(hdev)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                        MGMT_STATUS_NOT_SUPPORTED);
+                       mgmt_pending_remove(cmd);
+                       goto failed;
+               }
+
+               err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
+                                 LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
                break;
 
        case DISCOV_TYPE_INTERLEAVED:
-               if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev))
-                       err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
-                                         LE_SCAN_WIN,
-                                         LE_SCAN_TIMEOUT_BREDR_LE);
-               else
-                       err = -ENOTSUPP;
+               if (!lmp_host_le_capable(hdev) || !lmp_bredr_capable(hdev)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                        MGMT_STATUS_NOT_SUPPORTED);
+                       mgmt_pending_remove(cmd);
+                       goto failed;
+               }
+
+               err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, LE_SCAN_WIN,
+                                 LE_SCAN_TIMEOUT_BREDR_LE);
                break;
 
        default:
-               err = -EINVAL;
+               err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                MGMT_STATUS_INVALID_PARAMS);
+               mgmt_pending_remove(cmd);
+               goto failed;
        }
 
        if (err < 0)
@@ -2510,7 +2626,8 @@ static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data,
                hci_inquiry_cache_update_resolve(hdev, e);
        }
 
-       err = 0;
+       err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME, 0, &cp->addr,
+                          sizeof(cp->addr));
 
 failed:
        hci_dev_unlock(hdev);
@@ -2526,13 +2643,18 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("%s", hdev->name);
 
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &cp->addr, sizeof(cp->addr));
+
        hci_dev_lock(hdev);
 
        err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
        if (err < 0)
                status = MGMT_STATUS_FAILED;
        else
-               status = 0;
+               status = MGMT_STATUS_SUCCESS;
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status,
                           &cp->addr, sizeof(cp->addr));
@@ -2551,13 +2673,18 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("%s", hdev->name);
 
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &cp->addr, sizeof(cp->addr));
+
        hci_dev_lock(hdev);
 
        err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
        if (err < 0)
                status = MGMT_STATUS_INVALID_PARAMS;
        else
-               status = 0;
+               status = MGMT_STATUS_SUCCESS;
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status,
                           &cp->addr, sizeof(cp->addr));
@@ -2612,6 +2739,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
                return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        if (!hdev_is_powered(hdev))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
                                  MGMT_STATUS_NOT_POWERED);
@@ -2659,12 +2790,23 @@ done:
        return err;
 }
 
+static bool ltk_is_valid(struct mgmt_ltk_info *key)
+{
+       if (key->authenticated != 0x00 && key->authenticated != 0x01)
+               return false;
+       if (key->master != 0x00 && key->master != 0x01)
+               return false;
+       if (!bdaddr_type_is_le(key->addr.type))
+               return false;
+       return true;
+}
+
 static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                               void *cp_data, u16 len)
 {
        struct mgmt_cp_load_long_term_keys *cp = cp_data;
        u16 key_count, expected_len;
-       int i;
+       int i, err;
 
        key_count = __le16_to_cpu(cp->key_count);
 
@@ -2674,11 +2816,20 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                BT_ERR("load_keys: expected %u bytes, got %u bytes",
                       len, expected_len);
                return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
-                                 EINVAL);
+                                 MGMT_STATUS_INVALID_PARAMS);
        }
 
        BT_DBG("%s key_count %u", hdev->name, key_count);
 
+       for (i = 0; i < key_count; i++) {
+               struct mgmt_ltk_info *key = &cp->keys[i];
+
+               if (!ltk_is_valid(key))
+                       return cmd_status(sk, hdev->id,
+                                         MGMT_OP_LOAD_LONG_TERM_KEYS,
+                                         MGMT_STATUS_INVALID_PARAMS);
+       }
+
        hci_dev_lock(hdev);
 
        hci_smp_ltks_clear(hdev);
@@ -2698,9 +2849,12 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                            key->enc_size, key->ediv, key->rand);
        }
 
+       err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0,
+                          NULL, 0);
+
        hci_dev_unlock(hdev);
 
-       return 0;
+       return err;
 }
 
 static const struct mgmt_handler {
@@ -2915,6 +3069,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
        mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
 
        if (powered) {
+               u8 link_sec;
+
                if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
                    !lmp_host_ssp_capable(hdev)) {
                        u8 ssp = 1;
@@ -2938,6 +3094,11 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
                                             sizeof(cp), &cp);
                }
 
+               link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
+               if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
+                       hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE,
+                                    sizeof(link_sec), &link_sec);
+
                if (lmp_bredr_capable(hdev)) {
                        set_bredr_scan(hdev);
                        update_class(hdev);
@@ -2946,7 +3107,13 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
                }
        } else {
                u8 status = MGMT_STATUS_NOT_POWERED;
+               u8 zero_cod[] = { 0, 0, 0 };
+
                mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
+
+               if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
+                       mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
+                                  zero_cod, sizeof(zero_cod), NULL);
        }
 
        err = new_settings(hdev, match.sk);
index 57f250c20e399851ca6998d9813dc60e3ffa3455..b5178d62064ec86bcf3f469528ff80ac9668e126 100644 (file)
@@ -900,8 +900,6 @@ static void sco_conn_ready(struct sco_conn *conn)
 
        BT_DBG("conn %p", conn);
 
-       sco_conn_lock(conn);
-
        if (sk) {
                sco_sock_clear_timer(sk);
                bh_lock_sock(sk);
@@ -909,9 +907,13 @@ static void sco_conn_ready(struct sco_conn *conn)
                sk->sk_state_change(sk);
                bh_unlock_sock(sk);
        } else {
+               sco_conn_lock(conn);
+
                parent = sco_get_sock_listen(conn->src);
-               if (!parent)
-                       goto done;
+               if (!parent) {
+                       sco_conn_unlock(conn);
+                       return;
+               }
 
                bh_lock_sock(parent);
 
@@ -919,7 +921,8 @@ static void sco_conn_ready(struct sco_conn *conn)
                                    BTPROTO_SCO, GFP_ATOMIC);
                if (!sk) {
                        bh_unlock_sock(parent);
-                       goto done;
+                       sco_conn_unlock(conn);
+                       return;
                }
 
                sco_sock_init(sk, parent);
@@ -939,10 +942,9 @@ static void sco_conn_ready(struct sco_conn *conn)
                parent->sk_data_ready(parent, 1);
 
                bh_unlock_sock(parent);
-       }
 
-done:
-       sco_conn_unlock(conn);
+               sco_conn_unlock(conn);
+       }
 }
 
 /* ----- SCO interface with lower layer (HCI) ----- */
index 45b63ca5b48c802fd3a3377a52a93c98582b72d7..f3a58afb821ebcb92fa0a5913d3281412a0f47e8 100644 (file)
@@ -1406,10 +1406,10 @@ static void tcp_service_net_dma(struct sock *sk, bool wait)
                return;
 
        last_issued = tp->ucopy.dma_cookie;
-       dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+       dma_async_issue_pending(tp->ucopy.dma_chan);
 
        do {
-               if (dma_async_memcpy_complete(tp->ucopy.dma_chan,
+               if (dma_async_is_tx_complete(tp->ucopy.dma_chan,
                                              last_issued, &done,
                                              &used) == DMA_SUCCESS) {
                        /* Safe to free early-copied skbs now */
@@ -1751,7 +1751,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                                tcp_service_net_dma(sk, true);
                                tcp_cleanup_rbuf(sk, copied);
                        } else
-                               dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+                               dma_async_issue_pending(tp->ucopy.dma_chan);
                }
 #endif
                if (copied >= target) {
@@ -1844,7 +1844,7 @@ do_prequeue:
                                        break;
                                }
 
-                               dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+                               dma_async_issue_pending(tp->ucopy.dma_chan);
 
                                if ((offset + used) == skb->len)
                                        copied_early = true;
index 808338a1bce54666284c9b8f0c6c6c372d71326c..31bf2586fb84a59a0b8ce964c1717e2555a7ea65 100644 (file)
@@ -83,8 +83,8 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
        if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP,
                             &sta->sta, tid, NULL, 0))
                sdata_info(sta->sdata,
-                          "HW problem - can not stop rx aggregation for tid %d\n",
-                          tid);
+                          "HW problem - can not stop rx aggregation for %pM tid %d\n",
+                          sta->sta.addr, tid);
 
        /* check if this is a self generated aggregation halt */
        if (initiator == WLAN_BACK_RECIPIENT && tx)
@@ -159,7 +159,8 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)
        }
        rcu_read_unlock();
 
-       ht_dbg(sta->sdata, "rx session timer expired on tid %d\n", (u16)*ptid);
+       ht_dbg(sta->sdata, "RX session timer expired on %pM tid %d\n",
+              sta->sta.addr, (u16)*ptid);
 
        set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired);
        ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
@@ -247,7 +248,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
        status = WLAN_STATUS_REQUEST_DECLINED;
 
        if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
-               ht_dbg(sta->sdata, "Suspend in progress - Denying ADDBA request\n");
+               ht_dbg(sta->sdata,
+                      "Suspend in progress - Denying ADDBA request (%pM tid %d)\n",
+                      sta->sta.addr, tid);
                goto end_no_lock;
        }
 
@@ -317,7 +320,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
 
        ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
                               &sta->sta, tid, &start_seq_num, 0);
-       ht_dbg(sta->sdata, "Rx A-MPDU request on tid %d result %d\n", tid, ret);
+       ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
+              sta->sta.addr, tid, ret);
        if (ret) {
                kfree(tid_agg_rx->reorder_buf);
                kfree(tid_agg_rx->reorder_time);
index eb9df22418f08106241c51e6646b3c3d25c2f128..13b7683de5a455fe222c4148dc807471fe3b74bd 100644 (file)
@@ -149,16 +149,133 @@ void ieee80211_assign_tid_tx(struct sta_info *sta, int tid,
        rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
 }
 
+static inline int ieee80211_ac_from_tid(int tid)
+{
+       return ieee802_1d_to_ac[tid & 7];
+}
+
+/*
+ * When multiple aggregation sessions on multiple stations
+ * are being created/destroyed simultaneously, we need to
+ * refcount the global queue stop caused by that in order
+ * to not get into a situation where one of the aggregation
+ * setup or teardown re-enables queues before the other is
+ * ready to handle that.
+ *
+ * These two functions take care of this issue by keeping
+ * a global "agg_queue_stop" refcount.
+ */
+static void __acquires(agg_queue)
+ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
+{
+       int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
+
+       if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1)
+               ieee80211_stop_queue_by_reason(
+                       &sdata->local->hw, queue,
+                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+       __acquire(agg_queue);
+}
+
+static void __releases(agg_queue)
+ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
+{
+       int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
+
+       if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0)
+               ieee80211_wake_queue_by_reason(
+                       &sdata->local->hw, queue,
+                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+       __release(agg_queue);
+}
+
+/*
+ * splice packets from the STA's pending to the local pending,
+ * requires a call to ieee80211_agg_splice_finish later
+ */
+static void __acquires(agg_queue)
+ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata,
+                            struct tid_ampdu_tx *tid_tx, u16 tid)
+{
+       struct ieee80211_local *local = sdata->local;
+       int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
+       unsigned long flags;
+
+       ieee80211_stop_queue_agg(sdata, tid);
+
+       if (WARN(!tid_tx,
+                "TID %d gone but expected when splicing aggregates from the pending queue\n",
+                tid))
+               return;
+
+       if (!skb_queue_empty(&tid_tx->pending)) {
+               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+               /* copy over remaining packets */
+               skb_queue_splice_tail_init(&tid_tx->pending,
+                                          &local->pending[queue]);
+               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+       }
+}
+
+static void __releases(agg_queue)
+ieee80211_agg_splice_finish(struct ieee80211_sub_if_data *sdata, u16 tid)
+{
+       ieee80211_wake_queue_agg(sdata, tid);
+}
+
+static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid)
+{
+       struct tid_ampdu_tx *tid_tx;
+
+       lockdep_assert_held(&sta->ampdu_mlme.mtx);
+       lockdep_assert_held(&sta->lock);
+
+       tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
+
+       /*
+        * When we get here, the TX path will not be lockless any more wrt.
+        * aggregation, since the OPERATIONAL bit has long been cleared.
+        * Thus it will block on getting the lock, if it occurs. So if we
+        * stop the queue now, we will not get any more packets, and any
+        * that might be being processed will wait for us here, thereby
+        * guaranteeing that no packets go to the tid_tx pending queue any
+        * more.
+        */
+
+       ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid);
+
+       /* future packets must not find the tid_tx struct any more */
+       ieee80211_assign_tid_tx(sta, tid, NULL);
+
+       ieee80211_agg_splice_finish(sta->sdata, tid);
+
+       kfree_rcu(tid_tx, rcu_head);
+}
+
 int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
-                                   enum ieee80211_back_parties initiator,
-                                   bool tx)
+                                   enum ieee80211_agg_stop_reason reason)
 {
        struct ieee80211_local *local = sta->local;
        struct tid_ampdu_tx *tid_tx;
+       enum ieee80211_ampdu_mlme_action action;
        int ret;
 
        lockdep_assert_held(&sta->ampdu_mlme.mtx);
 
+       switch (reason) {
+       case AGG_STOP_DECLINED:
+       case AGG_STOP_LOCAL_REQUEST:
+       case AGG_STOP_PEER_REQUEST:
+               action = IEEE80211_AMPDU_TX_STOP_CONT;
+               break;
+       case AGG_STOP_DESTROY_STA:
+               action = IEEE80211_AMPDU_TX_STOP_FLUSH;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return -EINVAL;
+       }
+
        spin_lock_bh(&sta->lock);
 
        tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
@@ -167,10 +284,19 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                return -ENOENT;
        }
 
-       /* if we're already stopping ignore any new requests to stop */
+       /*
+        * if we're already stopping ignore any new requests to stop
+        * unless we're destroying it in which case notify the driver
+        */
        if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
                spin_unlock_bh(&sta->lock);
-               return -EALREADY;
+               if (reason != AGG_STOP_DESTROY_STA)
+                       return -EALREADY;
+               ret = drv_ampdu_action(local, sta->sdata,
+                                      IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
+                                      &sta->sta, tid, NULL, 0);
+               WARN_ON_ONCE(ret);
+               return 0;
        }
 
        if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
@@ -212,11 +338,12 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
         */
        synchronize_net();
 
-       tid_tx->stop_initiator = initiator;
-       tid_tx->tx_stop = tx;
+       tid_tx->stop_initiator = reason == AGG_STOP_PEER_REQUEST ?
+                                       WLAN_BACK_RECIPIENT :
+                                       WLAN_BACK_INITIATOR;
+       tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST;
 
-       ret = drv_ampdu_action(local, sta->sdata,
-                              IEEE80211_AMPDU_TX_STOP,
+       ret = drv_ampdu_action(local, sta->sdata, action,
                               &sta->sta, tid, NULL, 0);
 
        /* HW shall not deny going back to legacy */
@@ -227,7 +354,17 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                 */
        }
 
-       return ret;
+       /*
+        * In the case of AGG_STOP_DESTROY_STA, the driver won't
+        * necessarily call ieee80211_stop_tx_ba_cb(), so this may
+        * seem like we can leave the tid_tx data pending forever.
+        * This is true, in a way, but "forever" is only until the
+        * station struct is actually destroyed. In the meantime,
+        * leaving it around ensures that we don't transmit packets
+        * to the driver on this TID which might confuse it.
+        */
+
+       return 0;
 }
 
 /*
@@ -253,91 +390,18 @@ static void sta_addba_resp_timer_expired(unsigned long data)
            test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) {
                rcu_read_unlock();
                ht_dbg(sta->sdata,
-                      "timer expired on tid %d but we are not (or no longer) expecting addBA response there\n",
-                      tid);
+                      "timer expired on %pM tid %d but we are not (or no longer) expecting addBA response there\n",
+                      sta->sta.addr, tid);
                return;
        }
 
-       ht_dbg(sta->sdata, "addBA response timer expired on tid %d\n", tid);
+       ht_dbg(sta->sdata, "addBA response timer expired on %pM tid %d\n",
+              sta->sta.addr, tid);
 
        ieee80211_stop_tx_ba_session(&sta->sta, tid);
        rcu_read_unlock();
 }
 
-static inline int ieee80211_ac_from_tid(int tid)
-{
-       return ieee802_1d_to_ac[tid & 7];
-}
-
-/*
- * When multiple aggregation sessions on multiple stations
- * are being created/destroyed simultaneously, we need to
- * refcount the global queue stop caused by that in order
- * to not get into a situation where one of the aggregation
- * setup or teardown re-enables queues before the other is
- * ready to handle that.
- *
- * These two functions take care of this issue by keeping
- * a global "agg_queue_stop" refcount.
- */
-static void __acquires(agg_queue)
-ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
-{
-       int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
-
-       if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1)
-               ieee80211_stop_queue_by_reason(
-                       &sdata->local->hw, queue,
-                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
-       __acquire(agg_queue);
-}
-
-static void __releases(agg_queue)
-ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
-{
-       int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
-
-       if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0)
-               ieee80211_wake_queue_by_reason(
-                       &sdata->local->hw, queue,
-                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
-       __release(agg_queue);
-}
-
-/*
- * splice packets from the STA's pending to the local pending,
- * requires a call to ieee80211_agg_splice_finish later
- */
-static void __acquires(agg_queue)
-ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata,
-                            struct tid_ampdu_tx *tid_tx, u16 tid)
-{
-       struct ieee80211_local *local = sdata->local;
-       int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
-       unsigned long flags;
-
-       ieee80211_stop_queue_agg(sdata, tid);
-
-       if (WARN(!tid_tx,
-                "TID %d gone but expected when splicing aggregates from the pending queue\n",
-                tid))
-               return;
-
-       if (!skb_queue_empty(&tid_tx->pending)) {
-               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-               /* copy over remaining packets */
-               skb_queue_splice_tail_init(&tid_tx->pending,
-                                          &local->pending[queue]);
-               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
-       }
-}
-
-static void __releases(agg_queue)
-ieee80211_agg_splice_finish(struct ieee80211_sub_if_data *sdata, u16 tid)
-{
-       ieee80211_wake_queue_agg(sdata, tid);
-}
-
 void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
 {
        struct tid_ampdu_tx *tid_tx;
@@ -369,7 +433,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
                               &sta->sta, tid, &start_seq_num, 0);
        if (ret) {
                ht_dbg(sdata,
-                      "BA request denied - HW unavailable for tid %d\n", tid);
+                      "BA request denied - HW unavailable for %pM tid %d\n",
+                      sta->sta.addr, tid);
                spin_lock_bh(&sta->lock);
                ieee80211_agg_splice_packets(sdata, tid_tx, tid);
                ieee80211_assign_tid_tx(sta, tid, NULL);
@@ -382,7 +447,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
 
        /* activate the timer for the recipient's addBA response */
        mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
-       ht_dbg(sdata, "activated addBA response timer on tid %d\n", tid);
+       ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n",
+              sta->sta.addr, tid);
 
        spin_lock_bh(&sta->lock);
        sta->ampdu_mlme.last_addba_req_time[tid] = jiffies;
@@ -429,7 +495,8 @@ static void sta_tx_agg_session_timer_expired(unsigned long data)
 
        rcu_read_unlock();
 
-       ht_dbg(sta->sdata, "tx session timer expired on tid %d\n", (u16)*ptid);
+       ht_dbg(sta->sdata, "tx session timer expired on %pM tid %d\n",
+              sta->sta.addr, (u16)*ptid);
 
        ieee80211_stop_tx_ba_session(&sta->sta, *ptid);
 }
@@ -465,7 +532,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
 
        if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
                ht_dbg(sdata,
-                      "BA sessions blocked - Denying BA session request\n");
+                      "BA sessions blocked - Denying BA session request %pM tid %d\n",
+                      sta->sta.addr, tid);
                return -EINVAL;
        }
 
@@ -506,8 +574,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
            time_before(jiffies, sta->ampdu_mlme.last_addba_req_time[tid] +
                        HT_AGG_RETRIES_PERIOD)) {
                ht_dbg(sdata,
-                      "BA request denied - waiting a grace period after %d failed requests on tid %u\n",
-                      sta->ampdu_mlme.addba_req_num[tid], tid);
+                      "BA request denied - waiting a grace period after %d failed requests on %pM tid %u\n",
+                      sta->ampdu_mlme.addba_req_num[tid], sta->sta.addr, tid);
                ret = -EBUSY;
                goto err_unlock_sta;
        }
@@ -516,8 +584,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
        /* check if the TID is not in aggregation flow already */
        if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) {
                ht_dbg(sdata,
-                      "BA request denied - session is not idle on tid %u\n",
-                      tid);
+                      "BA request denied - session is not idle on %pM tid %u\n",
+                      sta->sta.addr, tid);
                ret = -EAGAIN;
                goto err_unlock_sta;
        }
@@ -572,7 +640,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
 
        tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
 
-       ht_dbg(sta->sdata, "Aggregation is on for tid %d\n", tid);
+       ht_dbg(sta->sdata, "Aggregation is on for %pM tid %d\n",
+              sta->sta.addr, tid);
 
        drv_ampdu_action(local, sta->sdata,
                         IEEE80211_AMPDU_TX_OPERATIONAL,
@@ -660,14 +729,13 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,
 EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe);
 
 int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
-                                  enum ieee80211_back_parties initiator,
-                                  bool tx)
+                                  enum ieee80211_agg_stop_reason reason)
 {
        int ret;
 
        mutex_lock(&sta->ampdu_mlme.mtx);
 
-       ret = ___ieee80211_stop_tx_ba_session(sta, tid, initiator, tx);
+       ret = ___ieee80211_stop_tx_ba_session(sta, tid, reason);
 
        mutex_unlock(&sta->ampdu_mlme.mtx);
 
@@ -743,7 +811,9 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
        tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
 
        if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
-               ht_dbg(sdata, "unexpected callback to A-MPDU stop\n");
+               ht_dbg(sdata,
+                      "unexpected callback to A-MPDU stop for %pM tid %d\n",
+                      sta->sta.addr, tid);
                goto unlock_sta;
        }
 
@@ -751,24 +821,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
                ieee80211_send_delba(sta->sdata, ra, tid,
                        WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
 
-       /*
-        * When we get here, the TX path will not be lockless any more wrt.
-        * aggregation, since the OPERATIONAL bit has long been cleared.
-        * Thus it will block on getting the lock, if it occurs. So if we
-        * stop the queue now, we will not get any more packets, and any
-        * that might be being processed will wait for us here, thereby
-        * guaranteeing that no packets go to the tid_tx pending queue any
-        * more.
-        */
-
-       ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid);
-
-       /* future packets must not find the tid_tx struct any more */
-       ieee80211_assign_tid_tx(sta, tid, NULL);
-
-       ieee80211_agg_splice_finish(sta->sdata, tid);
-
-       kfree_rcu(tid_tx, rcu_head);
+       ieee80211_remove_tid_tx(sta, tid);
 
  unlock_sta:
        spin_unlock_bh(&sta->lock);
@@ -819,13 +872,15 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
                goto out;
 
        if (mgmt->u.action.u.addba_resp.dialog_token != tid_tx->dialog_token) {
-               ht_dbg(sta->sdata, "wrong addBA response token, tid %d\n", tid);
+               ht_dbg(sta->sdata, "wrong addBA response token, %pM tid %d\n",
+                      sta->sta.addr, tid);
                goto out;
        }
 
        del_timer_sync(&tid_tx->addba_resp_timer);
 
-       ht_dbg(sta->sdata, "switched off addBA timer for tid %d\n", tid);
+       ht_dbg(sta->sdata, "switched off addBA timer for %pM tid %d\n",
+              sta->sta.addr, tid);
 
        /*
         * addba_resp_timer may have fired before we got here, and
@@ -835,8 +890,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
        if (test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state) ||
            test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
                ht_dbg(sta->sdata,
-                      "got addBA resp for tid %d but we already gave up\n",
-                      tid);
+                      "got addBA resp for %pM tid %d but we already gave up\n",
+                      sta->sta.addr, tid);
                goto out;
        }
 
@@ -868,8 +923,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
                }
 
        } else {
-               ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR,
-                                               false);
+               ___ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_DECLINED);
        }
 
  out:
index 0479c64aa83cc8beb27a90dedd1340cbeb2065a2..15d886c639e9d92d9024d0fbf13a804301d819c4 100644 (file)
@@ -520,6 +520,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                                BIT(NL80211_STA_FLAG_WME) |
                                BIT(NL80211_STA_FLAG_MFP) |
                                BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                               BIT(NL80211_STA_FLAG_ASSOCIATED) |
                                BIT(NL80211_STA_FLAG_TDLS_PEER);
        if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
@@ -531,6 +532,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
        if (test_sta_flag(sta, WLAN_STA_AUTH))
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+       if (test_sta_flag(sta, WLAN_STA_ASSOC))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
 }
@@ -940,6 +943,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
 
        sdata->vif.bss_conf.beacon_int = params->beacon_interval;
        sdata->vif.bss_conf.dtim_period = params->dtim_period;
+       sdata->vif.bss_conf.enable_beacon = true;
 
        sdata->vif.bss_conf.ssid_len = params->ssid_len;
        if (params->ssid_len)
@@ -1020,8 +1024,15 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
                kfree_rcu(old_probe_resp, rcu_head);
 
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
-               sta_info_flush(local, vlan);
-       sta_info_flush(local, sdata);
+               sta_info_flush_defer(vlan);
+       sta_info_flush_defer(sdata);
+       rcu_barrier();
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+               sta_info_flush_cleanup(vlan);
+       sta_info_flush_cleanup(sdata);
+
+       sdata->vif.bss_conf.enable_beacon = false;
+       clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
 
        drv_stop_ap(sdata->local, sdata);
@@ -1079,6 +1090,58 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)
        netif_rx_ni(skb);
 }
 
+static int sta_apply_auth_flags(struct ieee80211_local *local,
+                               struct sta_info *sta,
+                               u32 mask, u32 set)
+{
+       int ret;
+
+       if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+           set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+           !test_sta_flag(sta, WLAN_STA_AUTH)) {
+               ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
+               if (ret)
+                       return ret;
+       }
+
+       if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+           set & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+           !test_sta_flag(sta, WLAN_STA_ASSOC)) {
+               ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+               if (ret)
+                       return ret;
+       }
+
+       if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
+               if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
+                       ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+               else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                       ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+               else
+                       ret = 0;
+               if (ret)
+                       return ret;
+       }
+
+       if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+           !(set & BIT(NL80211_STA_FLAG_ASSOCIATED)) &&
+           test_sta_flag(sta, WLAN_STA_ASSOC)) {
+               ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
+               if (ret)
+                       return ret;
+       }
+
+       if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+           !(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
+           test_sta_flag(sta, WLAN_STA_AUTH)) {
+               ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static int sta_apply_parameters(struct ieee80211_local *local,
                                struct sta_info *sta,
                                struct station_parameters *params)
@@ -1096,52 +1159,20 @@ static int sta_apply_parameters(struct ieee80211_local *local,
        mask = params->sta_flags_mask;
        set = params->sta_flags_set;
 
-       /*
-        * In mesh mode, we can clear AUTHENTICATED flag but must
-        * also make ASSOCIATED follow appropriately for the driver
-        * API. See also below, after AUTHORIZED changes.
-        */
-       if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
-               /* cfg80211 should not allow this in non-mesh modes */
-               if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif)))
-                       return -EINVAL;
-
-               if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
-                   !test_sta_flag(sta, WLAN_STA_AUTH)) {
-                       ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
-                       if (ret)
-                               return ret;
-                       ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
-                       if (ret)
-                               return ret;
-               }
-       }
-
-       if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
-               if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
-                       ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
-               else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
-                       ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
-               if (ret)
-                       return ret;
-       }
-
-       if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
-               /* cfg80211 should not allow this in non-mesh modes */
-               if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif)))
-                       return -EINVAL;
-
-               if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
-                   test_sta_flag(sta, WLAN_STA_AUTH)) {
-                       ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
-                       if (ret)
-                               return ret;
-                       ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
-                       if (ret)
-                               return ret;
-               }
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               /*
+                * In mesh mode, ASSOCIATED isn't part of the nl80211
+                * API but must follow AUTHENTICATED for driver state.
+                */
+               if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED))
+                       mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+               if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
+                       set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
        }
 
+       ret = sta_apply_auth_flags(local, sta, mask, set);
+       if (ret)
+               return ret;
 
        if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
                if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
@@ -1187,10 +1218,11 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                sta->sta.aid = params->aid;
 
        /*
-        * FIXME: updating the following information is racy when this
-        *        function is called from ieee80211_change_station().
-        *        However, all this information should be static so
-        *        maybe we should just reject attemps to change it.
+        * Some of the following updates would be racy if called on an
+        * existing station, via ieee80211_change_station(). However,
+        * all such changes are rejected by cfg80211 except for updates
+        * changing the supported rates on an existing but not yet used
+        * TDLS peer.
         */
 
        if (params->listen_interval >= 0)
@@ -1221,18 +1253,33 @@ static int sta_apply_parameters(struct ieee80211_local *local,
 
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CONFIG_MAC80211_MESH
-               if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED)
+               if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) {
+                       u32 changed = 0;
+
                        switch (params->plink_state) {
-                       case NL80211_PLINK_LISTEN:
                        case NL80211_PLINK_ESTAB:
+                               if (sta->plink_state != NL80211_PLINK_ESTAB)
+                                       changed = mesh_plink_inc_estab_count(
+                                                       sdata);
+                               sta->plink_state = params->plink_state;
+                               break;
+                       case NL80211_PLINK_LISTEN:
                        case NL80211_PLINK_BLOCKED:
+                       case NL80211_PLINK_OPN_SNT:
+                       case NL80211_PLINK_OPN_RCVD:
+                       case NL80211_PLINK_CNF_RCVD:
+                       case NL80211_PLINK_HOLDING:
+                               if (sta->plink_state == NL80211_PLINK_ESTAB)
+                                       changed = mesh_plink_dec_estab_count(
+                                                       sdata);
                                sta->plink_state = params->plink_state;
                                break;
                        default:
                                /*  nothing  */
                                break;
                        }
-               else
+                       ieee80211_bss_info_change_notify(sdata, changed);
+               } else {
                        switch (params->plink_action) {
                        case PLINK_ACTION_OPEN:
                                mesh_plink_open(sta);
@@ -1241,6 +1288,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                mesh_plink_block(sta);
                                break;
                        }
+               }
 #endif
        }
 
@@ -1275,6 +1323,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        if (!sta)
                return -ENOMEM;
 
+       /*
+        * defaults -- if userspace wants something else we'll
+        * change it accordingly in sta_apply_parameters()
+        */
        sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
        sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
 
@@ -1311,7 +1363,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
                                 u8 *mac)
 {
-       struct ieee80211_local *local = wiphy_priv(wiphy);
        struct ieee80211_sub_if_data *sdata;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1319,7 +1370,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
        if (mac)
                return sta_info_destroy_addr_bss(sdata, mac);
 
-       sta_info_flush(local, sdata);
+       sta_info_flush(sdata);
        return 0;
 }
 
@@ -1625,6 +1676,9 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
        memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate,
                                                sizeof(setup->mcast_rate));
 
+       sdata->vif.bss_conf.beacon_int = setup->beacon_interval;
+       sdata->vif.bss_conf.dtim_period = setup->dtim_period;
+
        return 0;
 }
 
@@ -2208,7 +2262,8 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 
-       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+       if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
                return -EOPNOTSUPP;
 
        if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
index 80e55527504b91cd71b6d33a946507ad10b59c7e..1bfe0a8b19d222c95c3878d157114e9e85a0443b 100644 (file)
@@ -381,7 +381,8 @@ void ieee80211_iter_chan_contexts_atomic(
 
        rcu_read_lock();
        list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
-               iter(hw, &ctx->conf, iter_data);
+               if (ctx->driver_present)
+                       iter(hw, &ctx->conf, iter_data);
        rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
index 698dc7e6f309847373541748c3cc4a2f40b67f58..434b3c4f31b568a1fc3220764412743da0bcc130 100644 (file)
@@ -207,6 +207,14 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
 {
        might_sleep();
 
+       WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON |
+                               BSS_CHANGED_BEACON_ENABLED) &&
+                    sdata->vif.type != NL80211_IFTYPE_AP &&
+                    sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+                    sdata->vif.type != NL80211_IFTYPE_MESH_POINT);
+       WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE &&
+                    changed & ~BSS_CHANGED_IDLE);
+
        check_sdata_in_driver(sdata);
 
        trace_drv_bss_info_changed(local, sdata, info, changed);
@@ -561,7 +569,8 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local,
        check_sdata_in_driver(sdata);
 
        WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED &&
-               sdata->vif.type != NL80211_IFTYPE_ADHOC);
+               (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+                sdata->vif.type != NL80211_IFTYPE_MESH_POINT));
 
        trace_drv_sta_rc_update(local, sdata, sta, changed);
        if (local->ops->sta_rc_update)
@@ -837,11 +846,12 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local,
 }
 
 static inline void drv_rssi_callback(struct ieee80211_local *local,
+                                    struct ieee80211_sub_if_data *sdata,
                                     const enum ieee80211_rssi_event event)
 {
-       trace_drv_rssi_callback(local, event);
+       trace_drv_rssi_callback(local, sdata, event);
        if (local->ops->rssi_callback)
-               local->ops->rssi_callback(&local->hw, event);
+               local->ops->rssi_callback(&local->hw, &sdata->vif, event);
        trace_drv_return_void(local);
 }
 
@@ -913,6 +923,8 @@ static inline int drv_add_chanctx(struct ieee80211_local *local,
        if (local->ops->add_chanctx)
                ret = local->ops->add_chanctx(&local->hw, &ctx->conf);
        trace_drv_return_int(local, ret);
+       if (!ret)
+               ctx->driver_present = true;
 
        return ret;
 }
@@ -924,6 +936,7 @@ static inline void drv_remove_chanctx(struct ieee80211_local *local,
        if (local->ops->remove_chanctx)
                local->ops->remove_chanctx(&local->hw, &ctx->conf);
        trace_drv_return_void(local);
+       ctx->driver_present = false;
 }
 
 static inline void drv_change_chanctx(struct ieee80211_local *local,
@@ -931,8 +944,10 @@ static inline void drv_change_chanctx(struct ieee80211_local *local,
                                      u32 changed)
 {
        trace_drv_change_chanctx(local, ctx, changed);
-       if (local->ops->change_chanctx)
+       if (local->ops->change_chanctx) {
+               WARN_ON_ONCE(!ctx->driver_present);
                local->ops->change_chanctx(&local->hw, &ctx->conf, changed);
+       }
        trace_drv_return_void(local);
 }
 
@@ -945,10 +960,12 @@ static inline int drv_assign_vif_chanctx(struct ieee80211_local *local,
        check_sdata_in_driver(sdata);
 
        trace_drv_assign_vif_chanctx(local, sdata, ctx);
-       if (local->ops->assign_vif_chanctx)
+       if (local->ops->assign_vif_chanctx) {
+               WARN_ON_ONCE(!ctx->driver_present);
                ret = local->ops->assign_vif_chanctx(&local->hw,
                                                     &sdata->vif,
                                                     &ctx->conf);
+       }
        trace_drv_return_int(local, ret);
 
        return ret;
@@ -961,10 +978,12 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
        check_sdata_in_driver(sdata);
 
        trace_drv_unassign_vif_chanctx(local, sdata, ctx);
-       if (local->ops->unassign_vif_chanctx)
+       if (local->ops->unassign_vif_chanctx) {
+               WARN_ON_ONCE(!ctx->driver_present);
                local->ops->unassign_vif_chanctx(&local->hw,
                                                 &sdata->vif,
                                                 &ctx->conf);
+       }
        trace_drv_return_void(local);
 }
 
@@ -1003,4 +1022,32 @@ static inline void drv_restart_complete(struct ieee80211_local *local)
        trace_drv_return_void(local);
 }
 
+static inline void
+drv_set_default_unicast_key(struct ieee80211_local *local,
+                           struct ieee80211_sub_if_data *sdata,
+                           int key_idx)
+{
+       check_sdata_in_driver(sdata);
+
+       WARN_ON_ONCE(key_idx < -1 || key_idx > 3);
+
+       trace_drv_set_default_unicast_key(local, sdata, key_idx);
+       if (local->ops->set_default_unicast_key)
+               local->ops->set_default_unicast_key(&local->hw, &sdata->vif,
+                                                   key_idx);
+       trace_drv_return_void(local);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static inline void drv_ipv6_addr_change(struct ieee80211_local *local,
+                                       struct ieee80211_sub_if_data *sdata,
+                                       struct inet6_dev *idev)
+{
+       trace_drv_ipv6_addr_change(local, sdata);
+       if (local->ops->ipv6_addr_change)
+               local->ops->ipv6_addr_change(&local->hw, &sdata->vif, idev);
+       trace_drv_return_void(local);
+}
+#endif
+
 #endif /* __MAC80211_DRIVER_OPS */
index a71d891794a40ab96134ec16eef5568e8b5b3eca..61ac7c48ac0c48dfff325c5d11bdcf5ef549f907 100644 (file)
@@ -62,6 +62,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40);
        __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40);
 
+       /* Allow user to disable SGI-20 (SGI-40 is handled above) */
+       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_20);
+
        /* Allow user to disable the max-AMSDU bit. */
        __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU);
 
@@ -117,6 +120,21 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
                   IEEE80211_HT_CAP_SGI_20 |
                   IEEE80211_HT_CAP_SGI_40 |
                   IEEE80211_HT_CAP_DSSSCCK40));
+
+       /* Unset 40 MHz if we're not using a 40 MHz channel */
+       switch (sdata->vif.bss_conf.chandef.width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+       case NL80211_CHAN_WIDTH_20:
+               ht_cap->cap &= ~IEEE80211_HT_CAP_SGI_40;
+               ht_cap->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+               break;
+       case NL80211_CHAN_WIDTH_40:
+       case NL80211_CHAN_WIDTH_80:
+       case NL80211_CHAN_WIDTH_80P80:
+       case NL80211_CHAN_WIDTH_160:
+               break;
+       }
+
        /*
         * The STBC bits are asymmetric -- if we don't have
         * TX then mask out the peer's RX and vice versa.
@@ -179,16 +197,19 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
        ieee80211_apply_htcap_overrides(sdata, ht_cap);
 }
 
-void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx)
+void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
+                                        enum ieee80211_agg_stop_reason reason)
 {
        int i;
 
        cancel_work_sync(&sta->ampdu_mlme.work);
 
        for (i = 0; i <  IEEE80211_NUM_TIDS; i++) {
-               __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx);
+               __ieee80211_stop_tx_ba_session(sta, i, reason);
                __ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT,
-                                              WLAN_REASON_QSTA_LEAVE_QBSS, tx);
+                                              WLAN_REASON_QSTA_LEAVE_QBSS,
+                                              reason != AGG_STOP_DESTROY_STA &&
+                                              reason != AGG_STOP_PEER_REQUEST);
        }
 }
 
@@ -245,8 +266,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
                if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP,
                                                 &tid_tx->state))
                        ___ieee80211_stop_tx_ba_session(sta, tid,
-                                                       WLAN_BACK_INITIATOR,
-                                                       true);
+                                                       AGG_STOP_LOCAL_REQUEST);
        }
        mutex_unlock(&sta->ampdu_mlme.mtx);
 }
@@ -314,8 +334,7 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
                __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0,
                                               true);
        else
-               __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
-                                              true);
+               __ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST);
 }
 
 int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
index 6b7644e818d8f30629513318d4f9a17e4e622606..b4b866f41919a5c64b73837df29b301860702c25 100644 (file)
@@ -67,7 +67,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        skb_reserve(skb, sdata->local->hw.extra_tx_headroom);
 
        if (!ether_addr_equal(ifibss->bssid, bssid))
-               sta_info_flush(sdata->local, sdata);
+               sta_info_flush(sdata);
 
        /* if merging, indicate to driver that we leave the old IBSS */
        if (sdata->vif.bss_conf.ibss_joined) {
@@ -191,6 +191,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        rcu_assign_pointer(ifibss->presp, skb);
 
+       sdata->vif.bss_conf.enable_beacon = true;
        sdata->vif.bss_conf.beacon_int = beacon_int;
        sdata->vif.bss_conf.basic_rates = basic_rates;
        bss_change = BSS_CHANGED_BEACON_INT;
@@ -425,11 +426,9 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
 }
 
 static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
-                                 struct ieee80211_mgmt *mgmt,
-                                 size_t len,
+                                 struct ieee80211_mgmt *mgmt, size_t len,
                                  struct ieee80211_rx_status *rx_status,
-                                 struct ieee802_11_elems *elems,
-                                 bool beacon)
+                                 struct ieee802_11_elems *elems)
 {
        struct ieee80211_local *local = sdata->local;
        int freq;
@@ -530,7 +529,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        }
 
        bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
-                                       channel, beacon);
+                                       channel);
        if (!bss)
                return;
 
@@ -877,14 +876,21 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
        ieee80211_tx_skb(sdata, skb);
 }
 
-static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
-                                        struct ieee80211_mgmt *mgmt,
-                                        size_t len,
-                                        struct ieee80211_rx_status *rx_status)
+static
+void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
+                                   struct ieee80211_mgmt *mgmt, size_t len,
+                                   struct ieee80211_rx_status *rx_status)
 {
        size_t baselen;
        struct ieee802_11_elems elems;
 
+       BUILD_BUG_ON(offsetof(typeof(mgmt->u.probe_resp), variable) !=
+                    offsetof(typeof(mgmt->u.beacon), variable));
+
+       /*
+        * either beacon or probe_resp but the variable field is at the
+        * same offset
+        */
        baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
        if (baselen > len)
                return;
@@ -892,25 +898,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
        ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
                                &elems);
 
-       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
-}
-
-static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_mgmt *mgmt,
-                                    size_t len,
-                                    struct ieee80211_rx_status *rx_status)
-{
-       size_t baselen;
-       struct ieee802_11_elems elems;
-
-       /* Process beacon from the current BSS */
-       baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
-       if (baselen > len)
-               return;
-
-       ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
-
-       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true);
+       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
 }
 
 void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
@@ -934,12 +922,9 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                ieee80211_rx_mgmt_probe_req(sdata, skb);
                break;
        case IEEE80211_STYPE_PROBE_RESP:
-               ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len,
-                                            rx_status);
-               break;
        case IEEE80211_STYPE_BEACON:
-               ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
-                                        rx_status);
+               ieee80211_rx_mgmt_probe_beacon(sdata, mgmt, skb->len,
+                                              rx_status);
                break;
        case IEEE80211_STYPE_AUTH:
                ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
@@ -1182,7 +1167,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        memset(ifibss->bssid, 0, ETH_ALEN);
        ifibss->ssid_len = 0;
 
-       sta_info_flush(sdata->local, sdata);
+       sta_info_flush(sdata);
 
        spin_lock_bh(&ifibss->incomplete_lock);
        while (!list_empty(&ifibss->incomplete_stations)) {
@@ -1205,6 +1190,8 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
        sdata->vif.bss_conf.ibss_joined = false;
        sdata->vif.bss_conf.ibss_creator = false;
+       sdata->vif.bss_conf.enable_beacon = false;
+       clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
                                                BSS_CHANGED_IBSS);
        synchronize_rcu();
index 2ed065c095629403c42574872fec6a8b85819871..5fba867d9e2e0dc60109cc5dc006d302ad3763eb 100644 (file)
@@ -405,6 +405,8 @@ struct ieee80211_mgd_assoc_data {
 
        u8 ap_ht_param;
 
+       struct ieee80211_vht_cap ap_vht_cap;
+
        size_t ie_len;
        u8 ie[];
 };
@@ -659,10 +661,13 @@ enum ieee80211_sub_if_data_flags {
  *     change handling while the interface is up
  * @SDATA_STATE_OFFCHANNEL: This interface is currently in offchannel
  *     mode, so queues are stopped
+ * @SDATA_STATE_OFFCHANNEL_BEACON_STOPPED: Beaconing was stopped due
+ *     to offchannel, reset when offchannel returns
  */
 enum ieee80211_sdata_state_bits {
        SDATA_STATE_RUNNING,
        SDATA_STATE_OFFCHANNEL,
+       SDATA_STATE_OFFCHANNEL_BEACON_STOPPED,
 };
 
 /**
@@ -685,6 +690,7 @@ struct ieee80211_chanctx {
 
        enum ieee80211_chanctx_mode mode;
        int refcount;
+       bool driver_present;
 
        struct ieee80211_chanctx_conf conf;
 };
@@ -741,8 +747,6 @@ struct ieee80211_sub_if_data {
        struct work_struct work;
        struct sk_buff_head skb_queue;
 
-       bool arp_filter_state;
-
        u8 needed_rx_chains;
        enum ieee80211_smps_mode smps_mode;
 
@@ -783,6 +787,11 @@ struct ieee80211_sub_if_data {
                struct dentry *default_mgmt_key;
        } debugfs;
 #endif
+
+#ifdef CONFIG_PM
+       struct ieee80211_bss_conf suspend_bss_conf;
+#endif
+
        /* must be last, dynamically sized area in this! */
        struct ieee80211_vif vif;
 };
@@ -1118,6 +1127,7 @@ struct ieee80211_local {
        struct timer_list dynamic_ps_timer;
        struct notifier_block network_latency_notifier;
        struct notifier_block ifa_notifier;
+       struct notifier_block ifa6_notifier;
 
        /*
         * The dynamic ps timeout configured from user space via WEXT -
@@ -1346,8 +1356,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
                          struct ieee80211_mgmt *mgmt,
                          size_t len,
                          struct ieee802_11_elems *elems,
-                         struct ieee80211_channel *channel,
-                         bool beacon);
+                         struct ieee80211_channel *channel);
 void ieee80211_rx_bss_put(struct ieee80211_local *local,
                          struct ieee80211_bss *bss);
 
@@ -1420,7 +1429,8 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                     u16 initiator, u16 reason, bool stop);
 void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                    u16 initiator, u16 reason, bool stop);
-void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx);
+void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
+                                        enum ieee80211_agg_stop_reason reason);
 void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
                             struct sta_info *sta,
                             struct ieee80211_mgmt *mgmt, size_t len);
@@ -1434,11 +1444,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
                                     size_t len);
 
 int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
-                                  enum ieee80211_back_parties initiator,
-                                  bool tx);
+                                  enum ieee80211_agg_stop_reason reason);
 int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
-                                   enum ieee80211_back_parties initiator,
-                                   bool tx);
+                                   enum ieee80211_agg_stop_reason reason);
 void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid);
 void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid);
 void ieee80211_ba_session_work(struct work_struct *work);
index 8be854e86cd987d61e01cc7eabc8f7ddb068eff3..0a36dc6346bb707d5fdf708e1ccc0ea4af9305f2 100644 (file)
@@ -747,7 +747,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        unsigned long flags;
        struct sk_buff *skb, *tmp;
        u32 hw_reconf_flags = 0;
-       int i;
+       int i, flushed;
 
        clear_bit(SDATA_STATE_RUNNING, &sdata->state);
 
@@ -772,11 +772,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
         * (because if we remove a STA after ops->remove_interface()
         * the driver will have removed the vif info already!)
         *
-        * This is relevant only in AP, WDS and mesh modes, since in
-        * all other modes we've already removed all stations when
-        * disconnecting etc.
+        * This is relevant only in WDS mode, in all other modes we've
+        * already removed all stations when disconnecting or similar,
+        * so warn otherwise.
+        *
+        * We call sta_info_flush_cleanup() later, to combine RCU waits.
         */
-       sta_info_flush(local, sdata);
+       flushed = sta_info_flush_defer(sdata);
+       WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) ||
+                    (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1));
 
        /*
         * Don't count this interface for promisc/allmulti while it
@@ -859,11 +863,17 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                cancel_work_sync(&sdata->work);
                /*
                 * When we get here, the interface is marked down.
-                * Call synchronize_rcu() to wait for the RX path
-                * should it be using the interface and enqueuing
-                * frames at this very time on another CPU.
+                *
+                * sta_info_flush_cleanup() requires rcu_barrier()
+                * first to wait for the station call_rcu() calls
+                * to complete, here we need at least sychronize_rcu()
+                * it to wait for the RX path in case it is using the
+                * interface and enqueuing frames at this very time on
+                * another CPU.
                 */
-               synchronize_rcu();
+               rcu_barrier();
+               sta_info_flush_cleanup(sdata);
+
                skb_queue_purge(&sdata->skb_queue);
 
                /*
@@ -961,7 +971,6 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
  */
 static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_local *local = sdata->local;
        int flushed;
        int i;
 
@@ -977,7 +986,7 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
        if (ieee80211_vif_is_mesh(&sdata->vif))
                mesh_rmc_free(sdata);
 
-       flushed = sta_info_flush(local, sdata);
+       flushed = sta_info_flush(sdata);
        WARN_ON(flushed);
 }
 
@@ -1218,6 +1227,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        case NL80211_IFTYPE_AP:
                skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
                INIT_LIST_HEAD(&sdata->u.ap.vlans);
+               sdata->vif.bss_conf.bssid = sdata->vif.addr;
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
                type = NL80211_IFTYPE_STATION;
@@ -1225,9 +1235,11 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
                sdata->vif.p2p = true;
                /* fall through */
        case NL80211_IFTYPE_STATION:
+               sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
                ieee80211_sta_setup_sdata(sdata);
                break;
        case NL80211_IFTYPE_ADHOC:
+               sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
                ieee80211_ibss_setup_sdata(sdata);
                break;
        case NL80211_IFTYPE_MESH_POINT:
@@ -1241,8 +1253,12 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
                                      MONITOR_FLAG_OTHER_BSS;
                break;
        case NL80211_IFTYPE_WDS:
+               sdata->vif.bss_conf.bssid = NULL;
+               break;
        case NL80211_IFTYPE_AP_VLAN:
+               break;
        case NL80211_IFTYPE_P2P_DEVICE:
+               sdata->vif.bss_conf.bssid = sdata->vif.addr;
                break;
        case NL80211_IFTYPE_UNSPECIFIED:
        case NUM_NL80211_IFTYPES:
@@ -1558,9 +1574,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
        /* initialise type-independent data */
        sdata->wdev.wiphy = local->hw.wiphy;
        sdata->local = local;
-#ifdef CONFIG_INET
-       sdata->arp_filter_state = true;
-#endif
 
        for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
                skb_queue_head_init(&sdata->fragments[i].skb_list);
index 619c5d69799980108c03fc3c0c7d6b2553aebb6e..ef252eb58c36b0740e9ecbb6f9b93aae66e6b054 100644 (file)
@@ -204,8 +204,11 @@ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
        if (idx >= 0 && idx < NUM_DEFAULT_KEYS)
                key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
 
-       if (uni)
+       if (uni) {
                rcu_assign_pointer(sdata->default_unicast_key, key);
+               drv_set_default_unicast_key(sdata->local, sdata, idx);
+       }
+
        if (multi)
                rcu_assign_pointer(sdata->default_multicast_key, key);
 
index 1b087fff93e70f278c638c017789f0ba7fb4f1b7..2bdd454e8bcf4871f3935657de65c54f6906417c 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/inetdevice.h>
 #include <net/net_namespace.h>
 #include <net/cfg80211.h>
+#include <net/addrconf.h>
 
 #include "ieee80211_i.h"
 #include "driver-ops.h"
@@ -207,76 +208,10 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
                                      u32 changed)
 {
        struct ieee80211_local *local = sdata->local;
-       static const u8 zero[ETH_ALEN] = { 0 };
 
        if (!changed)
                return;
 
-       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-               sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
-       } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
-               sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
-       else if (sdata->vif.type == NL80211_IFTYPE_AP)
-               sdata->vif.bss_conf.bssid = sdata->vif.addr;
-       else if (sdata->vif.type == NL80211_IFTYPE_WDS)
-               sdata->vif.bss_conf.bssid = NULL;
-       else if (ieee80211_vif_is_mesh(&sdata->vif)) {
-               sdata->vif.bss_conf.bssid = zero;
-       } else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) {
-               sdata->vif.bss_conf.bssid = sdata->vif.addr;
-               WARN_ONCE(changed & ~(BSS_CHANGED_IDLE),
-                         "P2P Device BSS changed %#x", changed);
-       } else {
-               WARN_ON(1);
-               return;
-       }
-
-       switch (sdata->vif.type) {
-       case NL80211_IFTYPE_AP:
-       case NL80211_IFTYPE_ADHOC:
-       case NL80211_IFTYPE_WDS:
-       case NL80211_IFTYPE_MESH_POINT:
-               break;
-       default:
-               /* do not warn to simplify caller in scan.c */
-               changed &= ~BSS_CHANGED_BEACON_ENABLED;
-               if (WARN_ON(changed & BSS_CHANGED_BEACON))
-                       return;
-               break;
-       }
-
-       if (changed & BSS_CHANGED_BEACON_ENABLED) {
-               if (local->quiescing || !ieee80211_sdata_running(sdata) ||
-                   test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) {
-                       sdata->vif.bss_conf.enable_beacon = false;
-               } else {
-                       /*
-                        * Beacon should be enabled, but AP mode must
-                        * check whether there is a beacon configured.
-                        */
-                       switch (sdata->vif.type) {
-                       case NL80211_IFTYPE_AP:
-                               sdata->vif.bss_conf.enable_beacon =
-                                       !!sdata->u.ap.beacon;
-                               break;
-                       case NL80211_IFTYPE_ADHOC:
-                               sdata->vif.bss_conf.enable_beacon =
-                                       !!sdata->u.ibss.presp;
-                               break;
-#ifdef CONFIG_MAC80211_MESH
-                       case NL80211_IFTYPE_MESH_POINT:
-                               sdata->vif.bss_conf.enable_beacon =
-                                       !!sdata->u.mesh.mesh_id_len;
-                               break;
-#endif
-                       default:
-                               /* not reached */
-                               WARN_ON(1);
-                               break;
-                       }
-               }
-       }
-
        drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed);
 }
 
@@ -415,27 +350,19 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
 
        /* Copy the addresses to the bss_conf list */
        ifa = idev->ifa_list;
-       while (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN && ifa) {
-               bss_conf->arp_addr_list[c] = ifa->ifa_address;
+       while (ifa) {
+               if (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN)
+                       bss_conf->arp_addr_list[c] = ifa->ifa_address;
                ifa = ifa->ifa_next;
                c++;
        }
 
-       /* If not all addresses fit the list, disable filtering */
-       if (ifa) {
-               sdata->arp_filter_state = false;
-               c = 0;
-       } else {
-               sdata->arp_filter_state = true;
-       }
        bss_conf->arp_addr_cnt = c;
 
        /* Configure driver only if associated (which also implies it is up) */
-       if (ifmgd->associated) {
-               bss_conf->arp_filter_enabled = sdata->arp_filter_state;
+       if (ifmgd->associated)
                ieee80211_bss_info_change_notify(sdata,
                                                 BSS_CHANGED_ARP_FILTER);
-       }
 
        mutex_unlock(&ifmgd->mtx);
 
@@ -443,6 +370,37 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
 }
 #endif
 
+#if IS_ENABLED(CONFIG_IPV6)
+static int ieee80211_ifa6_changed(struct notifier_block *nb,
+                                 unsigned long data, void *arg)
+{
+       struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)arg;
+       struct inet6_dev *idev = ifa->idev;
+       struct net_device *ndev = ifa->idev->dev;
+       struct ieee80211_local *local =
+               container_of(nb, struct ieee80211_local, ifa6_notifier);
+       struct wireless_dev *wdev = ndev->ieee80211_ptr;
+       struct ieee80211_sub_if_data *sdata;
+
+       /* Make sure it's our interface that got changed */
+       if (!wdev || wdev->wiphy != local->hw.wiphy)
+               return NOTIFY_DONE;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(ndev);
+
+       /*
+        * For now only support station mode. This is mostly because
+        * doing AP would have to handle AP_VLAN in some way ...
+        */
+       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+               return NOTIFY_DONE;
+
+       drv_ipv6_addr_change(local, sdata, idev);
+
+       return NOTIFY_DONE;
+}
+#endif
+
 static int ieee80211_napi_poll(struct napi_struct *napi, int budget)
 {
        struct ieee80211_local *local =
@@ -537,6 +495,7 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
 
        .cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
                                IEEE80211_HT_CAP_MAX_AMSDU |
+                               IEEE80211_HT_CAP_SGI_20 |
                                IEEE80211_HT_CAP_SGI_40),
        .mcs = {
                .rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -606,7 +565,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
        wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
                           NL80211_FEATURE_SAE |
                           NL80211_FEATURE_HT_IBSS |
-                          NL80211_FEATURE_VIF_TXPOWER;
+                          NL80211_FEATURE_VIF_TXPOWER |
+                          NL80211_FEATURE_FULL_AP_CLIENT_STATE;
 
        if (!ops->hw_scan)
                wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
@@ -1049,12 +1009,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                goto fail_ifa;
 #endif
 
+#if IS_ENABLED(CONFIG_IPV6)
+       local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed;
+       result = register_inet6addr_notifier(&local->ifa6_notifier);
+       if (result)
+               goto fail_ifa6;
+#endif
+
        netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll,
                        local->hw.napi_weight);
 
        return 0;
 
+#if IS_ENABLED(CONFIG_IPV6)
+ fail_ifa6:
 #ifdef CONFIG_INET
+       unregister_inetaddr_notifier(&local->ifa_notifier);
+#endif
+#endif
+#if defined(CONFIG_INET) || defined(CONFIG_IPV6)
  fail_ifa:
        pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
                               &local->network_latency_notifier);
@@ -1090,6 +1063,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 #ifdef CONFIG_INET
        unregister_inetaddr_notifier(&local->ifa_notifier);
 #endif
+#if IS_ENABLED(CONFIG_IPV6)
+       unregister_inet6addr_notifier(&local->ifa6_notifier);
+#endif
 
        rtnl_lock();
 
index 649ad513547f99728d43dbeeabcd2b58d7862c30..694e27376afa151941ef5628c3fa83ae32867b15 100644 (file)
 int mesh_allocated;
 static struct kmem_cache *rm_cache;
 
-#ifdef CONFIG_MAC80211_MESH
 bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt)
 {
        return (mgmt->u.action.u.mesh_action.action_code ==
                        WLAN_MESH_ACTION_HWMP_PATH_SELECTION);
 }
-#else
-bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt)
-{ return false; }
-#endif
 
 void ieee80211s_init(void)
 {
@@ -607,6 +602,12 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        struct ieee80211_local *local = sdata->local;
+       u32 changed = BSS_CHANGED_BEACON |
+                     BSS_CHANGED_BEACON_ENABLED |
+                     BSS_CHANGED_HT |
+                     BSS_CHANGED_BASIC_RATES |
+                     BSS_CHANGED_BEACON_INT;
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
 
        local->fif_other_bss++;
        /* mesh ifaces must set allmulti to forward mcast traffic */
@@ -624,15 +625,16 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
        ieee80211_queue_work(&local->hw, &sdata->work);
        sdata->vif.bss_conf.ht_operation_mode =
                                ifmsh->mshcfg.ht_opmode;
-       sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL;
+       sdata->vif.bss_conf.enable_beacon = true;
        sdata->vif.bss_conf.basic_rates =
-               ieee80211_mandatory_rates(sdata->local,
-                                         ieee80211_get_sdata_band(sdata));
-       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
-                                               BSS_CHANGED_BEACON_ENABLED |
-                                               BSS_CHANGED_HT |
-                                               BSS_CHANGED_BASIC_RATES |
-                                               BSS_CHANGED_BEACON_INT);
+               ieee80211_mandatory_rates(local, band);
+
+       if (band == IEEE80211_BAND_5GHZ) {
+               sdata->vif.bss_conf.use_short_slot = true;
+               changed |= BSS_CHANGED_ERP_SLOT;
+       }
+
+       ieee80211_bss_info_change_notify(sdata, changed);
 
        netif_carrier_on(sdata->dev);
 }
@@ -646,10 +648,12 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
 
        /* stop the beacon */
        ifmsh->mesh_id_len = 0;
+       sdata->vif.bss_conf.enable_beacon = false;
+       clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
 
        /* flush STAs and mpaths on this iface */
-       sta_info_flush(sdata->local, sdata);
+       sta_info_flush(sdata);
        mesh_path_flush_by_iface(sdata);
 
        del_timer_sync(&sdata->u.mesh.housekeeping_timer);
@@ -805,6 +809,7 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
 void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       static u8 zero_addr[ETH_ALEN] = {};
 
        setup_timer(&ifmsh->housekeeping_timer,
                    ieee80211_mesh_housekeeping_timer,
@@ -830,4 +835,6 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
        INIT_LIST_HEAD(&ifmsh->preq_queue.list);
        spin_lock_init(&ifmsh->mesh_preq_queue_lock);
        spin_lock_init(&ifmsh->sync_offset_lock);
+
+       sdata->vif.bss_conf.bssid = zero_addr;
 }
index 84c28c6101cdd2ef832920677d05b7f02b27ada2..aff301544c7fb30cc0fb3b8e4309a2cfead6909d 100644 (file)
@@ -191,8 +191,6 @@ struct mesh_rmc {
 #define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ)
 #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ)
 
-#define MESH_DEFAULT_BEACON_INTERVAL           1000    /* in 1024 us units */
-
 #define MESH_PATH_EXPIRE (600 * HZ)
 
 /* Default maximum number of plinks per interface */
@@ -307,6 +305,20 @@ extern int mesh_paths_generation;
 #ifdef CONFIG_MAC80211_MESH
 extern int mesh_allocated;
 
+static inline
+u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
+{
+       atomic_inc(&sdata->u.mesh.estab_plinks);
+       return mesh_accept_plinks_update(sdata);
+}
+
+static inline
+u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
+{
+       atomic_dec(&sdata->u.mesh.estab_plinks);
+       return mesh_accept_plinks_update(sdata);
+}
+
 static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata)
 {
        return sdata->u.mesh.mshcfg.dot11MeshMaxPeerLinks -
index 2659e428b80c86cf367a75b034c8d89a856f28a1..6b4603a9003189c6de53c37d46c8441f9803eaa9 100644 (file)
@@ -220,12 +220,14 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
 }
 
 /**
- * mesh_send_path error - Sends a PERR mesh management frame
+ * mesh_path_error_tx - Sends a PERR mesh management frame
  *
+ * @ttl: allowed remaining hops
  * @target: broken destination
  * @target_sn: SN of the broken destination
  * @target_rcode: reason code for this PERR
  * @ra: node this frame is addressed to
+ * @sdata: local mesh subif
  *
  * Note: This function may be called with driver locks taken that the driver
  * also acquires in the TX path.  To avoid a deadlock we don't transmit the
@@ -353,6 +355,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
  * @sdata: local mesh subif
  * @mgmt: mesh management frame
  * @hwmp_ie: hwmp information element (PREP or PREQ)
+ * @action: type of hwmp ie
  *
  * This function updates the path routing information to the originator and the
  * transmitter of a HWMP PREQ or PREP frame.
index 4b274e9c91a5f19c526c9c91d290c9fc574d4098..81e612682bc3320e05ffd0a1e18149c7fdb1012f 100644 (file)
@@ -41,20 +41,6 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
                enum ieee80211_self_protected_actioncode action,
                u8 *da, __le16 llid, __le16 plid, __le16 reason);
 
-static inline
-u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
-{
-       atomic_inc(&sdata->u.mesh.estab_plinks);
-       return mesh_accept_plinks_update(sdata);
-}
-
-static inline
-u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
-{
-       atomic_dec(&sdata->u.mesh.estab_plinks);
-       return mesh_accept_plinks_update(sdata);
-}
-
 /**
  * mesh_plink_fsm_restart - restart a mesh peer link finite state machine
  *
@@ -69,30 +55,6 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta)
        sta->plink_retries = 0;
 }
 
-/*
- * Allocate mesh sta entry and insert into station table
- */
-static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
-                                        u8 *hw_addr)
-{
-       struct sta_info *sta;
-
-       if (sdata->local->num_sta >= MESH_MAX_PLINKS)
-               return NULL;
-
-       sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
-       if (!sta)
-               return NULL;
-
-       sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
-       sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
-       sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
-
-       set_sta_flag(sta, WLAN_STA_WME);
-
-       return sta;
-}
-
 /**
  * mesh_set_ht_prot_mode - set correct HT protection mode
  *
@@ -323,53 +285,27 @@ free:
        return err;
 }
 
-/**
- * mesh_peer_init - initialize new mesh peer and return resulting sta_info
- *
- * @sdata: local meshif
- * @addr: peer's address
- * @elems: IEs from beacon or mesh peering frame
- *
- * call under RCU
- */
-static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
-                                      u8 *addr,
-                                      struct ieee802_11_elems *elems)
+static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
+                              struct sta_info *sta,
+                              struct ieee802_11_elems *elems, bool insert)
 {
        struct ieee80211_local *local = sdata->local;
        enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        struct ieee80211_supported_band *sband;
-       u32 rates, basic_rates = 0;
-       struct sta_info *sta;
-       bool insert = false;
+       u32 rates, basic_rates = 0, changed = 0;
 
        sband = local->hw.wiphy->bands[band];
        rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates);
 
-       sta = sta_info_get(sdata, addr);
-       if (!sta) {
-               /* Userspace handles peer allocation when security is enabled */
-               if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) {
-                       cfg80211_notify_new_peer_candidate(sdata->dev, addr,
-                                                          elems->ie_start,
-                                                          elems->total_len,
-                                                          GFP_ATOMIC);
-                       return NULL;
-               }
-
-               sta = mesh_plink_alloc(sdata, addr);
-               if (!sta)
-                       return NULL;
-               insert = true;
-       }
-
        spin_lock_bh(&sta->lock);
        sta->last_rx = jiffies;
-       if (sta->plink_state == NL80211_PLINK_ESTAB) {
-               spin_unlock_bh(&sta->lock);
-               return sta;
-       }
 
+       /* rates and capabilities don't change during peering */
+       if (sta->plink_state == NL80211_PLINK_ESTAB)
+               goto out;
+
+       if (sta->sta.supp_rates[band] != rates)
+               changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
        sta->sta.supp_rates[band] = rates;
        if (elems->ht_cap_elem &&
            sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
@@ -388,27 +324,115 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
                                            ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
                ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
                                             elems->ht_operation, &chandef);
+               if (sta->ch_width != chandef.width)
+                       changed |= IEEE80211_RC_BW_CHANGED;
                sta->ch_width = chandef.width;
        }
 
        if (insert)
                rate_control_rate_init(sta);
+       else
+               rate_control_rate_update(local, sband, sta, changed);
+out:
        spin_unlock_bh(&sta->lock);
+}
 
-       if (insert && sta_info_insert(sta))
+static struct sta_info *
+__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
+{
+       struct sta_info *sta;
+
+       if (sdata->local->num_sta >= MESH_MAX_PLINKS)
                return NULL;
 
+       sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
+       if (!sta)
+               return NULL;
+
+       sta->plink_state = NL80211_PLINK_LISTEN;
+       init_timer(&sta->plink_timer);
+
+       sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+       sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+       sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
+
+       set_sta_flag(sta, WLAN_STA_WME);
+
+       return sta;
+}
+
+static struct sta_info *
+mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
+                   struct ieee802_11_elems *elems)
+{
+       struct sta_info *sta = NULL;
+
+       /* Userspace handles peer allocation when security is enabled */
+       if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
+               cfg80211_notify_new_peer_candidate(sdata->dev, addr,
+                                                  elems->ie_start,
+                                                  elems->total_len,
+                                                  GFP_KERNEL);
+       else
+               sta = __mesh_sta_info_alloc(sdata, addr);
+
        return sta;
 }
 
+/*
+ * mesh_sta_info_get - return mesh sta info entry for @addr.
+ *
+ * @sdata: local meshif
+ * @addr: peer's address
+ * @elems: IEs from beacon or mesh peering frame.
+ *
+ * Return existing or newly allocated sta_info under RCU read lock.
+ * (re)initialize with given IEs.
+ */
+static struct sta_info *
+mesh_sta_info_get(struct ieee80211_sub_if_data *sdata,
+                 u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU)
+{
+       struct sta_info *sta = NULL;
+
+       rcu_read_lock();
+       sta = sta_info_get(sdata, addr);
+       if (sta) {
+               mesh_sta_info_init(sdata, sta, elems, false);
+       } else {
+               rcu_read_unlock();
+               /* can't run atomic */
+               sta = mesh_sta_info_alloc(sdata, addr, elems);
+               if (!sta) {
+                       rcu_read_lock();
+                       return NULL;
+               }
+
+               mesh_sta_info_init(sdata, sta, elems, true);
+
+               if (sta_info_insert_rcu(sta))
+                       return NULL;
+       }
+
+       return sta;
+}
+
+/*
+ * mesh_neighbour_update - update or initialize new mesh neighbor.
+ *
+ * @sdata: local meshif
+ * @addr: peer's address
+ * @elems: IEs from beacon or mesh peering frame
+ *
+ * Initiates peering if appropriate.
+ */
 void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
                           u8 *hw_addr,
                           struct ieee802_11_elems *elems)
 {
        struct sta_info *sta;
 
-       rcu_read_lock();
-       sta = mesh_peer_init(sdata, hw_addr, elems);
+       sta = mesh_sta_info_get(sdata, hw_addr, elems);
        if (!sta)
                goto out;
 
@@ -646,6 +670,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
            (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8))
                memcpy(&llid, PLINK_GET_PLID(elems.peering), 2);
 
+       /* WARNING: Only for sta pointer, is dropped & re-acquired */
        rcu_read_lock();
 
        sta = sta_info_get(sdata, mgmt->sa);
@@ -749,8 +774,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
        }
 
        if (event == OPN_ACPT) {
+               rcu_read_unlock();
                /* allocate sta entry if necessary and update info */
-               sta = mesh_peer_init(sdata, mgmt->sa, &elems);
+               sta = mesh_sta_info_get(sdata, mgmt->sa, &elems);
                if (!sta) {
                        mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
                        rcu_read_unlock();
index 9979bf8d162fba28d807fa265e76ff91d9f0cc7f..27d1ec1917e655901f9c5fcb589fd28bb8bdea8b 100644 (file)
@@ -199,11 +199,11 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
        case NL80211_CHAN_WIDTH_40:
                if (sdata->vif.bss_conf.chandef.chan->center_freq >
                                sdata->vif.bss_conf.chandef.center_freq1 &&
-                   chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
+                   chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
                        disable_40 = true;
                if (sdata->vif.bss_conf.chandef.chan->center_freq <
                                sdata->vif.bss_conf.chandef.center_freq1 &&
-                   chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
+                   chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
                        disable_40 = true;
                break;
        default:
@@ -341,11 +341,13 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
 
 static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
                                 struct sk_buff *skb,
-                                struct ieee80211_supported_band *sband)
+                                struct ieee80211_supported_band *sband,
+                                struct ieee80211_vht_cap *ap_vht_cap)
 {
        u8 *pos;
        u32 cap;
        struct ieee80211_sta_vht_cap vht_cap;
+       int i;
 
        BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
 
@@ -364,6 +366,42 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
                cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
        }
 
+       /*
+        * Some APs apparently get confused if our capabilities are better
+        * than theirs, so restrict what we advertise in the assoc request.
+        */
+       if (!(ap_vht_cap->vht_cap_info &
+                       cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
+               cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+
+       if (!(ap_vht_cap->vht_cap_info &
+                       cpu_to_le32(IEEE80211_VHT_CAP_TXSTBC)))
+               cap &= ~(IEEE80211_VHT_CAP_RXSTBC_1 |
+                        IEEE80211_VHT_CAP_RXSTBC_3 |
+                        IEEE80211_VHT_CAP_RXSTBC_4);
+
+       for (i = 0; i < 8; i++) {
+               int shift = i * 2;
+               u16 mask = IEEE80211_VHT_MCS_NOT_SUPPORTED << shift;
+               u16 ap_mcs, our_mcs;
+
+               ap_mcs = (le16_to_cpu(ap_vht_cap->supp_mcs.tx_mcs_map) &
+                                                               mask) >> shift;
+               our_mcs = (le16_to_cpu(vht_cap.vht_mcs.rx_mcs_map) &
+                                                               mask) >> shift;
+
+               switch (ap_mcs) {
+               default:
+                       if (our_mcs <= ap_mcs)
+                               break;
+                       /* fall through */
+               case IEEE80211_VHT_MCS_NOT_SUPPORTED:
+                       vht_cap.vht_mcs.rx_mcs_map &= cpu_to_le16(~mask);
+                       vht_cap.vht_mcs.rx_mcs_map |=
+                               cpu_to_le16(ap_mcs << shift);
+               }
+       }
+
        /* reserve and fill IE */
        pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
        ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
@@ -562,7 +600,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                                    sband, chan, sdata->smps_mode);
 
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
-               ieee80211_add_vht_ie(sdata, skb, sband);
+               ieee80211_add_vht_ie(sdata, skb, sband,
+                                    &assoc_data->ap_vht_cap);
 
        /* if present, add any custom non-vendor IEs that go after HT */
        if (assoc_data->ie_len && assoc_data->ie) {
@@ -1426,10 +1465,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
                bss_info_changed |= BSS_CHANGED_CQM;
 
        /* Enable ARP filtering */
-       if (bss_conf->arp_filter_enabled != sdata->arp_filter_state) {
-               bss_conf->arp_filter_enabled = sdata->arp_filter_state;
+       if (bss_conf->arp_addr_cnt)
                bss_info_changed |= BSS_CHANGED_ARP_FILTER;
-       }
 
        ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
@@ -1450,7 +1487,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
-       struct sta_info *sta;
        u32 changed = 0;
 
        ASSERT_MGD_MTX(ifmgd);
@@ -1482,14 +1518,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        netif_tx_stop_all_queues(sdata->dev);
        netif_carrier_off(sdata->dev);
 
-       mutex_lock(&local->sta_mtx);
-       sta = sta_info_get(sdata, ifmgd->bssid);
-       if (sta) {
-               set_sta_flag(sta, WLAN_STA_BLOCK_BA);
-               ieee80211_sta_tear_down_BA_sessions(sta, false);
-       }
-       mutex_unlock(&local->sta_mtx);
-
        /*
         * if we want to get out of ps before disassoc (why?) we have
         * to do it before sending disassoc, as otherwise the null-packet
@@ -1521,7 +1549,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        memset(ifmgd->bssid, 0, ETH_ALEN);
 
        /* remove AP and TDLS peers */
-       sta_info_flush(local, sdata);
+       sta_info_flush_defer(sdata);
 
        /* finally reset all BSS / config parameters */
        changed |= ieee80211_reset_erp_info(sdata);
@@ -1543,10 +1571,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        cancel_work_sync(&local->dynamic_ps_enable_work);
 
        /* Disable ARP filtering */
-       if (sdata->vif.bss_conf.arp_filter_enabled) {
-               sdata->vif.bss_conf.arp_filter_enabled = false;
+       if (sdata->vif.bss_conf.arp_addr_cnt)
                changed |= BSS_CHANGED_ARP_FILTER;
-       }
 
        sdata->vif.bss_conf.qos = false;
        changed |= BSS_CHANGED_QOS;
@@ -2369,8 +2395,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
 static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                                  struct ieee80211_mgmt *mgmt, size_t len,
                                  struct ieee80211_rx_status *rx_status,
-                                 struct ieee802_11_elems *elems,
-                                 bool beacon)
+                                 struct ieee802_11_elems *elems)
 {
        struct ieee80211_local *local = sdata->local;
        int freq;
@@ -2404,7 +2429,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                return;
 
        bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
-                                       channel, beacon);
+                                       channel);
        if (bss)
                ieee80211_rx_bss_put(local, bss);
 
@@ -2447,7 +2472,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
        ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
                                &elems);
 
-       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
+       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
 
        if (ifmgd->associated &&
            ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
@@ -2528,8 +2553,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                ieee802_11_parse_elems(mgmt->u.beacon.variable,
                                       len - baselen, &elems);
 
-               ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
-                                     false);
+               ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
                ifmgd->assoc_data->have_beacon = true;
                ifmgd->assoc_data->sent_assoc = false;
                /* continue assoc process */
@@ -2571,12 +2595,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                if (sig > ifmgd->rssi_max_thold &&
                    (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
                        ifmgd->last_ave_beacon_signal = sig;
-                       drv_rssi_callback(local, RSSI_EVENT_HIGH);
+                       drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH);
                } else if (sig < ifmgd->rssi_min_thold &&
                           (last_sig >= ifmgd->rssi_max_thold ||
                           last_sig == 0)) {
                        ifmgd->last_ave_beacon_signal = sig;
-                       drv_rssi_callback(local, RSSI_EVENT_LOW);
+                       drv_rssi_callback(local, sdata, RSSI_EVENT_LOW);
                }
        }
 
@@ -2682,8 +2706,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        ifmgd->beacon_crc = ncrc;
        ifmgd->beacon_crc_valid = true;
 
-       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
-                             true);
+       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
 
        if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
                                     elems.wmm_param_len))
@@ -3133,23 +3156,22 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-       if (!ifmgd->associated)
+       mutex_lock(&ifmgd->mtx);
+       if (!ifmgd->associated) {
+               mutex_unlock(&ifmgd->mtx);
                return;
+       }
 
        if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
                sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
-               mutex_lock(&ifmgd->mtx);
-               if (ifmgd->associated) {
-                       mlme_dbg(sdata,
-                                "driver requested disconnect after resume\n");
-                       ieee80211_sta_connection_lost(sdata,
-                               ifmgd->associated->bssid,
-                               WLAN_REASON_UNSPECIFIED);
-                       mutex_unlock(&ifmgd->mtx);
-                       return;
-               }
+               mlme_dbg(sdata, "driver requested disconnect after resume\n");
+               ieee80211_sta_connection_lost(sdata,
+                                             ifmgd->associated->bssid,
+                                             WLAN_REASON_UNSPECIFIED);
                mutex_unlock(&ifmgd->mtx);
+               return;
        }
+       mutex_unlock(&ifmgd->mtx);
 
        if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
                add_timer(&ifmgd->timer);
@@ -3759,7 +3781,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_bss *bss = (void *)req->bss->priv;
        struct ieee80211_mgd_assoc_data *assoc_data;
        struct ieee80211_supported_band *sband;
-       const u8 *ssidie, *ht_ie;
+       const u8 *ssidie, *ht_ie, *vht_ie;
        int i, err;
 
        assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL);
@@ -3878,6 +3900,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                        ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
        else
                ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
+       vht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_VHT_CAPABILITY);
+       if (vht_ie && vht_ie[1] >= sizeof(struct ieee80211_vht_cap))
+               memcpy(&assoc_data->ap_vht_cap, vht_ie + 2,
+                      sizeof(struct ieee80211_vht_cap));
+       else
+               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
        rcu_read_unlock();
 
        if (bss->wmm_used && bss->uapsd_supported &&
index a3ad4c3c80a34229801bdbdd1124201485a2f671..82baf5b6ecf4cbac5accf2e38d33187df1545514 100644 (file)
@@ -125,11 +125,13 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
                        set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
 
                /* Check to see if we should disable beaconing. */
-               if (sdata->vif.type == NL80211_IFTYPE_AP ||
-                   sdata->vif.type == NL80211_IFTYPE_ADHOC ||
-                   sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+               if (sdata->vif.bss_conf.enable_beacon) {
+                       set_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED,
+                               &sdata->state);
+                       sdata->vif.bss_conf.enable_beacon = false;
                        ieee80211_bss_info_change_notify(
                                sdata, BSS_CHANGED_BEACON_ENABLED);
+               }
 
                if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
                        netif_tx_stop_all_queues(sdata->dev);
@@ -178,11 +180,12 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
                        netif_tx_wake_all_queues(sdata->dev);
                }
 
-               if (sdata->vif.type == NL80211_IFTYPE_AP ||
-                   sdata->vif.type == NL80211_IFTYPE_ADHOC ||
-                   sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+               if (test_and_clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED,
+                                      &sdata->state)) {
+                       sdata->vif.bss_conf.enable_beacon = true;
                        ieee80211_bss_info_change_notify(
                                sdata, BSS_CHANGED_BEACON_ENABLED);
+               }
        }
        mutex_unlock(&local->iflist_mtx);
 }
index 79a48f37d4092b27a88b8fa136f15a51812365f2..e45b83610e850fdf8df47f9f2774686665a13f8d 100644 (file)
@@ -7,25 +7,23 @@
 #include "led.h"
 
 /* return value indicates whether the driver should be further notified */
-static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_quiesce(struct ieee80211_sub_if_data *sdata)
 {
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_STATION:
                ieee80211_sta_quiesce(sdata);
-               return true;
+               break;
        case NL80211_IFTYPE_ADHOC:
                ieee80211_ibss_quiesce(sdata);
-               return true;
+               break;
        case NL80211_IFTYPE_MESH_POINT:
                ieee80211_mesh_quiesce(sdata);
-               return true;
-       case NL80211_IFTYPE_AP_VLAN:
-       case NL80211_IFTYPE_MONITOR:
-               /* don't tell driver about this */
-               return false;
+               break;
        default:
-               return true;
+               break;
        }
+
+       cancel_work_sync(&sdata->work);
 }
 
 int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
@@ -44,7 +42,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                mutex_lock(&local->sta_mtx);
                list_for_each_entry(sta, &local->sta_list, list) {
                        set_sta_flag(sta, WLAN_STA_BLOCK_BA);
-                       ieee80211_sta_tear_down_BA_sessions(sta, true);
+                       ieee80211_sta_tear_down_BA_sessions(
+                                       sta, AGG_STOP_LOCAL_REQUEST);
                }
                mutex_unlock(&local->sta_mtx);
        }
@@ -94,10 +93,9 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                        WARN_ON(err != 1);
                        local->wowlan = false;
                } else {
-                       list_for_each_entry(sdata, &local->interfaces, list) {
-                               cancel_work_sync(&sdata->work);
-                               ieee80211_quiesce(sdata);
-                       }
+                       list_for_each_entry(sdata, &local->interfaces, list)
+                               if (ieee80211_sdata_running(sdata))
+                                       ieee80211_quiesce(sdata);
                        goto suspend;
                }
        }
@@ -124,17 +122,43 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 
        /* remove all interfaces */
        list_for_each_entry(sdata, &local->interfaces, list) {
-               cancel_work_sync(&sdata->work);
+               static u8 zero_addr[ETH_ALEN] = {};
+               u32 changed = 0;
 
-               if (!ieee80211_quiesce(sdata))
+               if (!ieee80211_sdata_running(sdata))
                        continue;
 
-               if (!ieee80211_sdata_running(sdata))
+               switch (sdata->vif.type) {
+               case NL80211_IFTYPE_AP_VLAN:
+               case NL80211_IFTYPE_MONITOR:
+                       /* skip these */
                        continue;
+               case NL80211_IFTYPE_STATION:
+                       if (sdata->vif.bss_conf.assoc)
+                               changed = BSS_CHANGED_ASSOC |
+                                         BSS_CHANGED_BSSID |
+                                         BSS_CHANGED_IDLE;
+                       break;
+               case NL80211_IFTYPE_AP:
+               case NL80211_IFTYPE_ADHOC:
+               case NL80211_IFTYPE_MESH_POINT:
+                       if (sdata->vif.bss_conf.enable_beacon)
+                               changed = BSS_CHANGED_BEACON_ENABLED;
+                       break;
+               default:
+                       break;
+               }
+
+               ieee80211_quiesce(sdata);
+
+               sdata->suspend_bss_conf = sdata->vif.bss_conf;
+               memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf));
+               sdata->vif.bss_conf.idle = true;
+               if (sdata->suspend_bss_conf.bssid)
+                       sdata->vif.bss_conf.bssid = zero_addr;
 
-               /* disable beaconing */
-               ieee80211_bss_info_change_notify(sdata,
-                       BSS_CHANGED_BEACON_ENABLED);
+               /* disable beaconing or remove association */
+               ieee80211_bss_info_change_notify(sdata, changed);
 
                if (sdata->vif.type == NL80211_IFTYPE_AP &&
                    rcu_access_pointer(sdata->u.ap.beacon))
index 580704eba8b8495e0bdbf2268dfdc197b9983797..a19089565c4b84ed35a7b2c685d16c487a3ef356 100644 (file)
@@ -2353,7 +2353,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                    sdata->vif.type != NL80211_IFTYPE_ADHOC)
                        break;
 
-               /* verify action & smps_control are present */
+               /* verify action & smps_control/chanwidth are present */
                if (len < IEEE80211_MIN_ACTION_SIZE + 2)
                        goto invalid;
 
@@ -2392,6 +2392,35 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                                                 IEEE80211_RC_SMPS_CHANGED);
                        goto handled;
                }
+               case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: {
+                       struct ieee80211_supported_band *sband;
+                       u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth;
+                       bool old_40mhz, new_40mhz;
+
+                       /* If it doesn't support 40 MHz it can't change ... */
+                       if (!rx->sta->supports_40mhz)
+                               goto handled;
+
+                       old_40mhz = rx->sta->sta.ht_cap.cap &
+                                       IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+                       new_40mhz = chanwidth == IEEE80211_HT_CHANWIDTH_ANY;
+
+                       if (old_40mhz == new_40mhz)
+                               goto handled;
+
+                       if (new_40mhz)
+                               rx->sta->sta.ht_cap.cap |=
+                                       IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+                       else
+                               rx->sta->sta.ht_cap.cap &=
+                                       ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+                       sband = rx->local->hw.wiphy->bands[status->band];
+
+                       rate_control_rate_update(local, sband, rx->sta,
+                                                IEEE80211_RC_BW_CHANGED);
+                       goto handled;
+               }
                default:
                        goto invalid;
                }
index bf82e69d0601bf9210e817837b2af7725a9f7d87..607684c47d558a4a3d67d50cd72c617d8033396a 100644 (file)
@@ -65,12 +65,11 @@ static bool is_uapsd_supported(struct ieee802_11_elems *elems)
 struct ieee80211_bss *
 ieee80211_bss_info_update(struct ieee80211_local *local,
                          struct ieee80211_rx_status *rx_status,
-                         struct ieee80211_mgmt *mgmt,
-                         size_t len,
+                         struct ieee80211_mgmt *mgmt, size_t len,
                          struct ieee802_11_elems *elems,
-                         struct ieee80211_channel *channel,
-                         bool beacon)
+                         struct ieee80211_channel *channel)
 {
+       bool beacon = ieee80211_is_beacon(mgmt->frame_control);
        struct cfg80211_bss *cbss;
        struct ieee80211_bss *bss;
        int clen, srlen;
@@ -203,7 +202,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
 
        bss = ieee80211_bss_info_update(local, rx_status,
                                        mgmt, skb->len, &elems,
-                                       channel, beacon);
+                                       channel);
        if (bss)
                ieee80211_rx_bss_put(local, bss);
 }
index ca9fde1981889a559efa6b49385f071f1475a06f..227233c3ff7ffa71efd52f468cdc8f400eb9fc62 100644 (file)
@@ -104,6 +104,16 @@ static void cleanup_single_sta(struct sta_info *sta)
         * neither mac80211 nor the driver can reference this
         * sta struct any more except by still existing timers
         * associated with this station that we clean up below.
+        *
+        * Note though that this still uses the sdata and even
+        * calls the driver in AP and mesh mode, so interfaces
+        * of those types mush use call sta_info_flush_cleanup()
+        * (typically via sta_info_flush()) before deconfiguring
+        * the driver.
+        *
+        * In station mode, nothing happens here so it doesn't
+        * have to (and doesn't) do that, this is intentional to
+        * speed up roaming.
         */
 
        if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
@@ -370,11 +380,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
 
-#ifdef CONFIG_MAC80211_MESH
-       sta->plink_state = NL80211_PLINK_LISTEN;
-       init_timer(&sta->plink_timer);
-#endif
-
        return sta;
 }
 
@@ -774,7 +779,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
         * will be sufficient.
         */
        set_sta_flag(sta, WLAN_STA_BLOCK_BA);
-       ieee80211_sta_tear_down_BA_sessions(sta, false);
+       ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);
 
        ret = sta_info_hash_del(local, sta);
        if (ret)
@@ -885,20 +890,12 @@ void sta_info_init(struct ieee80211_local *local)
 void sta_info_stop(struct ieee80211_local *local)
 {
        del_timer_sync(&local->sta_cleanup);
-       sta_info_flush(local, NULL);
 }
 
-/**
- * sta_info_flush - flush matching STA entries from the STA table
- *
- * Returns the number of removed STA entries.
- *
- * @local: local interface data
- * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs
- */
-int sta_info_flush(struct ieee80211_local *local,
-                  struct ieee80211_sub_if_data *sdata)
+
+int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata)
 {
+       struct ieee80211_local *local = sdata->local;
        struct sta_info *sta, *tmp;
        int ret = 0;
 
@@ -906,30 +903,22 @@ int sta_info_flush(struct ieee80211_local *local,
 
        mutex_lock(&local->sta_mtx);
        list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
-               if (!sdata || sdata == sta->sdata) {
+               if (sdata == sta->sdata) {
                        WARN_ON(__sta_info_destroy(sta));
                        ret++;
                }
        }
        mutex_unlock(&local->sta_mtx);
 
-       rcu_barrier();
-
-       if (sdata) {
-               ieee80211_cleanup_sdata_stas(sdata);
-               cancel_work_sync(&sdata->cleanup_stations_wk);
-       } else {
-               mutex_lock(&local->iflist_mtx);
-               list_for_each_entry(sdata, &local->interfaces, list) {
-                       ieee80211_cleanup_sdata_stas(sdata);
-                       cancel_work_sync(&sdata->cleanup_stations_wk);
-               }
-               mutex_unlock(&local->iflist_mtx);
-       }
-
        return ret;
 }
 
+void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata)
+{
+       ieee80211_cleanup_sdata_stas(sdata);
+       cancel_work_sync(&sdata->cleanup_stations_wk);
+}
+
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                          unsigned long exp_time)
 {
index 37c1889afd3ae02f9d9d5f2573738a28d26c266e..af7d78aa55230e49e07252d9e87ef7c2f4fb2b31 100644 (file)
@@ -92,6 +92,13 @@ enum ieee80211_sta_info_flags {
 #define HT_AGG_STATE_WANT_START                4
 #define HT_AGG_STATE_WANT_STOP         5
 
+enum ieee80211_agg_stop_reason {
+       AGG_STOP_DECLINED,
+       AGG_STOP_LOCAL_REQUEST,
+       AGG_STOP_PEER_REQUEST,
+       AGG_STOP_DESTROY_STA,
+};
+
 /**
  * struct tid_ampdu_tx - TID aggregation information (Tx).
  *
@@ -548,8 +555,39 @@ void sta_info_recalc_tim(struct sta_info *sta);
 
 void sta_info_init(struct ieee80211_local *local);
 void sta_info_stop(struct ieee80211_local *local);
-int sta_info_flush(struct ieee80211_local *local,
-                  struct ieee80211_sub_if_data *sdata);
+int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata);
+
+/**
+ * sta_info_flush_cleanup - flush the sta_info cleanup queue
+ * @sdata: the interface
+ *
+ * Flushes the sta_info cleanup queue for a given interface;
+ * this is necessary before the interface is removed or, for
+ * AP/mesh interfaces, before it is deconfigured.
+ *
+ * Note an rcu_barrier() must precede the function, after all
+ * stations have been flushed/removed to ensure the call_rcu()
+ * calls that add stations to the cleanup queue have completed.
+ */
+void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata);
+
+/**
+ * sta_info_flush - flush matching STA entries from the STA table
+ *
+ * Returns the number of removed STA entries.
+ *
+ * @sdata: sdata to remove all stations from
+ */
+static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata)
+{
+       int ret = sta_info_flush_defer(sdata);
+
+       rcu_barrier();
+       sta_info_flush_cleanup(sdata);
+
+       return ret;
+}
+
 void sta_set_rate_info_tx(struct sta_info *sta,
                          const struct ieee80211_tx_rate *rate,
                          struct rate_info *rinfo);
index a8270b441a6f93249e095c74be849bdf9ce39f9b..6ca53d64cb28ad67ee9f47a3341f4d7c362720c0 100644 (file)
 #define VIF_PR_FMT     " vif:%s(%d%s)"
 #define VIF_PR_ARG     __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : ""
 
-#define CHANCTX_ENTRY  __field(u32, control_freq)                              \
+#define CHANDEF_ENTRY  __field(u32, control_freq)                              \
                        __field(u32, chan_width)                                \
                        __field(u32, center_freq1)                              \
-                       __field(u32, center_freq2)                              \
+                       __field(u32, center_freq2)
+#define CHANDEF_ASSIGN(c)                                                      \
+                       __entry->control_freq = (c)->chan->center_freq;         \
+                       __entry->chan_width = (c)->width;                       \
+                       __entry->center_freq1 = (c)->center_freq1;              \
+                       __entry->center_freq1 = (c)->center_freq2;
+#define CHANDEF_PR_FMT " control:%d MHz width:%d center: %d/%d MHz"
+#define CHANDEF_PR_ARG __entry->control_freq, __entry->chan_width,             \
+                       __entry->center_freq1, __entry->center_freq2
+
+#define CHANCTX_ENTRY  CHANDEF_ENTRY                                           \
                        __field(u8, rx_chains_static)                           \
                        __field(u8, rx_chains_dynamic)
-#define CHANCTX_ASSIGN __entry->control_freq = ctx->conf.def.chan->center_freq;\
-                       __entry->chan_width = ctx->conf.def.width;              \
-                       __entry->center_freq1 = ctx->conf.def.center_freq1;     \
-                       __entry->center_freq2 = ctx->conf.def.center_freq2;     \
+#define CHANCTX_ASSIGN CHANDEF_ASSIGN(&ctx->conf.def)                          \
                        __entry->rx_chains_static = ctx->conf.rx_chains_static; \
                        __entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic
-#define CHANCTX_PR_FMT " control:%d MHz width:%d center: %d/%d MHz chains:%d/%d"
-#define CHANCTX_PR_ARG __entry->control_freq, __entry->chan_width,             \
-                       __entry->center_freq1, __entry->center_freq2,           \
+#define CHANCTX_PR_FMT CHANDEF_PR_FMT " chains:%d/%d"
+#define CHANCTX_PR_ARG CHANDEF_PR_ARG,                                         \
                        __entry->rx_chains_static, __entry->rx_chains_dynamic
 
 
@@ -341,8 +347,11 @@ TRACE_EVENT(drv_bss_info_changed,
                __field(s32, cqm_rssi_hyst);
                __field(u32, channel_width);
                __field(u32, channel_cfreq1);
-               __dynamic_array(u32, arp_addr_list, info->arp_addr_cnt);
-               __field(bool, arp_filter_enabled);
+               __dynamic_array(u32, arp_addr_list,
+                               info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ?
+                                       IEEE80211_BSS_ARP_ADDR_LIST_LEN :
+                                       info->arp_addr_cnt);
+               __field(int, arp_addr_cnt);
                __field(bool, qos);
                __field(bool, idle);
                __field(bool, ps);
@@ -378,9 +387,11 @@ TRACE_EVENT(drv_bss_info_changed,
                __entry->cqm_rssi_hyst = info->cqm_rssi_hyst;
                __entry->channel_width = info->chandef.width;
                __entry->channel_cfreq1 = info->chandef.center_freq1;
+               __entry->arp_addr_cnt = info->arp_addr_cnt;
                memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list,
-                      sizeof(u32) * info->arp_addr_cnt);
-               __entry->arp_filter_enabled = info->arp_filter_enabled;
+                      sizeof(u32) * (info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ?
+                                       IEEE80211_BSS_ARP_ADDR_LIST_LEN :
+                                       info->arp_addr_cnt));
                __entry->qos = info->qos;
                __entry->idle = info->idle;
                __entry->ps = info->ps;
@@ -1178,23 +1189,26 @@ TRACE_EVENT(drv_set_rekey_data,
 
 TRACE_EVENT(drv_rssi_callback,
        TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
                 enum ieee80211_rssi_event rssi_event),
 
-       TP_ARGS(local, rssi_event),
+       TP_ARGS(local, sdata, rssi_event),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
+               VIF_ENTRY
                __field(u32, rssi_event)
        ),
 
        TP_fast_assign(
                LOCAL_ASSIGN;
+               VIF_ASSIGN;
                __entry->rssi_event = rssi_event;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT " rssi_event:%d",
-               LOCAL_PR_ARG, __entry->rssi_event
+               LOCAL_PR_FMT VIF_PR_FMT " rssi_event:%d",
+               LOCAL_PR_ARG, VIF_PR_ARG, __entry->rssi_event
        )
 );
 
@@ -1426,6 +1440,14 @@ DEFINE_EVENT(local_only_evt, drv_restart_complete,
        TP_ARGS(local)
 );
 
+#if IS_ENABLED(CONFIG_IPV6)
+DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata),
+       TP_ARGS(local, sdata)
+);
+#endif
+
 /*
  * Tracing for API calls that drivers call.
  */
@@ -1815,6 +1837,29 @@ TRACE_EVENT(stop_queue,
        )
 );
 
+TRACE_EVENT(drv_set_default_unicast_key,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                int key_idx),
+
+       TP_ARGS(local, sdata, key_idx),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               __field(int, key_idx)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               __entry->key_idx = key_idx;
+       ),
+
+       TP_printk(LOCAL_PR_FMT VIF_PR_FMT " key_idx:%d",
+                 LOCAL_PR_ARG, VIF_PR_ARG, __entry->key_idx)
+);
+
 #ifdef CONFIG_MAC80211_MESSAGE_TRACING
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM mac80211_msg
index 467c1d1b66f2f5615182abd689349027b88abe64..a2cb6a302cc74216d0ac929a2a193b1c0c5c0a25 100644 (file)
@@ -1787,16 +1787,16 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                        break;
                /* fall through */
        case NL80211_IFTYPE_AP:
+               if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+               if (!chanctx_conf)
+                       goto fail_rcu;
                fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
                /* DA BSSID SA */
                memcpy(hdr.addr1, skb->data, ETH_ALEN);
                memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
                memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
                hdrlen = 24;
-               if (sdata->vif.type == NL80211_IFTYPE_AP)
-                       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-               if (!chanctx_conf)
-                       goto fail_rcu;
                band = chanctx_conf->def.chan->band;
                break;
        case NL80211_IFTYPE_WDS:
@@ -2264,9 +2264,8 @@ void ieee80211_tx_pending(unsigned long data)
 
 /* functions for drivers to get certain frames */
 
-static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
-                                    struct ps_data *ps,
-                                    struct sk_buff *skb)
+static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
+                                      struct ps_data *ps, struct sk_buff *skb)
 {
        u8 *pos, *tim;
        int aid0 = 0;
@@ -2328,6 +2327,31 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
        }
 }
 
+static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
+                                   struct ps_data *ps, struct sk_buff *skb)
+{
+       struct ieee80211_local *local = sdata->local;
+
+       /*
+        * Not very nice, but we want to allow the driver to call
+        * ieee80211_beacon_get() as a response to the set_tim()
+        * callback. That, however, is already invoked under the
+        * sta_lock to guarantee consistent and race-free update
+        * of the tim bitmap in mac80211 and the driver.
+        */
+       if (local->tim_in_locked_section) {
+               __ieee80211_beacon_add_tim(sdata, ps, skb);
+       } else {
+               unsigned long flags;
+
+               spin_lock_irqsave(&local->tim_lock, flags);
+               __ieee80211_beacon_add_tim(sdata, ps, skb);
+               spin_unlock_irqrestore(&local->tim_lock, flags);
+       }
+
+       return 0;
+}
+
 struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                                         struct ieee80211_vif *vif,
                                         u16 *tim_offset, u16 *tim_length)
@@ -2372,22 +2396,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                        memcpy(skb_put(skb, beacon->head_len), beacon->head,
                               beacon->head_len);
 
-                       /*
-                        * Not very nice, but we want to allow the driver to call
-                        * ieee80211_beacon_get() as a response to the set_tim()
-                        * callback. That, however, is already invoked under the
-                        * sta_lock to guarantee consistent and race-free update
-                        * of the tim bitmap in mac80211 and the driver.
-                        */
-                       if (local->tim_in_locked_section) {
-                               ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
-                       } else {
-                               unsigned long flags;
-
-                               spin_lock_irqsave(&local->tim_lock, flags);
-                               ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
-                               spin_unlock_irqrestore(&local->tim_lock, flags);
-                       }
+                       ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
 
                        if (tim_offset)
                                *tim_offset = beacon->head_len;
index f11e8c540db41b71a310c1717c54a4e2278d9c4b..7519018ff71af2844241f43697a1926f54122def 100644 (file)
@@ -1358,6 +1358,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        struct ieee80211_chanctx *ctx;
        struct sta_info *sta;
        int res, i;
+       bool reconfig_due_to_wowlan = false;
 
 #ifdef CONFIG_PM
        if (local->suspended)
@@ -1377,6 +1378,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                 * res is 1, which means the driver requested
                 * to go through a regular reset on wakeup.
                 */
+               reconfig_due_to_wowlan = true;
        }
 #endif
        /* everything else happens only if HW was up & running */
@@ -1526,6 +1528,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                          BSS_CHANGED_IDLE |
                          BSS_CHANGED_TXPOWER;
 
+#ifdef CONFIG_PM
+               if (local->resuming && !reconfig_due_to_wowlan)
+                       sdata->vif.bss_conf = sdata->suspend_bss_conf;
+#endif
+
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_STATION:
                        changed |= BSS_CHANGED_ASSOC |
@@ -1550,9 +1557,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 
                        /* fall through */
                case NL80211_IFTYPE_MESH_POINT:
-                       changed |= BSS_CHANGED_BEACON |
-                                  BSS_CHANGED_BEACON_ENABLED;
-                       ieee80211_bss_info_change_notify(sdata, changed);
+                       if (sdata->vif.bss_conf.enable_beacon) {
+                               changed |= BSS_CHANGED_BEACON |
+                                          BSS_CHANGED_BEACON_ENABLED;
+                               ieee80211_bss_info_change_notify(sdata, changed);
+                       }
                        break;
                case NL80211_IFTYPE_WDS:
                        break;
@@ -1632,7 +1641,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                mutex_lock(&local->sta_mtx);
 
                list_for_each_entry(sta, &local->sta_list, list) {
-                       ieee80211_sta_tear_down_BA_sessions(sta, true);
+                       ieee80211_sta_tear_down_BA_sessions(
+                                       sta, AGG_STOP_LOCAL_REQUEST);
                        clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
                }
 
@@ -1646,10 +1656,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
         * If this is for hw restart things are still running.
         * We may want to change that later, however.
         */
-       if (!local->suspended) {
+       if (!local->suspended || reconfig_due_to_wowlan)
                drv_restart_complete(local);
+
+       if (!local->suspended)
                return 0;
-       }
 
 #ifdef CONFIG_PM
        /* first set suspended false, then resuming */
@@ -1864,7 +1875,7 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
 }
 
 u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
-                                                          u32 cap)
+                              u32 cap)
 {
        __le32 tmp;
 
index aa64ea441676a1ce9fc8c2417cc4487eaecf2cec..25522e56d3507645e6aa4d89bc692da91263dad9 100644 (file)
@@ -338,7 +338,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
                dev->active_target = target;
                dev->rf_mode = NFC_RF_INITIATOR;
 
-               if (dev->ops->check_presence)
+               if (dev->ops->check_presence && !dev->shutting_down)
                        mod_timer(&dev->check_pres_timer, jiffies +
                                  msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
        }
@@ -429,7 +429,7 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
                rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb,
                                             cb_context);
 
-               if (!rc && dev->ops->check_presence)
+               if (!rc && dev->ops->check_presence && !dev->shutting_down)
                        mod_timer(&dev->check_pres_timer, jiffies +
                                  msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
        } else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) {
@@ -684,11 +684,6 @@ static void nfc_release(struct device *d)
 
        pr_debug("dev_name=%s\n", dev_name(&dev->dev));
 
-       if (dev->ops->check_presence) {
-               del_timer_sync(&dev->check_pres_timer);
-               cancel_work_sync(&dev->check_pres_work);
-       }
-
        nfc_genl_data_exit(&dev->genl_data);
        kfree(dev->targets);
        kfree(dev);
@@ -706,15 +701,16 @@ static void nfc_check_pres_work(struct work_struct *work)
                rc = dev->ops->check_presence(dev, dev->active_target);
                if (rc == -EOPNOTSUPP)
                        goto exit;
-               if (!rc) {
-                       mod_timer(&dev->check_pres_timer, jiffies +
-                                 msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
-               } else {
+               if (rc) {
                        u32 active_target_idx = dev->active_target->idx;
                        device_unlock(&dev->dev);
                        nfc_target_lost(dev, active_target_idx);
                        return;
                }
+
+               if (!dev->shutting_down)
+                       mod_timer(&dev->check_pres_timer, jiffies +
+                                 msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
        }
 
 exit:
@@ -761,6 +757,7 @@ struct nfc_dev *nfc_get_device(unsigned int idx)
  */
 struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                                    u32 supported_protocols,
+                                   u32 supported_se,
                                    int tx_headroom, int tx_tailroom)
 {
        struct nfc_dev *dev;
@@ -778,6 +775,8 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
 
        dev->ops = ops;
        dev->supported_protocols = supported_protocols;
+       dev->supported_se = supported_se;
+       dev->active_se = NFC_SE_NONE;
        dev->tx_headroom = tx_headroom;
        dev->tx_tailroom = tx_tailroom;
 
@@ -853,26 +852,27 @@ void nfc_unregister_device(struct nfc_dev *dev)
 
        id = dev->idx;
 
-       mutex_lock(&nfc_devlist_mutex);
-       nfc_devlist_generation++;
-
-       /* lock to avoid unregistering a device while an operation
-          is in progress */
-       device_lock(&dev->dev);
-       device_del(&dev->dev);
-       device_unlock(&dev->dev);
+       if (dev->ops->check_presence) {
+               device_lock(&dev->dev);
+               dev->shutting_down = true;
+               device_unlock(&dev->dev);
+               del_timer_sync(&dev->check_pres_timer);
+               cancel_work_sync(&dev->check_pres_work);
+       }
 
-       mutex_unlock(&nfc_devlist_mutex);
+       rc = nfc_genl_device_removed(dev);
+       if (rc)
+               pr_debug("The userspace won't be notified that the device %s "
+                        "was removed\n", dev_name(&dev->dev));
 
        nfc_llcp_unregister_device(dev);
 
-       rc = nfc_genl_device_removed(dev);
-       if (rc)
-               pr_debug("The userspace won't be notified that the device %s was removed\n",
-                        dev_name(&dev->dev));
+       mutex_lock(&nfc_devlist_mutex);
+       nfc_devlist_generation++;
+       device_del(&dev->dev);
+       mutex_unlock(&nfc_devlist_mutex);
 
        ida_simple_remove(&nfc_index_ida, id);
-
 }
 EXPORT_SYMBOL(nfc_unregister_device);
 
index 7d99410e6c1a542d91795a40f5e9d8bf9e719c5f..64f922be928127d8837899ae081044b48190447f 100644 (file)
@@ -280,14 +280,19 @@ static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe)
 static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev)
 {
        u8 param[2];
+       size_t param_len = 2;
 
        /* TODO: Find out what the identity reference data is
         * and fill param with it. HCI spec 6.1.3.5 */
 
        pr_debug("\n");
 
+       if (test_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &hdev->quirks))
+               param_len = 0;
+
        return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
-                                  NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL);
+                                  NFC_HCI_ADM_CLEAR_ALL_PIPE, param, param_len,
+                                  NULL);
 }
 
 int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate)
index 7bea574d59344f754ad74a1a390a8d742843549b..91020b210d8774416467bf009130cad9db5870a1 100644 (file)
@@ -57,6 +57,8 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
        int r = 0;
 
        mutex_lock(&hdev->msg_tx_mutex);
+       if (hdev->shutting_down)
+               goto exit;
 
        if (hdev->cmd_pending_msg) {
                if (timer_pending(&hdev->cmd_timer) == 0) {
@@ -295,6 +297,12 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
                goto exit;
        }
 
+       if (hdev->ops->event_received) {
+               r = hdev->ops->event_received(hdev, gate, event, skb);
+               if (r <= 0)
+                       goto exit_noskb;
+       }
+
        switch (event) {
        case NFC_HCI_EVT_TARGET_DISCOVERED:
                if (skb->len < 1) {     /* no status data? */
@@ -320,17 +328,15 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
                r = nfc_hci_target_discovered(hdev, gate);
                break;
        default:
-               if (hdev->ops->event_received) {
-                       hdev->ops->event_received(hdev, gate, event, skb);
-                       return;
-               }
-
+               pr_info("Discarded unknown event %x to gate %x\n", event, gate);
+               r = -EINVAL;
                break;
        }
 
 exit:
        kfree_skb(skb);
 
+exit_noskb:
        if (r) {
                /* TODO: There was an error dispatching the event,
                 * how to propagate up to nfc core?
@@ -669,8 +675,10 @@ static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
 
        if (hdev->ops->tm_send)
                return hdev->ops->tm_send(hdev, skb);
-       else
-               return -ENOTSUPP;
+
+       kfree_skb(skb);
+
+       return -ENOTSUPP;
 }
 
 static int hci_check_presence(struct nfc_dev *nfc_dev,
@@ -787,7 +795,9 @@ static struct nfc_ops hci_nfc_ops = {
 
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
+                                           unsigned long quirks,
                                            u32 protocols,
+                                           u32 supported_se,
                                            const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
@@ -813,7 +823,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                return NULL;
        }
 
-       hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
+       hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, supported_se,
                                         tx_headroom + HCI_CMDS_HEADROOM,
                                         tx_tailroom);
        if (!hdev->ndev) {
@@ -830,6 +840,8 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
 
        memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
 
+       hdev->quirks = quirks;
+
        return hdev;
 }
 EXPORT_SYMBOL(nfc_hci_allocate_device);
@@ -868,6 +880,28 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
 {
        struct hci_msg *msg, *n;
 
+       mutex_lock(&hdev->msg_tx_mutex);
+
+       if (hdev->cmd_pending_msg) {
+               if (hdev->cmd_pending_msg->cb)
+                       hdev->cmd_pending_msg->cb(
+                                            hdev->cmd_pending_msg->cb_context,
+                                            NULL, -ESHUTDOWN);
+               kfree(hdev->cmd_pending_msg);
+               hdev->cmd_pending_msg = NULL;
+       }
+
+       hdev->shutting_down = true;
+
+       mutex_unlock(&hdev->msg_tx_mutex);
+
+       del_timer_sync(&hdev->cmd_timer);
+       cancel_work_sync(&hdev->msg_tx_work);
+
+       cancel_work_sync(&hdev->msg_rx_work);
+
+       nfc_unregister_device(hdev->ndev);
+
        skb_queue_purge(&hdev->rx_hcp_frags);
        skb_queue_purge(&hdev->msg_rx_queue);
 
@@ -876,13 +910,6 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
                skb_queue_purge(&msg->msg_frags);
                kfree(msg);
        }
-
-       del_timer_sync(&hdev->cmd_timer);
-
-       nfc_unregister_device(hdev->ndev);
-
-       cancel_work_sync(&hdev->msg_tx_work);
-       cancel_work_sync(&hdev->msg_rx_work);
 }
 EXPORT_SYMBOL(nfc_hci_unregister_device);
 
index bc308a7ca6093f2857905beb3eecfe86d612671f..b6b4109f2343eb9cc9628c1c0ba07342d4f6c6f0 100644 (file)
@@ -105,6 +105,13 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
        }
 
        mutex_lock(&hdev->msg_tx_mutex);
+
+       if (hdev->shutting_down) {
+               err = -ESHUTDOWN;
+               mutex_unlock(&hdev->msg_tx_mutex);
+               goto out_skb_err;
+       }
+
        list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
        mutex_unlock(&hdev->msg_tx_mutex);
 
index df24be48d4dad2ee998bb483bdd4b1d109b24333..c6bc3bd950526ee2de42426337e74962e6181deb 100644 (file)
@@ -304,6 +304,8 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)
 
        skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM);
 
+       __net_timestamp(skb);
+
        nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX);
 
        return nfc_data_exchange(dev, local->target_idx, skb,
index ec43914c92a9fbb416c5170e1d74440bb4dd1bf1..85bc75c38dea67544a39d7e352b144f3a8f4af0e 100644 (file)
@@ -54,7 +54,6 @@ static void nfc_llcp_socket_purge(struct nfc_llcp_sock *sock)
 
        skb_queue_purge(&sock->tx_queue);
        skb_queue_purge(&sock->tx_pending_queue);
-       skb_queue_purge(&sock->tx_backlog_queue);
 
        if (local == NULL)
                return;
@@ -668,6 +667,8 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                        if (ptype == LLCP_PDU_I)
                                copy_skb = skb_copy(skb, GFP_ATOMIC);
 
+                       __net_timestamp(skb);
+
                        nfc_llcp_send_to_raw_sock(local, skb,
                                                  NFC_LLCP_DIRECTION_TX);
 
@@ -781,9 +782,15 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,
 
        /* There is no sequence with UI frames */
        skb_pull(skb, LLCP_HEADER_SIZE);
-       if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
-               pr_err("receive queue is full\n");
-               skb_queue_head(&llcp_sock->tx_backlog_queue, skb);
+       if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
+               /*
+                * UI frames will be freed from the socket layer, so we
+                * need to keep them alive until someone receives them.
+                */
+               skb_get(skb);
+       } else {
+               pr_err("Receive queue is full\n");
+               kfree_skb(skb);
        }
 
        nfc_llcp_sock_put(llcp_sock);
@@ -976,9 +983,15 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local,
                        pr_err("Received out of sequence I PDU\n");
 
                skb_pull(skb, LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE);
-               if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
-                       pr_err("receive queue is full\n");
-                       skb_queue_head(&llcp_sock->tx_backlog_queue, skb);
+               if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
+                       /*
+                        * I frames will be freed from the socket layer, so we
+                        * need to keep them alive until someone receives them.
+                        */
+                       skb_get(skb);
+               } else {
+                       pr_err("Receive queue is full\n");
+                       kfree_skb(skb);
                }
        }
 
@@ -1245,6 +1258,8 @@ static void nfc_llcp_rx_work(struct work_struct *work)
                print_hex_dump(KERN_DEBUG, "LLCP Rx: ", DUMP_PREFIX_OFFSET,
                               16, 1, skb->data, skb->len, true);
 
+       __net_timestamp(skb);
+
        nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX);
 
        switch (ptype) {
@@ -1296,6 +1311,13 @@ static void nfc_llcp_rx_work(struct work_struct *work)
        local->rx_pending = NULL;
 }
 
+static void __nfc_llcp_recv(struct nfc_llcp_local *local, struct sk_buff *skb)
+{
+       local->rx_pending = skb;
+       del_timer(&local->link_timer);
+       schedule_work(&local->rx_work);
+}
+
 void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
 {
        struct nfc_llcp_local *local = (struct nfc_llcp_local *) data;
@@ -1306,9 +1328,7 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
                return;
        }
 
-       local->rx_pending = skb_get(skb);
-       del_timer(&local->link_timer);
-       schedule_work(&local->rx_work);
+       __nfc_llcp_recv(local, skb);
 }
 
 int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
@@ -1319,9 +1339,7 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
        if (local == NULL)
                return -ENODEV;
 
-       local->rx_pending = skb_get(skb);
-       del_timer(&local->link_timer);
-       schedule_work(&local->rx_work);
+       __nfc_llcp_recv(local, skb);
 
        return 0;
 }
index 0d62366f8cc3f0e8fe33761d0ec6cc3e45bda677..0eae5c5095047dcd449af0ddba8fd402f79f50e2 100644 (file)
@@ -121,7 +121,6 @@ struct nfc_llcp_sock {
 
        struct sk_buff_head tx_queue;
        struct sk_buff_head tx_pending_queue;
-       struct sk_buff_head tx_backlog_queue;
 
        struct list_head accept_queue;
        struct sock *parent;
index fea22eb41b8242a1fb80d22c6aa2ca6515d78d0e..5332751943a9ec564befadda63a5a3749d7434c1 100644 (file)
@@ -672,25 +672,27 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        copied = min_t(unsigned int, rlen, len);
 
        cskb = skb;
-       if (memcpy_toiovec(msg->msg_iov, cskb->data, copied)) {
+       if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) {
                if (!(flags & MSG_PEEK))
                        skb_queue_head(&sk->sk_receive_queue, skb);
                return -EFAULT;
        }
 
+       sock_recv_timestamp(msg, sk, skb);
+
        if (sk->sk_type == SOCK_DGRAM && msg->msg_name) {
                struct nfc_llcp_ui_cb *ui_cb = nfc_llcp_ui_skb_cb(skb);
-               struct sockaddr_nfc_llcp sockaddr;
+               struct sockaddr_nfc_llcp *sockaddr =
+                       (struct sockaddr_nfc_llcp *) msg->msg_name;
 
-               pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap);
+               msg->msg_namelen = sizeof(struct sockaddr_nfc_llcp);
 
-               sockaddr.sa_family = AF_NFC;
-               sockaddr.nfc_protocol = NFC_PROTO_NFC_DEP;
-               sockaddr.dsap = ui_cb->dsap;
-               sockaddr.ssap = ui_cb->ssap;
+               pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap);
 
-               memcpy(msg->msg_name, &sockaddr, sizeof(sockaddr));
-               msg->msg_namelen = sizeof(sockaddr);
+               sockaddr->sa_family = AF_NFC;
+               sockaddr->nfc_protocol = NFC_PROTO_NFC_DEP;
+               sockaddr->dsap = ui_cb->dsap;
+               sockaddr->ssap = ui_cb->ssap;
        }
 
        /* Mark read part of skb as used */
@@ -806,7 +808,6 @@ struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp)
        llcp_sock->reserved_ssap = LLCP_SAP_MAX;
        skb_queue_head_init(&llcp_sock->tx_queue);
        skb_queue_head_init(&llcp_sock->tx_pending_queue);
-       skb_queue_head_init(&llcp_sock->tx_backlog_queue);
        INIT_LIST_HEAD(&llcp_sock->accept_queue);
 
        if (sock != NULL)
@@ -821,7 +822,6 @@ void nfc_llcp_sock_free(struct nfc_llcp_sock *sock)
 
        skb_queue_purge(&sock->tx_queue);
        skb_queue_purge(&sock->tx_pending_queue);
-       skb_queue_purge(&sock->tx_backlog_queue);
 
        list_del_init(&sock->accept_queue);
 
index 5f98dc1bf03943abfd3315109acf94431858a9e2..48ada0ec749ec369bb6fc78308f12805cfa90c69 100644 (file)
@@ -658,6 +658,7 @@ static struct nfc_ops nci_nfc_ops = {
  */
 struct nci_dev *nci_allocate_device(struct nci_ops *ops,
                                    __u32 supported_protocols,
+                                   __u32 supported_se,
                                    int tx_headroom, int tx_tailroom)
 {
        struct nci_dev *ndev;
@@ -680,6 +681,7 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,
 
        ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
                                            supported_protocols,
+                                           supported_se,
                                            tx_headroom + NCI_DATA_HDR_SIZE,
                                            tx_tailroom);
        if (!ndev->nfc_dev)
index 3568ae16786d513830040ddb020c33237649198b..504b883439f1b535b565c8efdc79e965afe465d4 100644 (file)
@@ -366,6 +366,7 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
        if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
            nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
            nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
+           nla_put_u32(msg, NFC_ATTR_SE, dev->supported_se) ||
            nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) ||
            nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
                goto nla_put_failure;
index 324e8d851dc4ca67fd0ded84a927e782c3647c8c..a4a14e8f55cc42f2c77146f73b4c55b8434337b5 100644 (file)
@@ -46,3 +46,65 @@ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
 
        return err;
 }
+
+void cfg80211_ch_switch_notify(struct net_device *dev,
+                              struct cfg80211_chan_def *chandef)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_ch_switch_notify(dev, chandef);
+
+       wdev_lock(wdev);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO))
+               goto out;
+
+       wdev->channel = chandef->chan;
+       nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
+out:
+       wdev_unlock(wdev);
+       return;
+}
+EXPORT_SYMBOL(cfg80211_ch_switch_notify);
+
+bool cfg80211_rx_spurious_frame(struct net_device *dev,
+                               const u8 *addr, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       bool ret;
+
+       trace_cfg80211_rx_spurious_frame(dev, addr);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
+               trace_cfg80211_return_bool(false);
+               return false;
+       }
+       ret = nl80211_unexpected_frame(dev, addr, gfp);
+       trace_cfg80211_return_bool(ret);
+       return ret;
+}
+EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
+
+bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
+                                       const u8 *addr, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       bool ret;
+
+       trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+                   wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
+               trace_cfg80211_return_bool(false);
+               return false;
+       }
+       ret = nl80211_unexpected_4addr_frame(dev, addr, gfp);
+       trace_cfg80211_return_bool(ret);
+       return ret;
+}
+EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
index a7990bb165295bcefbd88c6396026e2db8b7fa38..396373f3ec260b96a178dce7e72c239cf78bdc51 100644 (file)
@@ -76,6 +76,10 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
                        return false;
                if (!chandef->center_freq2)
                        return false;
+               /* adjacent is not allowed -- that's a 160 MHz channel */
+               if (chandef->center_freq1 - chandef->center_freq2 == 80 ||
+                   chandef->center_freq2 - chandef->center_freq1 == 80)
+                       return false;
                break;
        case NL80211_CHAN_WIDTH_80:
                if (chandef->center_freq1 != control_freq + 30 &&
index b677eab55b68465494bffae68a2c38c2321f18ca..40dbe37cfbf6389c7f3178f474ddb58174b676db 100644 (file)
@@ -57,9 +57,6 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
 {
        struct cfg80211_registered_device *result = NULL, *rdev;
 
-       if (!wiphy_idx_valid(wiphy_idx))
-               return NULL;
-
        assert_cfg80211_lock();
 
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
@@ -74,10 +71,8 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
 
 int get_wiphy_idx(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev;
-       if (!wiphy)
-               return WIPHY_IDX_STALE;
-       rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
        return rdev->wiphy_idx;
 }
 
@@ -86,9 +81,6 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
 {
        struct cfg80211_registered_device *rdev;
 
-       if (!wiphy_idx_valid(wiphy_idx))
-               return NULL;
-
        assert_cfg80211_lock();
 
        rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx);
@@ -309,7 +301,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
 
        rdev->wiphy_idx = wiphy_counter++;
 
-       if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) {
+       if (unlikely(rdev->wiphy_idx < 0)) {
                wiphy_counter--;
                mutex_unlock(&cfg80211_mutex);
                /* ugh, wrapped! */
@@ -390,8 +382,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
 
                c = &wiphy->iface_combinations[i];
 
-               /* Combinations with just one interface aren't real */
-               if (WARN_ON(c->max_interfaces < 2))
+               /*
+                * Combinations with just one interface aren't real,
+                * however we make an exception for DFS.
+                */
+               if (WARN_ON((c->max_interfaces < 2) && !c->radar_detect_widths))
                        return -EINVAL;
 
                /* Need at least one channel */
@@ -406,6 +401,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
                                CFG80211_MAX_NUM_DIFFERENT_CHANNELS))
                        return -EINVAL;
 
+               /* DFS only works on one channel. */
+               if (WARN_ON(c->radar_detect_widths &&
+                           (c->num_different_channels > 1)))
+                       return -EINVAL;
+
                if (WARN_ON(!c->n_limits))
                        return -EINVAL;
 
@@ -478,6 +478,11 @@ int wiphy_register(struct wiphy *wiphy)
                           ETH_ALEN)))
                return -EINVAL;
 
+       if (WARN_ON(wiphy->max_acl_mac_addrs &&
+                   (!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) ||
+                    !rdev->ops->set_mac_acl)))
+               return -EINVAL;
+
        if (wiphy->addresses)
                memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
 
index 3563097169cb3ca8b767e9f5398a741c03a9885b..8396f7671c8db682c87baf657f607ae883e4128c 100644 (file)
@@ -18,6 +18,9 @@
 #include <net/cfg80211.h>
 #include "reg.h"
 
+
+#define WIPHY_IDX_INVALID      -1
+
 struct cfg80211_registered_device {
        const struct cfg80211_ops *ops;
        struct list_head list;
@@ -86,7 +89,7 @@ struct cfg80211_registered_device {
 
        /* must be last because of the way we do wiphy_priv(),
         * and it should at least be aligned to NETDEV_ALIGN */
-       struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
+       struct wiphy wiphy __aligned(NETDEV_ALIGN);
 };
 
 static inline
@@ -96,13 +99,6 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
        return container_of(wiphy, struct cfg80211_registered_device, wiphy);
 }
 
-/* Note 0 is valid, hence phy0 */
-static inline
-bool wiphy_idx_valid(int wiphy_idx)
-{
-       return wiphy_idx >= 0;
-}
-
 static inline void
 cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
 {
@@ -126,12 +122,6 @@ static inline void assert_cfg80211_lock(void)
        lockdep_assert_held(&cfg80211_mutex);
 }
 
-/*
- * You can use this to mark a wiphy_idx as not having an associated wiphy.
- * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL
- */
-#define WIPHY_IDX_STALE -1
-
 struct cfg80211_internal_bss {
        struct list_head list;
        struct rb_node rbn;
@@ -435,7 +425,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
                                 struct wireless_dev *wdev,
                                 enum nl80211_iftype iftype,
                                 struct ieee80211_channel *chan,
-                                enum cfg80211_chan_mode chanmode);
+                                enum cfg80211_chan_mode chanmode,
+                                u8 radar_detect);
 
 static inline int
 cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
@@ -443,7 +434,7 @@ cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
                              enum nl80211_iftype iftype)
 {
        return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL,
-                                           CHAN_MODE_UNDEFINED);
+                                           CHAN_MODE_UNDEFINED, 0);
 }
 
 static inline int
@@ -460,7 +451,7 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev,
                      enum cfg80211_chan_mode chanmode)
 {
        return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                           chan, chanmode);
+                                           chan, chanmode, 0);
 }
 
 void
index f9d6ce5cfabbaebfc202f2d0290d8b1f0fbbc314..55957a284f6c7e82c9b403dae01798316224ad49 100644 (file)
 
 #define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50
 
+#define MESH_DEFAULT_BEACON_INTERVAL   1000    /* in 1024 us units (=TUs) */
+#define MESH_DEFAULT_DTIM_PERIOD       2
+#define MESH_DEFAULT_AWAKE_WINDOW      10      /* in 1024 us units (=TUs) */
+
 const struct mesh_config default_mesh_config = {
        .dot11MeshRetryTimeout = MESH_RET_T,
        .dot11MeshConfirmTimeout = MESH_CONF_T,
@@ -69,6 +73,8 @@ const struct mesh_config default_mesh_config = {
        .dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT,
        .dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL,
        .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL,
+       .power_mode = NL80211_MESH_POWER_ACTIVE,
+       .dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW,
 };
 
 const struct mesh_setup default_mesh_setup = {
@@ -79,6 +85,8 @@ const struct mesh_setup default_mesh_setup = {
        .ie = NULL,
        .ie_len = 0,
        .is_secure = false,
+       .beacon_interval = MESH_DEFAULT_BEACON_INTERVAL,
+       .dtim_period = MESH_DEFAULT_DTIM_PERIOD,
 };
 
 int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
index 5e8123ee63fd316d48e224fe65b8336300ec2b79..461e692cdfec40d07e99e873f596e347e5421670 100644 (file)
@@ -987,65 +987,3 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
        nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
 }
 EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
-
-void cfg80211_ch_switch_notify(struct net_device *dev,
-                              struct cfg80211_chan_def *chandef)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_ch_switch_notify(dev, chandef);
-
-       wdev_lock(wdev);
-
-       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
-                   wdev->iftype != NL80211_IFTYPE_P2P_GO))
-               goto out;
-
-       wdev->channel = chandef->chan;
-       nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
-out:
-       wdev_unlock(wdev);
-       return;
-}
-EXPORT_SYMBOL(cfg80211_ch_switch_notify);
-
-bool cfg80211_rx_spurious_frame(struct net_device *dev,
-                               const u8 *addr, gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       bool ret;
-
-       trace_cfg80211_rx_spurious_frame(dev, addr);
-
-       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
-                   wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
-               trace_cfg80211_return_bool(false);
-               return false;
-       }
-       ret = nl80211_unexpected_frame(dev, addr, gfp);
-       trace_cfg80211_return_bool(ret);
-       return ret;
-}
-EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
-
-bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
-                                       const u8 *addr, gfp_t gfp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       bool ret;
-
-       trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
-
-       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
-                   wdev->iftype != NL80211_IFTYPE_P2P_GO &&
-                   wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
-               trace_cfg80211_return_bool(false);
-               return false;
-       }
-       ret = nl80211_unexpected_4addr_frame(dev, addr, gfp);
-       trace_cfg80211_return_bool(ret);
-       return ret;
-}
-EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
index f45706adaf3411813133704d41a12aa9d1d59712..b5978ab4ad7af95071169368b3e3a6064c215373 100644 (file)
@@ -365,6 +365,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
        [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
        [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
+       [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
+       [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
 };
 
 /* policy for the key attributes */
@@ -856,6 +858,9 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy,
                    nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
                                c->max_interfaces))
                        goto nla_put_failure;
+               if (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+                               c->radar_detect_widths))
+                       goto nla_put_failure;
 
                nla_nest_end(msg, nl_combi);
        }
@@ -1265,6 +1270,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
                    dev->wiphy.ht_capa_mod_mask))
                goto nla_put_failure;
 
+       if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
+           dev->wiphy.max_acl_mac_addrs &&
+           nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
+                       dev->wiphy.max_acl_mac_addrs))
+               goto nla_put_failure;
+
        return genlmsg_end(msg, hdr);
 
  nla_put_failure:
@@ -2079,6 +2090,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
            !(rdev->wiphy.interface_modes & (1 << type)))
                return -EOPNOTSUPP;
 
+       if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) {
+               nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
+                          ETH_ALEN);
+               if (!is_valid_ether_addr(params.macaddr))
+                       return -EADDRNOTAVAIL;
+       }
+
        if (info->attrs[NL80211_ATTR_4ADDR]) {
                params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
                err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
@@ -2481,6 +2499,97 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
+/* This function returns an error or the number of nested attributes */
+static int validate_acl_mac_addrs(struct nlattr *nl_attr)
+{
+       struct nlattr *attr;
+       int n_entries = 0, tmp;
+
+       nla_for_each_nested(attr, nl_attr, tmp) {
+               if (nla_len(attr) != ETH_ALEN)
+                       return -EINVAL;
+
+               n_entries++;
+       }
+
+       return n_entries;
+}
+
+/*
+ * This function parses ACL information and allocates memory for ACL data.
+ * On successful return, the calling function is responsible to free the
+ * ACL buffer returned by this function.
+ */
+static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy,
+                                               struct genl_info *info)
+{
+       enum nl80211_acl_policy acl_policy;
+       struct nlattr *attr;
+       struct cfg80211_acl_data *acl;
+       int i = 0, n_entries, tmp;
+
+       if (!wiphy->max_acl_mac_addrs)
+               return ERR_PTR(-EOPNOTSUPP);
+
+       if (!info->attrs[NL80211_ATTR_ACL_POLICY])
+               return ERR_PTR(-EINVAL);
+
+       acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]);
+       if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED &&
+           acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED)
+               return ERR_PTR(-EINVAL);
+
+       if (!info->attrs[NL80211_ATTR_MAC_ADDRS])
+               return ERR_PTR(-EINVAL);
+
+       n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]);
+       if (n_entries < 0)
+               return ERR_PTR(n_entries);
+
+       if (n_entries > wiphy->max_acl_mac_addrs)
+               return ERR_PTR(-ENOTSUPP);
+
+       acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries),
+                     GFP_KERNEL);
+       if (!acl)
+               return ERR_PTR(-ENOMEM);
+
+       nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) {
+               memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN);
+               i++;
+       }
+
+       acl->n_acl_entries = n_entries;
+       acl->acl_policy = acl_policy;
+
+       return acl;
+}
+
+static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct cfg80211_acl_data *acl;
+       int err;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+               return -EOPNOTSUPP;
+
+       if (!dev->ieee80211_ptr->beacon_interval)
+               return -EINVAL;
+
+       acl = parse_acl_data(&rdev->wiphy, info);
+       if (IS_ERR(acl))
+               return PTR_ERR(acl);
+
+       err = rdev_set_mac_acl(rdev, dev, acl);
+
+       kfree(acl);
+
+       return err;
+}
+
 static int nl80211_parse_beacon(struct genl_info *info,
                                struct cfg80211_beacon_data *bcn)
 {
@@ -2724,6 +2833,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
+       if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
+               params.acl = parse_acl_data(&rdev->wiphy, info);
+               if (IS_ERR(params.acl))
+                       return PTR_ERR(params.acl);
+       }
+
        err = rdev_start_ap(rdev, dev, &params);
        if (!err) {
                wdev->preset_chandef = params.chandef;
@@ -2732,6 +2847,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                wdev->ssid_len = params.ssid_len;
                memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
        }
+
+       kfree(params.acl);
+
        return err;
 }
 
@@ -3001,6 +3119,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
            nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,
                        sinfo->beacon_loss_count))
                goto nla_put_failure;
+       if ((sinfo->filled & STATION_INFO_LOCAL_PM) &&
+           nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM,
+                       sinfo->local_pm))
+               goto nla_put_failure;
+       if ((sinfo->filled & STATION_INFO_PEER_PM) &&
+           nla_put_u32(msg, NL80211_STA_INFO_PEER_PM,
+                       sinfo->peer_pm))
+               goto nla_put_failure;
+       if ((sinfo->filled & STATION_INFO_NONPEER_PM) &&
+           nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM,
+                       sinfo->nonpeer_pm))
+               goto nla_put_failure;
        if (sinfo->filled & STATION_INFO_BSS_PARAM) {
                bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
                if (!bss_param)
@@ -3188,13 +3318,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                        nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
        }
 
-       if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
-               params.listen_interval =
-                   nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
-
-       if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
-               params.ht_capa =
-                       nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+       if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL] ||
+           info->attrs[NL80211_ATTR_HT_CAPABILITY])
+               return -EINVAL;
 
        if (!rdev->ops->change_station)
                return -EOPNOTSUPP;
@@ -3210,6 +3336,17 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                params.plink_state =
                    nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
 
+       if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
+               enum nl80211_mesh_power_mode pm = nla_get_u32(
+                       info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
+
+               if (pm <= NL80211_MESH_POWER_UNKNOWN ||
+                   pm > NL80211_MESH_POWER_MAX)
+                       return -EINVAL;
+
+               params.local_pm = pm;
+       }
+
        switch (dev->ieee80211_ptr->iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_AP_VLAN:
@@ -3217,6 +3354,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                /* disallow mesh-specific things */
                if (params.plink_action)
                        return -EINVAL;
+               if (params.local_pm)
+                       return -EINVAL;
 
                /* TDLS can't be set, ... */
                if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
@@ -3231,11 +3370,25 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                /* accept only the listed bits */
                if (params.sta_flags_mask &
                                ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+                                 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                                 BIT(NL80211_STA_FLAG_ASSOCIATED) |
                                  BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
                                  BIT(NL80211_STA_FLAG_WME) |
                                  BIT(NL80211_STA_FLAG_MFP)))
                        return -EINVAL;
 
+               /* but authenticated/associated only if driver handles it */
+               if (!(rdev->wiphy.features &
+                               NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
+                   params.sta_flags_mask &
+                               (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                                BIT(NL80211_STA_FLAG_ASSOCIATED)))
+                       return -EINVAL;
+
+               /* reject other things that can't change */
+               if (params.supported_rates)
+                       return -EINVAL;
+
                /* must be last in here for error handling */
                params.vlan = get_vlan(info, rdev);
                if (IS_ERR(params.vlan))
@@ -3255,9 +3408,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                /* disallow things sta doesn't support */
                if (params.plink_action)
                        return -EINVAL;
-               if (params.ht_capa)
-                       return -EINVAL;
-               if (params.listen_interval >= 0)
+               if (params.local_pm)
                        return -EINVAL;
                /* reject any changes other than AUTHORIZED */
                if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
@@ -3267,9 +3418,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                /* disallow things mesh doesn't support */
                if (params.vlan)
                        return -EINVAL;
-               if (params.ht_capa)
-                       return -EINVAL;
-               if (params.listen_interval >= 0)
+               if (params.supported_rates)
                        return -EINVAL;
                /*
                 * No special handling for TDLS here -- the userspace
@@ -3393,17 +3542,31 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                /* but don't bother the driver with it */
                params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
 
+               /* allow authenticated/associated only if driver handles it */
+               if (!(rdev->wiphy.features &
+                               NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
+                   params.sta_flags_mask &
+                               (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                                BIT(NL80211_STA_FLAG_ASSOCIATED)))
+                       return -EINVAL;
+
                /* must be last in here for error handling */
                params.vlan = get_vlan(info, rdev);
                if (IS_ERR(params.vlan))
                        return PTR_ERR(params.vlan);
                break;
        case NL80211_IFTYPE_MESH_POINT:
+               /* associated is disallowed */
+               if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
+                       return -EINVAL;
                /* TDLS peers cannot be added */
                if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_STATION:
+               /* associated is disallowed */
+               if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
+                       return -EINVAL;
                /* Only TDLS peers can be added */
                if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
                        return -EINVAL;
@@ -3787,12 +3950,8 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
         * window between nl80211_init() and regulatory_init(), if that is
         * even possible.
         */
-       mutex_lock(&cfg80211_mutex);
-       if (unlikely(!cfg80211_regdomain)) {
-               mutex_unlock(&cfg80211_mutex);
+       if (unlikely(!rcu_access_pointer(cfg80211_regdomain)))
                return -EINPROGRESS;
-       }
-       mutex_unlock(&cfg80211_mutex);
 
        if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
                return -EINVAL;
@@ -3908,7 +4067,11 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
            nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
                        cur_params.dot11MeshHWMProotInterval) ||
            nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
-                       cur_params.dot11MeshHWMPconfirmationInterval))
+                       cur_params.dot11MeshHWMPconfirmationInterval) ||
+           nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE,
+                       cur_params.power_mode) ||
+           nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
+                       cur_params.dot11MeshAwakeWindowDuration))
                goto nla_put_failure;
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
@@ -3947,6 +4110,8 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
        [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 },
        [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 },
        [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 },
+       [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
 };
 
 static const struct nla_policy
@@ -3967,13 +4132,15 @@ static int nl80211_parse_mesh_config(struct genl_info *info,
        struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
        u32 mask = 0;
 
-#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
-do {\
-       if (table[attr_num]) {\
-               cfg->param = nla_fn(table[attr_num]); \
-               mask |= (1 << (attr_num - 1)); \
-       } \
-} while (0);\
+#define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, min, max, mask, attr, fn) \
+do {                                                                       \
+       if (tb[attr]) {                                                     \
+               if (fn(tb[attr]) < min || fn(tb[attr]) > max)               \
+                       return -EINVAL;                                     \
+               cfg->param = fn(tb[attr]);                                  \
+               mask |= (1 << (attr - 1));                                  \
+       }                                                                   \
+} while (0)
 
 
        if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
@@ -3988,83 +4155,98 @@ do {\
        BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
 
        /* Fill in the params struct */
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, 1, 255,
                                  mask, NL80211_MESHCONF_RETRY_TIMEOUT,
                                  nla_get_u16);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, 1, 255,
                                  mask, NL80211_MESHCONF_CONFIRM_TIMEOUT,
                                  nla_get_u16);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, 1, 255,
                                  mask, NL80211_MESHCONF_HOLDING_TIMEOUT,
                                  nla_get_u16);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, 0, 255,
                                  mask, NL80211_MESHCONF_MAX_PEER_LINKS,
                                  nla_get_u16);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, 0, 16,
                                  mask, NL80211_MESHCONF_MAX_RETRIES,
                                  nla_get_u8);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, 1, 255,
                                  mask, NL80211_MESHCONF_TTL, nla_get_u8);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, 1, 255,
                                  mask, NL80211_MESHCONF_ELEMENT_TTL,
                                  nla_get_u8);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, 0, 1,
                                  mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
                                  nla_get_u8);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, mask,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor,
+                                 1, 255, mask,
                                  NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
                                  nla_get_u32);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, 0, 255,
                                  mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
                                  nla_get_u8);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, 1, 65535,
                                  mask, NL80211_MESHCONF_PATH_REFRESH_TIME,
                                  nla_get_u32);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, 1, 65535,
                                  mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
                                  nla_get_u16);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, mask,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
+                                 1, 65535, mask,
                                  NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
                                  nla_get_u32);
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
-                                 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+                                 1, 65535, mask,
+                                 NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
                                  nla_get_u16);
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval,
-                                 mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
+                                 1, 65535, mask,
+                                 NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
                                  nla_get_u16);
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
-                                 dot11MeshHWMPnetDiameterTraversalTime, mask,
+                                 dot11MeshHWMPnetDiameterTraversalTime,
+                                 1, 65535, mask,
                                  NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
                                  nla_get_u16);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, mask,
-                                 NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, mask,
-                                 NL80211_MESHCONF_HWMP_RANN_INTERVAL,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, 0, 4,
+                                 mask, NL80211_MESHCONF_HWMP_ROOTMODE,
+                                 nla_get_u8);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, 1, 65535,
+                                 mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
                                  nla_get_u16);
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
-                                 dot11MeshGateAnnouncementProtocol, mask,
-                                 NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
+                                 dot11MeshGateAnnouncementProtocol, 0, 1,
+                                 mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
                                  nla_get_u8);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1,
                                  mask, NL80211_MESHCONF_FORWARDING,
                                  nla_get_u8);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, 1, 255,
                                  mask, NL80211_MESHCONF_RSSI_THRESHOLD,
                                  nla_get_u32);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, 0, 16,
                                  mask, NL80211_MESHCONF_HT_OPMODE,
                                  nla_get_u16);
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout,
-                                 mask,
+                                 1, 65535, mask,
                                  NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
                                  nla_get_u32);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, 1, 65535,
                                  mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
                                  nla_get_u16);
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
-                                 dot11MeshHWMPconfirmationInterval, mask,
+                                 dot11MeshHWMPconfirmationInterval,
+                                 1, 65535, mask,
                                  NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
                                  nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode,
+                                 NL80211_MESH_POWER_ACTIVE,
+                                 NL80211_MESH_POWER_MAX,
+                                 mask, NL80211_MESHCONF_POWER_MODE,
+                                 nla_get_u32);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
+                                 0, 65535, mask,
+                                 NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
        if (mask_out)
                *mask_out = mask;
 
@@ -4152,6 +4334,7 @@ static int nl80211_update_mesh_config(struct sk_buff *skb,
 
 static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
 {
+       const struct ieee80211_regdomain *regdom;
        struct sk_buff *msg;
        void *hdr = NULL;
        struct nlattr *nl_reg_rules;
@@ -4174,35 +4357,36 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
        if (!hdr)
                goto put_failure;
 
-       if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2,
-                          cfg80211_regdomain->alpha2) ||
-           (cfg80211_regdomain->dfs_region &&
-            nla_put_u8(msg, NL80211_ATTR_DFS_REGION,
-                       cfg80211_regdomain->dfs_region)))
-               goto nla_put_failure;
-
        if (reg_last_request_cell_base() &&
            nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
                        NL80211_USER_REG_HINT_CELL_BASE))
                goto nla_put_failure;
 
+       rcu_read_lock();
+       regdom = rcu_dereference(cfg80211_regdomain);
+
+       if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
+           (regdom->dfs_region &&
+            nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
+               goto nla_put_failure_rcu;
+
        nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
        if (!nl_reg_rules)
-               goto nla_put_failure;
+               goto nla_put_failure_rcu;
 
-       for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
+       for (i = 0; i < regdom->n_reg_rules; i++) {
                struct nlattr *nl_reg_rule;
                const struct ieee80211_reg_rule *reg_rule;
                const struct ieee80211_freq_range *freq_range;
                const struct ieee80211_power_rule *power_rule;
 
-               reg_rule = &cfg80211_regdomain->reg_rules[i];
+               reg_rule = &regdom->reg_rules[i];
                freq_range = &reg_rule->freq_range;
                power_rule = &reg_rule->power_rule;
 
                nl_reg_rule = nla_nest_start(msg, i);
                if (!nl_reg_rule)
-                       goto nla_put_failure;
+                       goto nla_put_failure_rcu;
 
                if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,
                                reg_rule->flags) ||
@@ -4216,10 +4400,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
                                power_rule->max_antenna_gain) ||
                    nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
                                power_rule->max_eirp))
-                       goto nla_put_failure;
+                       goto nla_put_failure_rcu;
 
                nla_nest_end(msg, nl_reg_rule);
        }
+       rcu_read_unlock();
 
        nla_nest_end(msg, nl_reg_rules);
 
@@ -4227,6 +4412,8 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
        err = genlmsg_reply(msg, info);
        goto out;
 
+nla_put_failure_rcu:
+       rcu_read_unlock();
 nla_put_failure:
        genlmsg_cancel(msg, hdr);
 put_failure:
@@ -4259,27 +4446,18 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
 
        nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
-                       rem_reg_rules) {
+                           rem_reg_rules) {
                num_rules++;
                if (num_rules > NL80211_MAX_SUPP_REG_RULES)
                        return -EINVAL;
        }
 
-       mutex_lock(&cfg80211_mutex);
-
-       if (!reg_is_valid_request(alpha2)) {
-               r = -EINVAL;
-               goto bad_reg;
-       }
-
        size_of_regd = sizeof(struct ieee80211_regdomain) +
-               (num_rules * sizeof(struct ieee80211_reg_rule));
+                      num_rules * sizeof(struct ieee80211_reg_rule);
 
        rd = kzalloc(size_of_regd, GFP_KERNEL);
-       if (!rd) {
-               r = -ENOMEM;
-               goto bad_reg;
-       }
+       if (!rd)
+               return -ENOMEM;
 
        rd->n_reg_rules = num_rules;
        rd->alpha2[0] = alpha2[0];
@@ -4293,10 +4471,10 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                rd->dfs_region = dfs_region;
 
        nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
-                       rem_reg_rules) {
+                           rem_reg_rules) {
                nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
-                       nla_data(nl_reg_rule), nla_len(nl_reg_rule),
-                       reg_rule_policy);
+                         nla_data(nl_reg_rule), nla_len(nl_reg_rule),
+                         reg_rule_policy);
                r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
                if (r)
                        goto bad_reg;
@@ -4309,16 +4487,14 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       BUG_ON(rule_idx != num_rules);
+       mutex_lock(&cfg80211_mutex);
 
        r = set_regdom(rd);
-
+       /* set_regdom took ownership */
+       rd = NULL;
        mutex_unlock(&cfg80211_mutex);
 
-       return r;
-
  bad_reg:
-       mutex_unlock(&cfg80211_mutex);
        kfree(rd);
        return r;
 }
@@ -5867,6 +6043,15 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
+       if (info->attrs[NL80211_ATTR_USE_MFP]) {
+               connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
+               if (connect.mfp != NL80211_MFP_REQUIRED &&
+                   connect.mfp != NL80211_MFP_NO)
+                       return -EINVAL;
+       } else {
+               connect.mfp = NL80211_MFP_NO;
+       }
+
        if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
                connect.channel =
                        ieee80211_get_channel(wiphy,
@@ -6652,6 +6837,21 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
                            nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
                        return -EINVAL;
 
+       if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
+               setup.beacon_interval =
+                       nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
+               if (setup.beacon_interval < 10 ||
+                   setup.beacon_interval > 10000)
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
+               setup.dtim_period =
+                       nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
+               if (setup.dtim_period < 1 || setup.dtim_period > 100)
+                       return -EINVAL;
+       }
+
        if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
                /* parse additional setup parameters if given */
                err = nl80211_parse_mesh_setup(info, &setup);
@@ -7784,6 +7984,14 @@ static struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_SET_MAC_ACL,
+               .doit = nl80211_set_mac_acl,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -8051,7 +8259,7 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
                        goto nla_put_failure;
        }
 
-       if (wiphy_idx_valid(request->wiphy_idx) &&
+       if (request->wiphy_idx != WIPHY_IDX_INVALID &&
            nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
                goto nla_put_failure;
 
index 6c0c8191f83771e361fd1f4a14424383d347d7f6..422d38291d66e3eb00c8cf2cdb5b9369182530fd 100644 (file)
@@ -875,4 +875,16 @@ static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev,
        rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
        trace_rdev_return_void(&rdev->wiphy);
 }                                      
+
+static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev,
+                                  struct net_device *dev,
+                                  struct cfg80211_acl_data *params)
+{
+       int ret;
+
+       trace_rdev_set_mac_acl(&rdev->wiphy, dev, params);
+       ret = rdev->ops->set_mac_acl(&rdev->wiphy, dev, params);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+       return ret;
+}
 #endif /* __CFG80211_RDEV_OPS */
index 82c4fc7c994cbe3a0dc89b4a6708f873fcc783d9..de02d633c212eb6ea516a7d87e05978a2a1b3ba9 100644 (file)
@@ -48,7 +48,6 @@
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/list.h>
-#include <linux/random.h>
 #include <linux/ctype.h>
 #include <linux/nl80211.h>
 #include <linux/platform_device.h>
 #define REG_DBG_PRINT(args...)
 #endif
 
+enum reg_request_treatment {
+       REG_REQ_OK,
+       REG_REQ_IGNORE,
+       REG_REQ_INTERSECT,
+       REG_REQ_ALREADY_SET,
+};
+
 static struct regulatory_request core_request_world = {
        .initiator = NL80211_REGDOM_SET_BY_CORE,
        .alpha2[0] = '0',
@@ -76,7 +82,8 @@ static struct regulatory_request core_request_world = {
 };
 
 /* Receipt of information from last regulatory request */
-static struct regulatory_request *last_request = &core_request_world;
+static struct regulatory_request __rcu *last_request =
+       (void __rcu *)&core_request_world;
 
 /* To trigger userspace events */
 static struct platform_device *reg_pdev;
@@ -88,16 +95,16 @@ static struct device_type reg_device_type = {
 /*
  * Central wireless core regulatory domains, we only need two,
  * the current one and a world regulatory domain in case we have no
- * information to give us an alpha2
+ * information to give us an alpha2.
  */
-const struct ieee80211_regdomain *cfg80211_regdomain;
+const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
 
 /*
  * Protects static reg.c components:
- *     - cfg80211_world_regdom
- *     - cfg80211_regdom
- *     - last_request
- *     - reg_num_devs_support_basehint
+ *     - cfg80211_regdomain (if not used with RCU)
+ *     - cfg80211_world_regdom
+ *     - last_request (if not used with RCU)
+ *     - reg_num_devs_support_basehint
  */
 static DEFINE_MUTEX(reg_mutex);
 
@@ -112,6 +119,31 @@ static inline void assert_reg_lock(void)
        lockdep_assert_held(&reg_mutex);
 }
 
+static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
+{
+       return rcu_dereference_protected(cfg80211_regdomain,
+                                        lockdep_is_held(&reg_mutex));
+}
+
+static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
+{
+       return rcu_dereference_protected(wiphy->regd,
+                                        lockdep_is_held(&reg_mutex));
+}
+
+static void rcu_free_regdom(const struct ieee80211_regdomain *r)
+{
+       if (!r)
+               return;
+       kfree_rcu((struct ieee80211_regdomain *)r, rcu_head);
+}
+
+static struct regulatory_request *get_last_request(void)
+{
+       return rcu_dereference_check(last_request,
+                                    lockdep_is_held(&reg_mutex));
+}
+
 /* Used to queue up regulatory hints */
 static LIST_HEAD(reg_requests_list);
 static spinlock_t reg_requests_lock;
@@ -177,28 +209,37 @@ static char user_alpha2[2];
 module_param(ieee80211_regdom, charp, 0444);
 MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
 
-static void reset_regdomains(bool full_reset)
+static void reset_regdomains(bool full_reset,
+                            const struct ieee80211_regdomain *new_regdom)
 {
+       const struct ieee80211_regdomain *r;
+       struct regulatory_request *lr;
+
+       assert_reg_lock();
+
+       r = get_cfg80211_regdom();
+
        /* avoid freeing static information or freeing something twice */
-       if (cfg80211_regdomain == cfg80211_world_regdom)
-               cfg80211_regdomain = NULL;
+       if (r == cfg80211_world_regdom)
+               r = NULL;
        if (cfg80211_world_regdom == &world_regdom)
                cfg80211_world_regdom = NULL;
-       if (cfg80211_regdomain == &world_regdom)
-               cfg80211_regdomain = NULL;
+       if (r == &world_regdom)
+               r = NULL;
 
-       kfree(cfg80211_regdomain);
-       kfree(cfg80211_world_regdom);
+       rcu_free_regdom(r);
+       rcu_free_regdom(cfg80211_world_regdom);
 
        cfg80211_world_regdom = &world_regdom;
-       cfg80211_regdomain = NULL;
+       rcu_assign_pointer(cfg80211_regdomain, new_regdom);
 
        if (!full_reset)
                return;
 
-       if (last_request != &core_request_world)
-               kfree(last_request);
-       last_request = &core_request_world;
+       lr = get_last_request();
+       if (lr != &core_request_world && lr)
+               kfree_rcu(lr, rcu_head);
+       rcu_assign_pointer(last_request, &core_request_world);
 }
 
 /*
@@ -207,30 +248,29 @@ static void reset_regdomains(bool full_reset)
  */
 static void update_world_regdomain(const struct ieee80211_regdomain *rd)
 {
-       BUG_ON(!last_request);
+       struct regulatory_request *lr;
+
+       lr = get_last_request();
+
+       WARN_ON(!lr);
 
-       reset_regdomains(false);
+       reset_regdomains(false, rd);
 
        cfg80211_world_regdom = rd;
-       cfg80211_regdomain = rd;
 }
 
 bool is_world_regdom(const char *alpha2)
 {
        if (!alpha2)
                return false;
-       if (alpha2[0] == '0' && alpha2[1] == '0')
-               return true;
-       return false;
+       return alpha2[0] == '0' && alpha2[1] == '0';
 }
 
 static bool is_alpha2_set(const char *alpha2)
 {
        if (!alpha2)
                return false;
-       if (alpha2[0] != 0 && alpha2[1] != 0)
-               return true;
-       return false;
+       return alpha2[0] && alpha2[1];
 }
 
 static bool is_unknown_alpha2(const char *alpha2)
@@ -241,9 +281,7 @@ static bool is_unknown_alpha2(const char *alpha2)
         * Special case where regulatory domain was built by driver
         * but a specific alpha2 cannot be determined
         */
-       if (alpha2[0] == '9' && alpha2[1] == '9')
-               return true;
-       return false;
+       return alpha2[0] == '9' && alpha2[1] == '9';
 }
 
 static bool is_intersected_alpha2(const char *alpha2)
@@ -255,39 +293,30 @@ static bool is_intersected_alpha2(const char *alpha2)
         * result of an intersection between two regulatory domain
         * structures
         */
-       if (alpha2[0] == '9' && alpha2[1] == '8')
-               return true;
-       return false;
+       return alpha2[0] == '9' && alpha2[1] == '8';
 }
 
 static bool is_an_alpha2(const char *alpha2)
 {
        if (!alpha2)
                return false;
-       if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
-               return true;
-       return false;
+       return isalpha(alpha2[0]) && isalpha(alpha2[1]);
 }
 
 static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y)
 {
        if (!alpha2_x || !alpha2_y)
                return false;
-       if (alpha2_x[0] == alpha2_y[0] &&
-               alpha2_x[1] == alpha2_y[1])
-               return true;
-       return false;
+       return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1];
 }
 
 static bool regdom_changes(const char *alpha2)
 {
-       assert_cfg80211_lock();
+       const struct ieee80211_regdomain *r = get_cfg80211_regdom();
 
-       if (!cfg80211_regdomain)
+       if (!r)
                return true;
-       if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
-               return false;
-       return true;
+       return !alpha2_equal(r->alpha2, alpha2);
 }
 
 /*
@@ -301,38 +330,36 @@ static bool is_user_regdom_saved(void)
                return false;
 
        /* This would indicate a mistake on the design */
-       if (WARN((!is_world_regdom(user_alpha2) &&
-                 !is_an_alpha2(user_alpha2)),
+       if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2),
                 "Unexpected user alpha2: %c%c\n",
-                user_alpha2[0],
-                user_alpha2[1]))
+                user_alpha2[0], user_alpha2[1]))
                return false;
 
        return true;
 }
 
-static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,
-                        const struct ieee80211_regdomain *src_regd)
+static const struct ieee80211_regdomain *
+reg_copy_regd(const struct ieee80211_regdomain *src_regd)
 {
        struct ieee80211_regdomain *regd;
-       int size_of_regd = 0;
+       int size_of_regd;
        unsigned int i;
 
-       size_of_regd = sizeof(struct ieee80211_regdomain) +
-         ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule));
+       size_of_regd =
+               sizeof(struct ieee80211_regdomain) +
+               src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule);
 
        regd = kzalloc(size_of_regd, GFP_KERNEL);
        if (!regd)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));
 
        for (i = 0; i < src_regd->n_reg_rules; i++)
                memcpy(&regd->reg_rules[i], &src_regd->reg_rules[i],
-                       sizeof(struct ieee80211_reg_rule));
+                      sizeof(struct ieee80211_reg_rule));
 
-       *dst_regd = regd;
-       return 0;
+       return regd;
 }
 
 #ifdef CONFIG_CFG80211_INTERNAL_REGDB
@@ -347,9 +374,8 @@ static DEFINE_MUTEX(reg_regdb_search_mutex);
 static void reg_regdb_search(struct work_struct *work)
 {
        struct reg_regdb_search_request *request;
-       const struct ieee80211_regdomain *curdom, *regdom;
-       int i, r;
-       bool set_reg = false;
+       const struct ieee80211_regdomain *curdom, *regdom = NULL;
+       int i;
 
        mutex_lock(&cfg80211_mutex);
 
@@ -360,14 +386,11 @@ static void reg_regdb_search(struct work_struct *work)
                                           list);
                list_del(&request->list);
 
-               for (i=0; i<reg_regdb_size; i++) {
+               for (i = 0; i < reg_regdb_size; i++) {
                        curdom = reg_regdb[i];
 
-                       if (!memcmp(request->alpha2, curdom->alpha2, 2)) {
-                               r = reg_copy_regd(&regdom, curdom);
-                               if (r)
-                                       break;
-                               set_reg = true;
+                       if (alpha2_equal(request->alpha2, curdom->alpha2)) {
+                               regdom = reg_copy_regd(curdom);
                                break;
                        }
                }
@@ -376,7 +399,7 @@ static void reg_regdb_search(struct work_struct *work)
        }
        mutex_unlock(&reg_regdb_search_mutex);
 
-       if (set_reg)
+       if (!IS_ERR_OR_NULL(regdom))
                set_regdom(regdom);
 
        mutex_unlock(&cfg80211_mutex);
@@ -434,15 +457,14 @@ static int call_crda(const char *alpha2)
        return kobject_uevent(&reg_pdev->dev.kobj, KOBJ_CHANGE);
 }
 
-/* Used by nl80211 before kmalloc'ing our regulatory domain */
-bool reg_is_valid_request(const char *alpha2)
+static bool reg_is_valid_request(const char *alpha2)
 {
-       assert_cfg80211_lock();
+       struct regulatory_request *lr = get_last_request();
 
-       if (!last_request)
+       if (!lr || lr->processed)
                return false;
 
-       return alpha2_equal(last_request->alpha2, alpha2);
+       return alpha2_equal(lr->alpha2, alpha2);
 }
 
 /* Sanity check on a regulatory rule */
@@ -460,7 +482,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
        freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
 
        if (freq_range->end_freq_khz <= freq_range->start_freq_khz ||
-                       freq_range->max_bandwidth_khz > freq_diff)
+           freq_range->max_bandwidth_khz > freq_diff)
                return false;
 
        return true;
@@ -487,8 +509,7 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd)
 }
 
 static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
-                           u32 center_freq_khz,
-                           u32 bw_khz)
+                           u32 center_freq_khz, u32 bw_khz)
 {
        u32 start_freq_khz, end_freq_khz;
 
@@ -518,7 +539,7 @@ static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
  * regulatory rule support for other "bands".
  **/
 static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
-       u32 freq_khz)
+                             u32 freq_khz)
 {
 #define ONE_GHZ_IN_KHZ 1000000
        /*
@@ -540,10 +561,9 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
  * Helper for regdom_intersect(), this does the real
  * mathematical intersection fun
  */
-static int reg_rules_intersect(
-       const struct ieee80211_reg_rule *rule1,
-       const struct ieee80211_reg_rule *rule2,
-       struct ieee80211_reg_rule *intersected_rule)
+static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,
+                              const struct ieee80211_reg_rule *rule2,
+                              struct ieee80211_reg_rule *intersected_rule)
 {
        const struct ieee80211_freq_range *freq_range1, *freq_range2;
        struct ieee80211_freq_range *freq_range;
@@ -560,11 +580,11 @@ static int reg_rules_intersect(
        power_rule = &intersected_rule->power_rule;
 
        freq_range->start_freq_khz = max(freq_range1->start_freq_khz,
-               freq_range2->start_freq_khz);
+                                        freq_range2->start_freq_khz);
        freq_range->end_freq_khz = min(freq_range1->end_freq_khz,
-               freq_range2->end_freq_khz);
+                                      freq_range2->end_freq_khz);
        freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz,
-               freq_range2->max_bandwidth_khz);
+                                           freq_range2->max_bandwidth_khz);
 
        freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
        if (freq_range->max_bandwidth_khz > freq_diff)
@@ -575,7 +595,7 @@ static int reg_rules_intersect(
        power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain,
                power_rule2->max_antenna_gain);
 
-       intersected_rule->flags = (rule1->flags | rule2->flags);
+       intersected_rule->flags = rule1->flags | rule2->flags;
 
        if (!is_valid_reg_rule(intersected_rule))
                return -EINVAL;
@@ -596,9 +616,9 @@ static int reg_rules_intersect(
  * resulting intersection of rules between rd1 and rd2. We will
  * kzalloc() this structure for you.
  */
-static struct ieee80211_regdomain *regdom_intersect(
-       const struct ieee80211_regdomain *rd1,
-       const struct ieee80211_regdomain *rd2)
+static struct ieee80211_regdomain *
+regdom_intersect(const struct ieee80211_regdomain *rd1,
+                const struct ieee80211_regdomain *rd2)
 {
        int r, size_of_regd;
        unsigned int x, y;
@@ -607,12 +627,7 @@ static struct ieee80211_regdomain *regdom_intersect(
        struct ieee80211_reg_rule *intersected_rule;
        struct ieee80211_regdomain *rd;
        /* This is just a dummy holder to help us count */
-       struct ieee80211_reg_rule irule;
-
-       /* Uses the stack temporarily for counter arithmetic */
-       intersected_rule = &irule;
-
-       memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule));
+       struct ieee80211_reg_rule dummy_rule;
 
        if (!rd1 || !rd2)
                return NULL;
@@ -629,11 +644,8 @@ static struct ieee80211_regdomain *regdom_intersect(
                rule1 = &rd1->reg_rules[x];
                for (y = 0; y < rd2->n_reg_rules; y++) {
                        rule2 = &rd2->reg_rules[y];
-                       if (!reg_rules_intersect(rule1, rule2,
-                                       intersected_rule))
+                       if (!reg_rules_intersect(rule1, rule2, &dummy_rule))
                                num_rules++;
-                       memset(intersected_rule, 0,
-                                       sizeof(struct ieee80211_reg_rule));
                }
        }
 
@@ -641,15 +653,15 @@ static struct ieee80211_regdomain *regdom_intersect(
                return NULL;
 
        size_of_regd = sizeof(struct ieee80211_regdomain) +
-               ((num_rules + 1) * sizeof(struct ieee80211_reg_rule));
+                      num_rules * sizeof(struct ieee80211_reg_rule);
 
        rd = kzalloc(size_of_regd, GFP_KERNEL);
        if (!rd)
                return NULL;
 
-       for (x = 0; x < rd1->n_reg_rules; x++) {
+       for (x = 0; x < rd1->n_reg_rules && rule_idx < num_rules; x++) {
                rule1 = &rd1->reg_rules[x];
-               for (y = 0; y < rd2->n_reg_rules; y++) {
+               for (y = 0; y < rd2->n_reg_rules && rule_idx < num_rules; y++) {
                        rule2 = &rd2->reg_rules[y];
                        /*
                         * This time around instead of using the stack lets
@@ -657,8 +669,7 @@ static struct ieee80211_regdomain *regdom_intersect(
                         * a memcpy()
                         */
                        intersected_rule = &rd->reg_rules[rule_idx];
-                       r = reg_rules_intersect(rule1, rule2,
-                               intersected_rule);
+                       r = reg_rules_intersect(rule1, rule2, intersected_rule);
                        /*
                         * No need to memset here the intersected rule here as
                         * we're not using the stack anymore
@@ -699,34 +710,16 @@ static u32 map_regdom_flags(u32 rd_flags)
        return channel_flags;
 }
 
-static int freq_reg_info_regd(struct wiphy *wiphy,
-                             u32 center_freq,
-                             u32 desired_bw_khz,
-                             const struct ieee80211_reg_rule **reg_rule,
-                             const struct ieee80211_regdomain *custom_regd)
+static const struct ieee80211_reg_rule *
+freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
+                  const struct ieee80211_regdomain *regd)
 {
        int i;
        bool band_rule_found = false;
-       const struct ieee80211_regdomain *regd;
        bool bw_fits = false;
 
-       if (!desired_bw_khz)
-               desired_bw_khz = MHZ_TO_KHZ(20);
-
-       regd = custom_regd ? custom_regd : cfg80211_regdomain;
-
-       /*
-        * Follow the driver's regulatory domain, if present, unless a country
-        * IE has been processed or a user wants to help complaince further
-        */
-       if (!custom_regd &&
-           last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
-           last_request->initiator != NL80211_REGDOM_SET_BY_USER &&
-           wiphy->regd)
-               regd = wiphy->regd;
-
        if (!regd)
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
        for (i = 0; i < regd->n_reg_rules; i++) {
                const struct ieee80211_reg_rule *rr;
@@ -743,33 +736,36 @@ static int freq_reg_info_regd(struct wiphy *wiphy,
                if (!band_rule_found)
                        band_rule_found = freq_in_rule_band(fr, center_freq);
 
-               bw_fits = reg_does_bw_fit(fr,
-                                         center_freq,
-                                         desired_bw_khz);
+               bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20));
 
-               if (band_rule_found && bw_fits) {
-                       *reg_rule = rr;
-                       return 0;
-               }
+               if (band_rule_found && bw_fits)
+                       return rr;
        }
 
        if (!band_rule_found)
-               return -ERANGE;
+               return ERR_PTR(-ERANGE);
 
-       return -EINVAL;
+       return ERR_PTR(-EINVAL);
 }
 
-int freq_reg_info(struct wiphy *wiphy,
-                 u32 center_freq,
-                 u32 desired_bw_khz,
-                 const struct ieee80211_reg_rule **reg_rule)
+const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
+                                              u32 center_freq)
 {
-       assert_cfg80211_lock();
-       return freq_reg_info_regd(wiphy,
-                                 center_freq,
-                                 desired_bw_khz,
-                                 reg_rule,
-                                 NULL);
+       const struct ieee80211_regdomain *regd;
+       struct regulatory_request *lr = get_last_request();
+
+       /*
+        * Follow the driver's regulatory domain, if present, unless a country
+        * IE has been processed or a user wants to help complaince further
+        */
+       if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+           lr->initiator != NL80211_REGDOM_SET_BY_USER &&
+           wiphy->regd)
+               regd = get_wiphy_regdom(wiphy);
+       else
+               regd = get_cfg80211_regdom();
+
+       return freq_reg_info_regd(wiphy, center_freq, regd);
 }
 EXPORT_SYMBOL(freq_reg_info);
 
@@ -792,7 +788,6 @@ static const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
 }
 
 static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
-                                   u32 desired_bw_khz,
                                    const struct ieee80211_reg_rule *reg_rule)
 {
        const struct ieee80211_power_rule *power_rule;
@@ -807,21 +802,16 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
        else
                snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain);
 
-       REG_DBG_PRINT("Updating information on frequency %d MHz "
-                     "for a %d MHz width channel with regulatory rule:\n",
-                     chan->center_freq,
-                     KHZ_TO_MHZ(desired_bw_khz));
+       REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n",
+                     chan->center_freq);
 
        REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n",
-                     freq_range->start_freq_khz,
-                     freq_range->end_freq_khz,
-                     freq_range->max_bandwidth_khz,
-                     max_antenna_gain,
+                     freq_range->start_freq_khz, freq_range->end_freq_khz,
+                     freq_range->max_bandwidth_khz, max_antenna_gain,
                      power_rule->max_eirp);
 }
 #else
 static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
-                                   u32 desired_bw_khz,
                                    const struct ieee80211_reg_rule *reg_rule)
 {
        return;
@@ -831,43 +821,25 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
 /*
  * Note that right now we assume the desired channel bandwidth
  * is always 20 MHz for each individual channel (HT40 uses 20 MHz
- * per channel, the primary and the extension channel). To support
- * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a
- * new ieee80211_channel.target_bw and re run the regulatory check
- * on the wiphy with the target_bw specified. Then we can simply use
- * that below for the desired_bw_khz below.
+ * per channel, the primary and the extension channel).
  */
 static void handle_channel(struct wiphy *wiphy,
                           enum nl80211_reg_initiator initiator,
-                          enum ieee80211_band band,
-                          unsigned int chan_idx)
+                          struct ieee80211_channel *chan)
 {
-       int r;
        u32 flags, bw_flags = 0;
-       u32 desired_bw_khz = MHZ_TO_KHZ(20);
        const struct ieee80211_reg_rule *reg_rule = NULL;
        const struct ieee80211_power_rule *power_rule = NULL;
        const struct ieee80211_freq_range *freq_range = NULL;
-       struct ieee80211_supported_band *sband;
-       struct ieee80211_channel *chan;
        struct wiphy *request_wiphy = NULL;
+       struct regulatory_request *lr = get_last_request();
 
-       assert_cfg80211_lock();
-
-       request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
-
-       sband = wiphy->bands[band];
-       BUG_ON(chan_idx >= sband->n_channels);
-       chan = &sband->channels[chan_idx];
+       request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
 
        flags = chan->orig_flags;
 
-       r = freq_reg_info(wiphy,
-                         MHZ_TO_KHZ(chan->center_freq),
-                         desired_bw_khz,
-                         &reg_rule);
-
-       if (r) {
+       reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
+       if (IS_ERR(reg_rule)) {
                /*
                 * We will disable all channels that do not match our
                 * received regulatory rule unless the hint is coming
@@ -879,7 +851,7 @@ static void handle_channel(struct wiphy *wiphy,
                 * while 5 GHz is still supported.
                 */
                if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
-                   r == -ERANGE)
+                   PTR_ERR(reg_rule) == -ERANGE)
                        return;
 
                REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq);
@@ -887,7 +859,7 @@ static void handle_channel(struct wiphy *wiphy,
                return;
        }
 
-       chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule);
+       chan_reg_rule_print_dbg(chan, reg_rule);
 
        power_rule = &reg_rule->power_rule;
        freq_range = &reg_rule->freq_range;
@@ -895,7 +867,7 @@ static void handle_channel(struct wiphy *wiphy,
        if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
                bw_flags = IEEE80211_CHAN_NO_HT40;
 
-       if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+       if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
            request_wiphy && request_wiphy == wiphy &&
            request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
                /*
@@ -914,8 +886,9 @@ static void handle_channel(struct wiphy *wiphy,
 
        chan->beacon_found = false;
        chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
-       chan->max_antenna_gain = min(chan->orig_mag,
-               (int) MBI_TO_DBI(power_rule->max_antenna_gain));
+       chan->max_antenna_gain =
+               min_t(int, chan->orig_mag,
+                     MBI_TO_DBI(power_rule->max_antenna_gain));
        chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
        if (chan->orig_mpwr) {
                /*
@@ -935,68 +908,65 @@ static void handle_channel(struct wiphy *wiphy,
 }
 
 static void handle_band(struct wiphy *wiphy,
-                       enum ieee80211_band band,
-                       enum nl80211_reg_initiator initiator)
+                       enum nl80211_reg_initiator initiator,
+                       struct ieee80211_supported_band *sband)
 {
        unsigned int i;
-       struct ieee80211_supported_band *sband;
 
-       BUG_ON(!wiphy->bands[band]);
-       sband = wiphy->bands[band];
+       if (!sband)
+               return;
 
        for (i = 0; i < sband->n_channels; i++)
-               handle_channel(wiphy, initiator, band, i);
+               handle_channel(wiphy, initiator, &sband->channels[i]);
 }
 
 static bool reg_request_cell_base(struct regulatory_request *request)
 {
        if (request->initiator != NL80211_REGDOM_SET_BY_USER)
                return false;
-       if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE)
-               return false;
-       return true;
+       return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE;
 }
 
 bool reg_last_request_cell_base(void)
 {
        bool val;
-       assert_cfg80211_lock();
 
        mutex_lock(&reg_mutex);
-       val = reg_request_cell_base(last_request);
+       val = reg_request_cell_base(get_last_request());
        mutex_unlock(&reg_mutex);
+
        return val;
 }
 
 #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
-
 /* Core specific check */
-static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
+static enum reg_request_treatment
+reg_ignore_cell_hint(struct regulatory_request *pending_request)
 {
+       struct regulatory_request *lr = get_last_request();
+
        if (!reg_num_devs_support_basehint)
-               return -EOPNOTSUPP;
+               return REG_REQ_IGNORE;
 
-       if (reg_request_cell_base(last_request)) {
-               if (!regdom_changes(pending_request->alpha2))
-                       return -EALREADY;
-               return 0;
-       }
-       return 0;
+       if (reg_request_cell_base(lr) &&
+           !regdom_changes(pending_request->alpha2))
+               return REG_REQ_ALREADY_SET;
+
+       return REG_REQ_OK;
 }
 
 /* Device specific check */
 static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
 {
-       if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS))
-               return true;
-       return false;
+       return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS);
 }
 #else
 static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
 {
-       return -EOPNOTSUPP;
+       return REG_REQ_IGNORE;
 }
-static int reg_dev_ignore_cell_hint(struct wiphy *wiphy)
+
+static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
 {
        return true;
 }
@@ -1006,18 +976,17 @@ static int reg_dev_ignore_cell_hint(struct wiphy *wiphy)
 static bool ignore_reg_update(struct wiphy *wiphy,
                              enum nl80211_reg_initiator initiator)
 {
-       if (!last_request) {
-               REG_DBG_PRINT("Ignoring regulatory request %s since "
-                             "last_request is not set\n",
+       struct regulatory_request *lr = get_last_request();
+
+       if (!lr) {
+               REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n",
                              reg_initiator_name(initiator));
                return true;
        }
 
        if (initiator == NL80211_REGDOM_SET_BY_CORE &&
            wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
-               REG_DBG_PRINT("Ignoring regulatory request %s "
-                             "since the driver uses its own custom "
-                             "regulatory domain\n",
+               REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n",
                              reg_initiator_name(initiator));
                return true;
        }
@@ -1028,22 +997,35 @@ static bool ignore_reg_update(struct wiphy *wiphy,
         */
        if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd &&
            initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
-           !is_world_regdom(last_request->alpha2)) {
-               REG_DBG_PRINT("Ignoring regulatory request %s "
-                             "since the driver requires its own regulatory "
-                             "domain to be set first\n",
+           !is_world_regdom(lr->alpha2)) {
+               REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n",
                              reg_initiator_name(initiator));
                return true;
        }
 
-       if (reg_request_cell_base(last_request))
+       if (reg_request_cell_base(lr))
                return reg_dev_ignore_cell_hint(wiphy);
 
        return false;
 }
 
-static void handle_reg_beacon(struct wiphy *wiphy,
-                             unsigned int chan_idx,
+static bool reg_is_world_roaming(struct wiphy *wiphy)
+{
+       const struct ieee80211_regdomain *cr = get_cfg80211_regdom();
+       const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy);
+       struct regulatory_request *lr = get_last_request();
+
+       if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2)))
+               return true;
+
+       if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+           wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)
+               return true;
+
+       return false;
+}
+
+static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,
                              struct reg_beacon *reg_beacon)
 {
        struct ieee80211_supported_band *sband;
@@ -1051,8 +1033,6 @@ static void handle_reg_beacon(struct wiphy *wiphy,
        bool channel_changed = false;
        struct ieee80211_channel chan_before;
 
-       assert_cfg80211_lock();
-
        sband = wiphy->bands[reg_beacon->chan.band];
        chan = &sband->channels[chan_idx];
 
@@ -1064,6 +1044,9 @@ static void handle_reg_beacon(struct wiphy *wiphy,
 
        chan->beacon_found = true;
 
+       if (!reg_is_world_roaming(wiphy))
+               return;
+
        if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS)
                return;
 
@@ -1094,8 +1077,6 @@ static void wiphy_update_new_beacon(struct wiphy *wiphy,
        unsigned int i;
        struct ieee80211_supported_band *sband;
 
-       assert_cfg80211_lock();
-
        if (!wiphy->bands[reg_beacon->chan.band])
                return;
 
@@ -1114,11 +1095,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy)
        struct ieee80211_supported_band *sband;
        struct reg_beacon *reg_beacon;
 
-       assert_cfg80211_lock();
-
-       if (list_empty(&reg_beacon_list))
-               return;
-
        list_for_each_entry(reg_beacon, &reg_beacon_list, list) {
                if (!wiphy->bands[reg_beacon->chan.band])
                        continue;
@@ -1128,18 +1104,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy)
        }
 }
 
-static bool reg_is_world_roaming(struct wiphy *wiphy)
-{
-       if (is_world_regdom(cfg80211_regdomain->alpha2) ||
-           (wiphy->regd && is_world_regdom(wiphy->regd->alpha2)))
-               return true;
-       if (last_request &&
-           last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
-           wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)
-               return true;
-       return false;
-}
-
 /* Reap the advantages of previously found beacons */
 static void reg_process_beacons(struct wiphy *wiphy)
 {
@@ -1149,39 +1113,29 @@ static void reg_process_beacons(struct wiphy *wiphy)
         */
        if (!last_request)
                return;
-       if (!reg_is_world_roaming(wiphy))
-               return;
        wiphy_update_beacon_reg(wiphy);
 }
 
-static bool is_ht40_not_allowed(struct ieee80211_channel *chan)
+static bool is_ht40_allowed(struct ieee80211_channel *chan)
 {
        if (!chan)
-               return true;
+               return false;
        if (chan->flags & IEEE80211_CHAN_DISABLED)
-               return true;
+               return false;
        /* This would happen when regulatory rules disallow HT40 completely */
-       if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40)))
-               return true;
-       return false;
+       if ((chan->flags & IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40)
+               return false;
+       return true;
 }
 
 static void reg_process_ht_flags_channel(struct wiphy *wiphy,
-                                        enum ieee80211_band band,
-                                        unsigned int chan_idx)
+                                        struct ieee80211_channel *channel)
 {
-       struct ieee80211_supported_band *sband;
-       struct ieee80211_channel *channel;
+       struct ieee80211_supported_band *sband = wiphy->bands[channel->band];
        struct ieee80211_channel *channel_before = NULL, *channel_after = NULL;
        unsigned int i;
 
-       assert_cfg80211_lock();
-
-       sband = wiphy->bands[band];
-       BUG_ON(chan_idx >= sband->n_channels);
-       channel = &sband->channels[chan_idx];
-
-       if (is_ht40_not_allowed(channel)) {
+       if (!is_ht40_allowed(channel)) {
                channel->flags |= IEEE80211_CHAN_NO_HT40;
                return;
        }
@@ -1192,6 +1146,7 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy,
         */
        for (i = 0; i < sband->n_channels; i++) {
                struct ieee80211_channel *c = &sband->channels[i];
+
                if (c->center_freq == (channel->center_freq - 20))
                        channel_before = c;
                if (c->center_freq == (channel->center_freq + 20))
@@ -1203,28 +1158,27 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy,
         * if that ever changes we also need to change the below logic
         * to include that as well.
         */
-       if (is_ht40_not_allowed(channel_before))
+       if (!is_ht40_allowed(channel_before))
                channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
        else
                channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
 
-       if (is_ht40_not_allowed(channel_after))
+       if (!is_ht40_allowed(channel_after))
                channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
        else
                channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
 }
 
 static void reg_process_ht_flags_band(struct wiphy *wiphy,
-                                     enum ieee80211_band band)
+                                     struct ieee80211_supported_band *sband)
 {
        unsigned int i;
-       struct ieee80211_supported_band *sband;
 
-       BUG_ON(!wiphy->bands[band]);
-       sband = wiphy->bands[band];
+       if (!sband)
+               return;
 
        for (i = 0; i < sband->n_channels; i++)
-               reg_process_ht_flags_channel(wiphy, band, i);
+               reg_process_ht_flags_channel(wiphy, &sband->channels[i]);
 }
 
 static void reg_process_ht_flags(struct wiphy *wiphy)
@@ -1234,34 +1188,29 @@ static void reg_process_ht_flags(struct wiphy *wiphy)
        if (!wiphy)
                return;
 
-       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
-               if (wiphy->bands[band])
-                       reg_process_ht_flags_band(wiphy, band);
-       }
-
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+               reg_process_ht_flags_band(wiphy, wiphy->bands[band]);
 }
 
 static void wiphy_update_regulatory(struct wiphy *wiphy,
                                    enum nl80211_reg_initiator initiator)
 {
        enum ieee80211_band band;
-
-       assert_reg_lock();
+       struct regulatory_request *lr = get_last_request();
 
        if (ignore_reg_update(wiphy, initiator))
                return;
 
-       last_request->dfs_region = cfg80211_regdomain->dfs_region;
+       lr->dfs_region = get_cfg80211_regdom()->dfs_region;
 
-       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
-               if (wiphy->bands[band])
-                       handle_band(wiphy, band, initiator);
-       }
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+               handle_band(wiphy, initiator, wiphy->bands[band]);
 
        reg_process_beacons(wiphy);
        reg_process_ht_flags(wiphy);
+
        if (wiphy->reg_notifier)
-               wiphy->reg_notifier(wiphy, last_request);
+               wiphy->reg_notifier(wiphy, lr);
 }
 
 static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
@@ -1269,6 +1218,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
        struct cfg80211_registered_device *rdev;
        struct wiphy *wiphy;
 
+       assert_cfg80211_lock();
+
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                wiphy = &rdev->wiphy;
                wiphy_update_regulatory(wiphy, initiator);
@@ -1280,47 +1231,30 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
                if (initiator == NL80211_REGDOM_SET_BY_CORE &&
                    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
                    wiphy->reg_notifier)
-                       wiphy->reg_notifier(wiphy, last_request);
+                       wiphy->reg_notifier(wiphy, get_last_request());
        }
 }
 
 static void handle_channel_custom(struct wiphy *wiphy,
-                                 enum ieee80211_band band,
-                                 unsigned int chan_idx,
+                                 struct ieee80211_channel *chan,
                                  const struct ieee80211_regdomain *regd)
 {
-       int r;
-       u32 desired_bw_khz = MHZ_TO_KHZ(20);
        u32 bw_flags = 0;
        const struct ieee80211_reg_rule *reg_rule = NULL;
        const struct ieee80211_power_rule *power_rule = NULL;
        const struct ieee80211_freq_range *freq_range = NULL;
-       struct ieee80211_supported_band *sband;
-       struct ieee80211_channel *chan;
-
-       assert_reg_lock();
 
-       sband = wiphy->bands[band];
-       BUG_ON(chan_idx >= sband->n_channels);
-       chan = &sband->channels[chan_idx];
-
-       r = freq_reg_info_regd(wiphy,
-                              MHZ_TO_KHZ(chan->center_freq),
-                              desired_bw_khz,
-                              &reg_rule,
-                              regd);
+       reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
+                                     regd);
 
-       if (r) {
-               REG_DBG_PRINT("Disabling freq %d MHz as custom "
-                             "regd has no rule that fits a %d MHz "
-                             "wide channel\n",
-                             chan->center_freq,
-                             KHZ_TO_MHZ(desired_bw_khz));
+       if (IS_ERR(reg_rule)) {
+               REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
+                             chan->center_freq);
                chan->flags = IEEE80211_CHAN_DISABLED;
                return;
        }
 
-       chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule);
+       chan_reg_rule_print_dbg(chan, reg_rule);
 
        power_rule = &reg_rule->power_rule;
        freq_range = &reg_rule->freq_range;
@@ -1334,17 +1268,17 @@ static void handle_channel_custom(struct wiphy *wiphy,
                (int) MBM_TO_DBM(power_rule->max_eirp);
 }
 
-static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band,
+static void handle_band_custom(struct wiphy *wiphy,
+                              struct ieee80211_supported_band *sband,
                               const struct ieee80211_regdomain *regd)
 {
        unsigned int i;
-       struct ieee80211_supported_band *sband;
 
-       BUG_ON(!wiphy->bands[band]);
-       sband = wiphy->bands[band];
+       if (!sband)
+               return;
 
        for (i = 0; i < sband->n_channels; i++)
-               handle_channel_custom(wiphy, band, i, regd);
+               handle_channel_custom(wiphy, &sband->channels[i], regd);
 }
 
 /* Used by drivers prior to wiphy registration */
@@ -1354,60 +1288,50 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
        enum ieee80211_band band;
        unsigned int bands_set = 0;
 
-       mutex_lock(&reg_mutex);
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                if (!wiphy->bands[band])
                        continue;
-               handle_band_custom(wiphy, band, regd);
+               handle_band_custom(wiphy, wiphy->bands[band], regd);
                bands_set++;
        }
-       mutex_unlock(&reg_mutex);
 
        /*
         * no point in calling this if it won't have any effect
-        * on your device's supportd bands.
+        * on your device's supported bands.
         */
        WARN_ON(!bands_set);
 }
 EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
 
-/*
- * Return value which can be used by ignore_request() to indicate
- * it has been determined we should intersect two regulatory domains
- */
-#define REG_INTERSECT  1
-
 /* This has the logic which determines when a new request
  * should be ignored. */
-static int ignore_request(struct wiphy *wiphy,
+static enum reg_request_treatment
+get_reg_request_treatment(struct wiphy *wiphy,
                          struct regulatory_request *pending_request)
 {
        struct wiphy *last_wiphy = NULL;
-
-       assert_cfg80211_lock();
+       struct regulatory_request *lr = get_last_request();
 
        /* All initial requests are respected */
-       if (!last_request)
-               return 0;
+       if (!lr)
+               return REG_REQ_OK;
 
        switch (pending_request->initiator) {
        case NL80211_REGDOM_SET_BY_CORE:
-               return 0;
+               return REG_REQ_OK;
        case NL80211_REGDOM_SET_BY_COUNTRY_IE:
-
-               if (reg_request_cell_base(last_request)) {
+               if (reg_request_cell_base(lr)) {
                        /* Trust a Cell base station over the AP's country IE */
                        if (regdom_changes(pending_request->alpha2))
-                               return -EOPNOTSUPP;
-                       return -EALREADY;
+                               return REG_REQ_IGNORE;
+                       return REG_REQ_ALREADY_SET;
                }
 
-               last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
+               last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
 
                if (unlikely(!is_an_alpha2(pending_request->alpha2)))
                        return -EINVAL;
-               if (last_request->initiator ==
-                   NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+               if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
                        if (last_wiphy != wiphy) {
                                /*
                                 * Two cards with two APs claiming different
@@ -1416,23 +1340,23 @@ static int ignore_request(struct wiphy *wiphy,
                                 * to be correct. Reject second one for now.
                                 */
                                if (regdom_changes(pending_request->alpha2))
-                                       return -EOPNOTSUPP;
-                               return -EALREADY;
+                                       return REG_REQ_IGNORE;
+                               return REG_REQ_ALREADY_SET;
                        }
                        /*
                         * Two consecutive Country IE hints on the same wiphy.
                         * This should be picked up early by the driver/stack
                         */
                        if (WARN_ON(regdom_changes(pending_request->alpha2)))
-                               return 0;
-                       return -EALREADY;
+                               return REG_REQ_OK;
+                       return REG_REQ_ALREADY_SET;
                }
                return 0;
        case NL80211_REGDOM_SET_BY_DRIVER:
-               if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) {
+               if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
                        if (regdom_changes(pending_request->alpha2))
-                               return 0;
-                       return -EALREADY;
+                               return REG_REQ_OK;
+                       return REG_REQ_ALREADY_SET;
                }
 
                /*
@@ -1440,59 +1364,59 @@ static int ignore_request(struct wiphy *wiphy,
                 * back in or if you add a new device for which the previously
                 * loaded card also agrees on the regulatory domain.
                 */
-               if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+               if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
                    !regdom_changes(pending_request->alpha2))
-                       return -EALREADY;
+                       return REG_REQ_ALREADY_SET;
 
-               return REG_INTERSECT;
+               return REG_REQ_INTERSECT;
        case NL80211_REGDOM_SET_BY_USER:
                if (reg_request_cell_base(pending_request))
                        return reg_ignore_cell_hint(pending_request);
 
-               if (reg_request_cell_base(last_request))
-                       return -EOPNOTSUPP;
+               if (reg_request_cell_base(lr))
+                       return REG_REQ_IGNORE;
 
-               if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
-                       return REG_INTERSECT;
+               if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
+                       return REG_REQ_INTERSECT;
                /*
                 * If the user knows better the user should set the regdom
                 * to their country before the IE is picked up
                 */
-               if (last_request->initiator == NL80211_REGDOM_SET_BY_USER &&
-                         last_request->intersect)
-                       return -EOPNOTSUPP;
+               if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
+                   lr->intersect)
+                       return REG_REQ_IGNORE;
                /*
                 * Process user requests only after previous user/driver/core
                 * requests have been processed
                 */
-               if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE ||
-                   last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
-                   last_request->initiator == NL80211_REGDOM_SET_BY_USER) {
-                       if (regdom_changes(last_request->alpha2))
-                               return -EAGAIN;
-               }
+               if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE ||
+                    lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
+                    lr->initiator == NL80211_REGDOM_SET_BY_USER) &&
+                   regdom_changes(lr->alpha2))
+                       return REG_REQ_IGNORE;
 
                if (!regdom_changes(pending_request->alpha2))
-                       return -EALREADY;
+                       return REG_REQ_ALREADY_SET;
 
-               return 0;
+               return REG_REQ_OK;
        }
 
-       return -EINVAL;
+       return REG_REQ_IGNORE;
 }
 
 static void reg_set_request_processed(void)
 {
        bool need_more_processing = false;
+       struct regulatory_request *lr = get_last_request();
 
-       last_request->processed = true;
+       lr->processed = true;
 
        spin_lock(&reg_requests_lock);
        if (!list_empty(&reg_requests_list))
                need_more_processing = true;
        spin_unlock(&reg_requests_lock);
 
-       if (last_request->initiator == NL80211_REGDOM_SET_BY_USER)
+       if (lr->initiator == NL80211_REGDOM_SET_BY_USER)
                cancel_delayed_work(&reg_timeout);
 
        if (need_more_processing)
@@ -1508,116 +1432,122 @@ static void reg_set_request_processed(void)
  * The Wireless subsystem can use this function to hint to the wireless core
  * what it believes should be the current regulatory domain.
  *
- * Returns zero if all went fine, %-EALREADY if a regulatory domain had
- * already been set or other standard error codes.
+ * Returns one of the different reg request treatment values.
  *
- * Caller must hold &cfg80211_mutex and &reg_mutex
+ * Caller must hold &reg_mutex
  */
-static int __regulatory_hint(struct wiphy *wiphy,
-                            struct regulatory_request *pending_request)
+static enum reg_request_treatment
+__regulatory_hint(struct wiphy *wiphy,
+                 struct regulatory_request *pending_request)
 {
+       const struct ieee80211_regdomain *regd;
        bool intersect = false;
-       int r = 0;
-
-       assert_cfg80211_lock();
+       enum reg_request_treatment treatment;
+       struct regulatory_request *lr;
 
-       r = ignore_request(wiphy, pending_request);
+       treatment = get_reg_request_treatment(wiphy, pending_request);
 
-       if (r == REG_INTERSECT) {
+       switch (treatment) {
+       case REG_REQ_INTERSECT:
                if (pending_request->initiator ==
                    NL80211_REGDOM_SET_BY_DRIVER) {
-                       r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain);
-                       if (r) {
+                       regd = reg_copy_regd(get_cfg80211_regdom());
+                       if (IS_ERR(regd)) {
                                kfree(pending_request);
-                               return r;
+                               return PTR_ERR(regd);
                        }
+                       rcu_assign_pointer(wiphy->regd, regd);
                }
                intersect = true;
-       } else if (r) {
+               break;
+       case REG_REQ_OK:
+               break;
+       default:
                /*
                 * If the regulatory domain being requested by the
                 * driver has already been set just copy it to the
                 * wiphy
                 */
-               if (r == -EALREADY &&
-                   pending_request->initiator ==
-                   NL80211_REGDOM_SET_BY_DRIVER) {
-                       r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain);
-                       if (r) {
+               if (treatment == REG_REQ_ALREADY_SET &&
+                   pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
+                       regd = reg_copy_regd(get_cfg80211_regdom());
+                       if (IS_ERR(regd)) {
                                kfree(pending_request);
-                               return r;
+                               return REG_REQ_IGNORE;
                        }
-                       r = -EALREADY;
+                       treatment = REG_REQ_ALREADY_SET;
+                       rcu_assign_pointer(wiphy->regd, regd);
                        goto new_request;
                }
                kfree(pending_request);
-               return r;
+               return treatment;
        }
 
 new_request:
-       if (last_request != &core_request_world)
-               kfree(last_request);
+       lr = get_last_request();
+       if (lr != &core_request_world && lr)
+               kfree_rcu(lr, rcu_head);
 
-       last_request = pending_request;
-       last_request->intersect = intersect;
+       pending_request->intersect = intersect;
+       pending_request->processed = false;
+       rcu_assign_pointer(last_request, pending_request);
+       lr = pending_request;
 
        pending_request = NULL;
 
-       if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) {
-               user_alpha2[0] = last_request->alpha2[0];
-               user_alpha2[1] = last_request->alpha2[1];
+       if (lr->initiator == NL80211_REGDOM_SET_BY_USER) {
+               user_alpha2[0] = lr->alpha2[0];
+               user_alpha2[1] = lr->alpha2[1];
        }
 
-       /* When r == REG_INTERSECT we do need to call CRDA */
-       if (r < 0) {
+       /* When r == REG_REQ_INTERSECT we do need to call CRDA */
+       if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) {
                /*
                 * Since CRDA will not be called in this case as we already
                 * have applied the requested regulatory domain before we just
                 * inform userspace we have processed the request
                 */
-               if (r == -EALREADY) {
-                       nl80211_send_reg_change_event(last_request);
+               if (treatment == REG_REQ_ALREADY_SET) {
+                       nl80211_send_reg_change_event(lr);
                        reg_set_request_processed();
                }
-               return r;
+               return treatment;
        }
 
-       return call_crda(last_request->alpha2);
+       if (call_crda(lr->alpha2))
+               return REG_REQ_IGNORE;
+       return REG_REQ_OK;
 }
 
 /* This processes *all* regulatory hints */
 static void reg_process_hint(struct regulatory_request *reg_request,
                             enum nl80211_reg_initiator reg_initiator)
 {
-       int r = 0;
        struct wiphy *wiphy = NULL;
 
-       BUG_ON(!reg_request->alpha2);
+       if (WARN_ON(!reg_request->alpha2))
+               return;
 
-       if (wiphy_idx_valid(reg_request->wiphy_idx))
+       if (reg_request->wiphy_idx != WIPHY_IDX_INVALID)
                wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
 
-       if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER &&
-           !wiphy) {
+       if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) {
                kfree(reg_request);
                return;
        }
 
-       r = __regulatory_hint(wiphy, reg_request);
-       /* This is required so that the orig_* parameters are saved */
-       if (r == -EALREADY && wiphy &&
-           wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
-               wiphy_update_regulatory(wiphy, reg_initiator);
-               return;
+       switch (__regulatory_hint(wiphy, reg_request)) {
+       case REG_REQ_ALREADY_SET:
+               /* This is required so that the orig_* parameters are saved */
+               if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+                       wiphy_update_regulatory(wiphy, reg_initiator);
+               break;
+       default:
+               if (reg_initiator == NL80211_REGDOM_SET_BY_USER)
+                       schedule_delayed_work(&reg_timeout,
+                                             msecs_to_jiffies(3142));
+               break;
        }
-
-       /*
-        * We only time out user hints, given that they should be the only
-        * source of bogus requests.
-        */
-       if (r != -EALREADY &&
-           reg_initiator == NL80211_REGDOM_SET_BY_USER)
-               schedule_delayed_work(&reg_timeout, msecs_to_jiffies(3142));
 }
 
 /*
@@ -1627,15 +1557,15 @@ static void reg_process_hint(struct regulatory_request *reg_request,
  */
 static void reg_process_pending_hints(void)
 {
-       struct regulatory_request *reg_request;
+       struct regulatory_request *reg_request, *lr;
 
        mutex_lock(&cfg80211_mutex);
        mutex_lock(&reg_mutex);
+       lr = get_last_request();
 
        /* When last_request->processed becomes true this will be rescheduled */
-       if (last_request && !last_request->processed) {
-               REG_DBG_PRINT("Pending regulatory request, waiting "
-                             "for it to be processed...\n");
+       if (lr && !lr->processed) {
+               REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n");
                goto out;
        }
 
@@ -1666,23 +1596,14 @@ static void reg_process_pending_beacon_hints(void)
        struct cfg80211_registered_device *rdev;
        struct reg_beacon *pending_beacon, *tmp;
 
-       /*
-        * No need to hold the reg_mutex here as we just touch wiphys
-        * and do not read or access regulatory variables.
-        */
        mutex_lock(&cfg80211_mutex);
+       mutex_lock(&reg_mutex);
 
        /* This goes through the _pending_ beacon list */
        spin_lock_bh(&reg_pending_beacons_lock);
 
-       if (list_empty(&reg_pending_beacons)) {
-               spin_unlock_bh(&reg_pending_beacons_lock);
-               goto out;
-       }
-
        list_for_each_entry_safe(pending_beacon, tmp,
                                 &reg_pending_beacons, list) {
-
                list_del_init(&pending_beacon->list);
 
                /* Applies the beacon hint to current wiphys */
@@ -1694,7 +1615,7 @@ static void reg_process_pending_beacon_hints(void)
        }
 
        spin_unlock_bh(&reg_pending_beacons_lock);
-out:
+       mutex_unlock(&reg_mutex);
        mutex_unlock(&cfg80211_mutex);
 }
 
@@ -1706,10 +1627,8 @@ static void reg_todo(struct work_struct *work)
 
 static void queue_regulatory_request(struct regulatory_request *request)
 {
-       if (isalpha(request->alpha2[0]))
-               request->alpha2[0] = toupper(request->alpha2[0]);
-       if (isalpha(request->alpha2[1]))
-               request->alpha2[1] = toupper(request->alpha2[1]);
+       request->alpha2[0] = toupper(request->alpha2[0]);
+       request->alpha2[1] = toupper(request->alpha2[1]);
 
        spin_lock(&reg_requests_lock);
        list_add_tail(&request->list, &reg_requests_list);
@@ -1726,8 +1645,7 @@ static int regulatory_hint_core(const char *alpha2)
 {
        struct regulatory_request *request;
 
-       request = kzalloc(sizeof(struct regulatory_request),
-                         GFP_KERNEL);
+       request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
        if (!request)
                return -ENOMEM;
 
@@ -1746,13 +1664,14 @@ int regulatory_hint_user(const char *alpha2,
 {
        struct regulatory_request *request;
 
-       BUG_ON(!alpha2);
+       if (WARN_ON(!alpha2))
+               return -EINVAL;
 
        request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
        if (!request)
                return -ENOMEM;
 
-       request->wiphy_idx = WIPHY_IDX_STALE;
+       request->wiphy_idx = WIPHY_IDX_INVALID;
        request->alpha2[0] = alpha2[0];
        request->alpha2[1] = alpha2[1];
        request->initiator = NL80211_REGDOM_SET_BY_USER;
@@ -1768,8 +1687,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 {
        struct regulatory_request *request;
 
-       BUG_ON(!alpha2);
-       BUG_ON(!wiphy);
+       if (WARN_ON(!alpha2 || !wiphy))
+               return -EINVAL;
 
        request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
        if (!request)
@@ -1777,9 +1696,6 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 
        request->wiphy_idx = get_wiphy_idx(wiphy);
 
-       /* Must have registered wiphy first */
-       BUG_ON(!wiphy_idx_valid(request->wiphy_idx));
-
        request->alpha2[0] = alpha2[0];
        request->alpha2[1] = alpha2[1];
        request->initiator = NL80211_REGDOM_SET_BY_DRIVER;
@@ -1794,18 +1710,17 @@ EXPORT_SYMBOL(regulatory_hint);
  * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and
  * therefore cannot iterate over the rdev list here.
  */
-void regulatory_hint_11d(struct wiphy *wiphy,
-                        enum ieee80211_band band,
-                        const u8 *country_ie,
-                        u8 country_ie_len)
+void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
+                        const u8 *country_ie, u8 country_ie_len)
 {
        char alpha2[2];
        enum environment_cap env = ENVIRON_ANY;
-       struct regulatory_request *request;
+       struct regulatory_request *request, *lr;
 
        mutex_lock(&reg_mutex);
+       lr = get_last_request();
 
-       if (unlikely(!last_request))
+       if (unlikely(!lr))
                goto out;
 
        /* IE len must be evenly divisible by 2 */
@@ -1828,9 +1743,8 @@ void regulatory_hint_11d(struct wiphy *wiphy,
         * We leave conflict resolution to the workqueue, where can hold
         * cfg80211_mutex.
         */
-       if (likely(last_request->initiator ==
-           NL80211_REGDOM_SET_BY_COUNTRY_IE &&
-           wiphy_idx_valid(last_request->wiphy_idx)))
+       if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+           lr->wiphy_idx != WIPHY_IDX_INVALID)
                goto out;
 
        request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
@@ -1843,12 +1757,7 @@ void regulatory_hint_11d(struct wiphy *wiphy,
        request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE;
        request->country_ie_env = env;
 
-       mutex_unlock(&reg_mutex);
-
        queue_regulatory_request(request);
-
-       return;
-
 out:
        mutex_unlock(&reg_mutex);
 }
@@ -1863,8 +1772,7 @@ static void restore_alpha2(char *alpha2, bool reset_user)
        if (is_user_regdom_saved()) {
                /* Unless we're asked to ignore it and reset it */
                if (reset_user) {
-                       REG_DBG_PRINT("Restoring regulatory settings "
-                              "including user preference\n");
+                       REG_DBG_PRINT("Restoring regulatory settings including user preference\n");
                        user_alpha2[0] = '9';
                        user_alpha2[1] = '7';
 
@@ -1874,26 +1782,20 @@ static void restore_alpha2(char *alpha2, bool reset_user)
                         * back as they were for a full restore.
                         */
                        if (!is_world_regdom(ieee80211_regdom)) {
-                               REG_DBG_PRINT("Keeping preference on "
-                                      "module parameter ieee80211_regdom: %c%c\n",
-                                      ieee80211_regdom[0],
-                                      ieee80211_regdom[1]);
+                               REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
+                                             ieee80211_regdom[0], ieee80211_regdom[1]);
                                alpha2[0] = ieee80211_regdom[0];
                                alpha2[1] = ieee80211_regdom[1];
                        }
                } else {
-                       REG_DBG_PRINT("Restoring regulatory settings "
-                              "while preserving user preference for: %c%c\n",
-                              user_alpha2[0],
-                              user_alpha2[1]);
+                       REG_DBG_PRINT("Restoring regulatory settings while preserving user preference for: %c%c\n",
+                                     user_alpha2[0], user_alpha2[1]);
                        alpha2[0] = user_alpha2[0];
                        alpha2[1] = user_alpha2[1];
                }
        } else if (!is_world_regdom(ieee80211_regdom)) {
-               REG_DBG_PRINT("Keeping preference on "
-                      "module parameter ieee80211_regdom: %c%c\n",
-                      ieee80211_regdom[0],
-                      ieee80211_regdom[1]);
+               REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
+                             ieee80211_regdom[0], ieee80211_regdom[1]);
                alpha2[0] = ieee80211_regdom[0];
                alpha2[1] = ieee80211_regdom[1];
        } else
@@ -1948,7 +1850,7 @@ static void restore_regulatory_settings(bool reset_user)
        mutex_lock(&cfg80211_mutex);
        mutex_lock(&reg_mutex);
 
-       reset_regdomains(true);
+       reset_regdomains(true, &world_regdom);
        restore_alpha2(alpha2, reset_user);
 
        /*
@@ -1958,49 +1860,35 @@ static void restore_regulatory_settings(bool reset_user)
         * settings.
         */
        spin_lock(&reg_requests_lock);
-       if (!list_empty(&reg_requests_list)) {
-               list_for_each_entry_safe(reg_request, tmp,
-                                        &reg_requests_list, list) {
-                       if (reg_request->initiator !=
-                           NL80211_REGDOM_SET_BY_USER)
-                               continue;
-                       list_move_tail(&reg_request->list, &tmp_reg_req_list);
-               }
+       list_for_each_entry_safe(reg_request, tmp, &reg_requests_list, list) {
+               if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER)
+                       continue;
+               list_move_tail(&reg_request->list, &tmp_reg_req_list);
        }
        spin_unlock(&reg_requests_lock);
 
        /* Clear beacon hints */
        spin_lock_bh(&reg_pending_beacons_lock);
-       if (!list_empty(&reg_pending_beacons)) {
-               list_for_each_entry_safe(reg_beacon, btmp,
-                                        &reg_pending_beacons, list) {
-                       list_del(&reg_beacon->list);
-                       kfree(reg_beacon);
-               }
+       list_for_each_entry_safe(reg_beacon, btmp, &reg_pending_beacons, list) {
+               list_del(&reg_beacon->list);
+               kfree(reg_beacon);
        }
        spin_unlock_bh(&reg_pending_beacons_lock);
 
-       if (!list_empty(&reg_beacon_list)) {
-               list_for_each_entry_safe(reg_beacon, btmp,
-                                        &reg_beacon_list, list) {
-                       list_del(&reg_beacon->list);
-                       kfree(reg_beacon);
-               }
+       list_for_each_entry_safe(reg_beacon, btmp, &reg_beacon_list, list) {
+               list_del(&reg_beacon->list);
+               kfree(reg_beacon);
        }
 
        /* First restore to the basic regulatory settings */
-       cfg80211_regdomain = cfg80211_world_regdom;
-       world_alpha2[0] = cfg80211_regdomain->alpha2[0];
-       world_alpha2[1] = cfg80211_regdomain->alpha2[1];
+       world_alpha2[0] = cfg80211_world_regdom->alpha2[0];
+       world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
 
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY)
                        restore_custom_reg_settings(&rdev->wiphy);
        }
 
-       mutex_unlock(&reg_mutex);
-       mutex_unlock(&cfg80211_mutex);
-
        regulatory_hint_core(world_alpha2);
 
        /*
@@ -2011,20 +1899,8 @@ static void restore_regulatory_settings(bool reset_user)
        if (is_an_alpha2(alpha2))
                regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER);
 
-       if (list_empty(&tmp_reg_req_list))
-               return;
-
-       mutex_lock(&cfg80211_mutex);
-       mutex_lock(&reg_mutex);
-
        spin_lock(&reg_requests_lock);
-       list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) {
-               REG_DBG_PRINT("Adding request for country %c%c back "
-                             "into the queue\n",
-                             reg_request->alpha2[0],
-                             reg_request->alpha2[1]);
-               list_move_tail(&reg_request->list, &reg_requests_list);
-       }
+       list_splice_tail_init(&tmp_reg_req_list, &reg_requests_list);
        spin_unlock(&reg_requests_lock);
 
        mutex_unlock(&reg_mutex);
@@ -2037,8 +1913,7 @@ static void restore_regulatory_settings(bool reset_user)
 
 void regulatory_hint_disconnect(void)
 {
-       REG_DBG_PRINT("All devices are disconnected, going to "
-                     "restore regulatory settings\n");
+       REG_DBG_PRINT("All devices are disconnected, going to restore regulatory settings\n");
        restore_regulatory_settings(false);
 }
 
@@ -2051,31 +1926,48 @@ static bool freq_is_chan_12_13_14(u16 freq)
        return false;
 }
 
+static bool pending_reg_beacon(struct ieee80211_channel *beacon_chan)
+{
+       struct reg_beacon *pending_beacon;
+
+       list_for_each_entry(pending_beacon, &reg_pending_beacons, list)
+               if (beacon_chan->center_freq ==
+                   pending_beacon->chan.center_freq)
+                       return true;
+       return false;
+}
+
 int regulatory_hint_found_beacon(struct wiphy *wiphy,
                                 struct ieee80211_channel *beacon_chan,
                                 gfp_t gfp)
 {
        struct reg_beacon *reg_beacon;
+       bool processing;
 
-       if (likely((beacon_chan->beacon_found ||
-           (beacon_chan->flags & IEEE80211_CHAN_RADAR) ||
+       if (beacon_chan->beacon_found ||
+           beacon_chan->flags & IEEE80211_CHAN_RADAR ||
            (beacon_chan->band == IEEE80211_BAND_2GHZ &&
-            !freq_is_chan_12_13_14(beacon_chan->center_freq)))))
+            !freq_is_chan_12_13_14(beacon_chan->center_freq)))
+               return 0;
+
+       spin_lock_bh(&reg_pending_beacons_lock);
+       processing = pending_reg_beacon(beacon_chan);
+       spin_unlock_bh(&reg_pending_beacons_lock);
+
+       if (processing)
                return 0;
 
        reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp);
        if (!reg_beacon)
                return -ENOMEM;
 
-       REG_DBG_PRINT("Found new beacon on "
-                     "frequency: %d MHz (Ch %d) on %s\n",
+       REG_DBG_PRINT("Found new beacon on frequency: %d MHz (Ch %d) on %s\n",
                      beacon_chan->center_freq,
                      ieee80211_frequency_to_channel(beacon_chan->center_freq),
                      wiphy_name(wiphy));
 
        memcpy(&reg_beacon->chan, beacon_chan,
-               sizeof(struct ieee80211_channel));
-
+              sizeof(struct ieee80211_channel));
 
        /*
         * Since we can be called from BH or and non-BH context
@@ -2155,21 +2047,19 @@ static void print_dfs_region(u8 dfs_region)
                pr_info(" DFS Master region JP");
                break;
        default:
-               pr_info(" DFS Master region Uknown");
+               pr_info(" DFS Master region Unknown");
                break;
        }
 }
 
 static void print_regdomain(const struct ieee80211_regdomain *rd)
 {
+       struct regulatory_request *lr = get_last_request();
 
        if (is_intersected_alpha2(rd->alpha2)) {
-
-               if (last_request->initiator ==
-                   NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+               if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
                        struct cfg80211_registered_device *rdev;
-                       rdev = cfg80211_rdev_by_wiphy_idx(
-                               last_request->wiphy_idx);
+                       rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx);
                        if (rdev) {
                                pr_info("Current regulatory domain updated by AP to: %c%c\n",
                                        rdev->country_ie_alpha2[0],
@@ -2178,22 +2068,21 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
                                pr_info("Current regulatory domain intersected:\n");
                } else
                        pr_info("Current regulatory domain intersected:\n");
-       } else if (is_world_regdom(rd->alpha2))
+       } else if (is_world_regdom(rd->alpha2)) {
                pr_info("World regulatory domain updated:\n");
-       else {
+       else {
                if (is_unknown_alpha2(rd->alpha2))
                        pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n");
                else {
-                       if (reg_request_cell_base(last_request))
-                               pr_info("Regulatory domain changed "
-                                       "to country: %c%c by Cell Station\n",
+                       if (reg_request_cell_base(lr))
+                               pr_info("Regulatory domain changed to country: %c%c by Cell Station\n",
                                        rd->alpha2[0], rd->alpha2[1]);
                        else
-                               pr_info("Regulatory domain changed "
-                                       "to country: %c%c\n",
+                               pr_info("Regulatory domain changed to country: %c%c\n",
                                        rd->alpha2[0], rd->alpha2[1]);
                }
        }
+
        print_dfs_region(rd->dfs_region);
        print_rd_rules(rd);
 }
@@ -2207,22 +2096,23 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)
 /* Takes ownership of rd only if it doesn't fail */
 static int __set_regdom(const struct ieee80211_regdomain *rd)
 {
+       const struct ieee80211_regdomain *regd;
        const struct ieee80211_regdomain *intersected_rd = NULL;
        struct wiphy *request_wiphy;
+       struct regulatory_request *lr = get_last_request();
+
        /* Some basic sanity checks first */
 
+       if (!reg_is_valid_request(rd->alpha2))
+               return -EINVAL;
+
        if (is_world_regdom(rd->alpha2)) {
-               if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
-                       return -EINVAL;
                update_world_regdomain(rd);
                return 0;
        }
 
        if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
-                       !is_unknown_alpha2(rd->alpha2))
-               return -EINVAL;
-
-       if (!last_request)
+           !is_unknown_alpha2(rd->alpha2))
                return -EINVAL;
 
        /*
@@ -2230,7 +2120,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
         * rd is non static (it means CRDA was present and was used last)
         * and the pending request came in from a country IE
         */
-       if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+       if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
                /*
                 * If someone else asked us to change the rd lets only bother
                 * checking if the alpha2 changes if CRDA was already called
@@ -2246,29 +2136,23 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
         * internal EEPROM data
         */
 
-       if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
-               return -EINVAL;
-
        if (!is_valid_rd(rd)) {
                pr_err("Invalid regulatory domain detected:\n");
                print_regdomain_info(rd);
                return -EINVAL;
        }
 
-       request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
+       request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
        if (!request_wiphy &&
-           (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
-            last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) {
+           (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
+            lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) {
                schedule_delayed_work(&reg_timeout, 0);
                return -ENODEV;
        }
 
-       if (!last_request->intersect) {
-               int r;
-
-               if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) {
-                       reset_regdomains(false);
-                       cfg80211_regdomain = rd;
+       if (!lr->intersect) {
+               if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) {
+                       reset_regdomains(false, rd);
                        return 0;
                }
 
@@ -2284,20 +2168,19 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
                if (request_wiphy->regd)
                        return -EALREADY;
 
-               r = reg_copy_regd(&request_wiphy->regd, rd);
-               if (r)
-                       return r;
+               regd = reg_copy_regd(rd);
+               if (IS_ERR(regd))
+                       return PTR_ERR(regd);
 
-               reset_regdomains(false);
-               cfg80211_regdomain = rd;
+               rcu_assign_pointer(request_wiphy->regd, regd);
+               reset_regdomains(false, rd);
                return 0;
        }
 
        /* Intersection requires a bit more work */
 
-       if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
-
-               intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
+       if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+               intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
                if (!intersected_rd)
                        return -EINVAL;
 
@@ -2306,15 +2189,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
                 * However if a driver requested this specific regulatory
                 * domain we keep it for its private use
                 */
-               if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER)
-                       request_wiphy->regd = rd;
+               if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER)
+                       rcu_assign_pointer(request_wiphy->regd, rd);
                else
                        kfree(rd);
 
                rd = NULL;
 
-               reset_regdomains(false);
-               cfg80211_regdomain = intersected_rd;
+               reset_regdomains(false, intersected_rd);
 
                return 0;
        }
@@ -2326,15 +2208,15 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
 /*
  * Use this call to set the current regulatory domain. Conflicts with
  * multiple drivers can be ironed out later. Caller must've already
- * kmalloc'd the rd structure. Caller must hold cfg80211_mutex
+ * kmalloc'd the rd structure.
  */
 int set_regdom(const struct ieee80211_regdomain *rd)
 {
+       struct regulatory_request *lr;
        int r;
 
-       assert_cfg80211_lock();
-
        mutex_lock(&reg_mutex);
+       lr = get_last_request();
 
        /* Note that this doesn't update the wiphys, this is done below */
        r = __set_regdom(rd);
@@ -2343,23 +2225,25 @@ int set_regdom(const struct ieee80211_regdomain *rd)
                        reg_set_request_processed();
 
                kfree(rd);
-               mutex_unlock(&reg_mutex);
-               return r;
+               goto out;
        }
 
        /* This would make this whole thing pointless */
-       if (!last_request->intersect)
-               BUG_ON(rd != cfg80211_regdomain);
+       if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) {
+               r = -EINVAL;
+               goto out;
+       }
 
        /* update all wiphys now with the new established regulatory domain */
-       update_all_wiphy_regulatory(last_request->initiator);
+       update_all_wiphy_regulatory(lr->initiator);
 
-       print_regdomain(cfg80211_regdomain);
+       print_regdomain(get_cfg80211_regdom());
 
-       nl80211_send_reg_change_event(last_request);
+       nl80211_send_reg_change_event(lr);
 
        reg_set_request_processed();
 
+ out:
        mutex_unlock(&reg_mutex);
 
        return r;
@@ -2367,20 +2251,26 @@ int set_regdom(const struct ieee80211_regdomain *rd)
 
 int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
-       if (last_request && !last_request->processed) {
-               if (add_uevent_var(env, "COUNTRY=%c%c",
-                                  last_request->alpha2[0],
-                                  last_request->alpha2[1]))
-                       return -ENOMEM;
+       struct regulatory_request *lr;
+       u8 alpha2[2];
+       bool add = false;
+
+       rcu_read_lock();
+       lr = get_last_request();
+       if (lr && !lr->processed) {
+               memcpy(alpha2, lr->alpha2, 2);
+               add = true;
        }
+       rcu_read_unlock();
 
+       if (add)
+               return add_uevent_var(env, "COUNTRY=%c%c",
+                                     alpha2[0], alpha2[1]);
        return 0;
 }
 
 void wiphy_regulatory_register(struct wiphy *wiphy)
 {
-       assert_cfg80211_lock();
-
        mutex_lock(&reg_mutex);
 
        if (!reg_dev_ignore_cell_hint(wiphy))
@@ -2395,32 +2285,32 @@ void wiphy_regulatory_register(struct wiphy *wiphy)
 void wiphy_regulatory_deregister(struct wiphy *wiphy)
 {
        struct wiphy *request_wiphy = NULL;
-
-       assert_cfg80211_lock();
+       struct regulatory_request *lr;
 
        mutex_lock(&reg_mutex);
+       lr = get_last_request();
 
        if (!reg_dev_ignore_cell_hint(wiphy))
                reg_num_devs_support_basehint--;
 
-       kfree(wiphy->regd);
+       rcu_free_regdom(get_wiphy_regdom(wiphy));
+       rcu_assign_pointer(wiphy->regd, NULL);
 
-       if (last_request)
-               request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
+       if (lr)
+               request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
 
        if (!request_wiphy || request_wiphy != wiphy)
                goto out;
 
-       last_request->wiphy_idx = WIPHY_IDX_STALE;
-       last_request->country_ie_env = ENVIRON_ANY;
+       lr->wiphy_idx = WIPHY_IDX_INVALID;
+       lr->country_ie_env = ENVIRON_ANY;
 out:
        mutex_unlock(&reg_mutex);
 }
 
 static void reg_timeout_work(struct work_struct *work)
 {
-       REG_DBG_PRINT("Timeout while waiting for CRDA to reply, "
-                     "restoring regulatory settings\n");
+       REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
        restore_regulatory_settings(true);
 }
 
@@ -2439,13 +2329,13 @@ int __init regulatory_init(void)
 
        reg_regdb_size_check();
 
-       cfg80211_regdomain = cfg80211_world_regdom;
+       rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom);
 
        user_alpha2[0] = '9';
        user_alpha2[1] = '7';
 
        /* We always try to get an update for the static regdomain */
-       err = regulatory_hint_core(cfg80211_regdomain->alpha2);
+       err = regulatory_hint_core(cfg80211_world_regdom->alpha2);
        if (err) {
                if (err == -ENOMEM)
                        return err;
@@ -2457,10 +2347,6 @@ int __init regulatory_init(void)
                 * errors as non-fatal.
                 */
                pr_err("kobject_uevent_env() was unable to call CRDA during init\n");
-#ifdef CONFIG_CFG80211_REG_DEBUG
-               /* We want to find out exactly why when debugging */
-               WARN_ON(err);
-#endif
        }
 
        /*
@@ -2474,7 +2360,7 @@ int __init regulatory_init(void)
        return 0;
 }
 
-void /* __init_or_exit */ regulatory_exit(void)
+void regulatory_exit(void)
 {
        struct regulatory_request *reg_request, *tmp;
        struct reg_beacon *reg_beacon, *btmp;
@@ -2482,43 +2368,27 @@ void /* __init_or_exit */ regulatory_exit(void)
        cancel_work_sync(&reg_work);
        cancel_delayed_work_sync(&reg_timeout);
 
-       mutex_lock(&cfg80211_mutex);
+       /* Lock to suppress warnings */
        mutex_lock(&reg_mutex);
-
-       reset_regdomains(true);
+       reset_regdomains(true, NULL);
+       mutex_unlock(&reg_mutex);
 
        dev_set_uevent_suppress(&reg_pdev->dev, true);
 
        platform_device_unregister(reg_pdev);
 
-       spin_lock_bh(&reg_pending_beacons_lock);
-       if (!list_empty(&reg_pending_beacons)) {
-               list_for_each_entry_safe(reg_beacon, btmp,
-                                        &reg_pending_beacons, list) {
-                       list_del(&reg_beacon->list);
-                       kfree(reg_beacon);
-               }
+       list_for_each_entry_safe(reg_beacon, btmp, &reg_pending_beacons, list) {
+               list_del(&reg_beacon->list);
+               kfree(reg_beacon);
        }
-       spin_unlock_bh(&reg_pending_beacons_lock);
 
-       if (!list_empty(&reg_beacon_list)) {
-               list_for_each_entry_safe(reg_beacon, btmp,
-                                        &reg_beacon_list, list) {
-                       list_del(&reg_beacon->list);
-                       kfree(reg_beacon);
-               }
+       list_for_each_entry_safe(reg_beacon, btmp, &reg_beacon_list, list) {
+               list_del(&reg_beacon->list);
+               kfree(reg_beacon);
        }
 
-       spin_lock(&reg_requests_lock);
-       if (!list_empty(&reg_requests_list)) {
-               list_for_each_entry_safe(reg_request, tmp,
-                                        &reg_requests_list, list) {
-                       list_del(&reg_request->list);
-                       kfree(reg_request);
-               }
+       list_for_each_entry_safe(reg_request, tmp, &reg_requests_list, list) {
+               list_del(&reg_request->list);
+               kfree(reg_request);
        }
-       spin_unlock(&reg_requests_lock);
-
-       mutex_unlock(&reg_mutex);
-       mutex_unlock(&cfg80211_mutex);
 }
index 4c0a32ffd530daabb18912edb827c968456a26fa..af2d5f8a5d828481e4036cba15d7577674b0e303 100644 (file)
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-extern const struct ieee80211_regdomain *cfg80211_regdomain;
+extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
 
 bool is_world_regdom(const char *alpha2);
-bool reg_is_valid_request(const char *alpha2);
 bool reg_supported_dfs_region(u8 dfs_region);
 
 int regulatory_hint_user(const char *alpha2,
@@ -55,8 +54,8 @@ bool reg_last_request_cell_base(void);
  * set the wiphy->disable_beacon_hints to true.
  */
 int regulatory_hint_found_beacon(struct wiphy *wiphy,
-                                       struct ieee80211_channel *beacon_chan,
-                                       gfp_t gfp);
+                                struct ieee80211_channel *beacon_chan,
+                                gfp_t gfp);
 
 /**
  * regulatory_hint_11d - hints a country IE as a regulatory domain
index f2431e41a373d47b12422642b73e00068834cdd7..a825dfe12cf74bfad50ea500b5a96f9018a11a19 100644 (file)
@@ -192,7 +192,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                                            prev_bssid,
                                            params->ssid, params->ssid_len,
                                            params->ie, params->ie_len,
-                                           false, &params->crypto,
+                                           params->mfp != NL80211_MFP_NO,
+                                           &params->crypto,
                                            params->flags, &params->ht_capa,
                                            &params->ht_capa_mask);
                if (err)
@@ -519,10 +520,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
         * - country_ie + 2, the start of the country ie data, and
         * - and country_ie[1] which is the IE length
         */
-       regulatory_hint_11d(wdev->wiphy,
-                           bss->channel->band,
-                           country_ie + 2,
-                           country_ie[1]);
+       regulatory_hint_11d(wdev->wiphy, bss->channel->band,
+                           country_ie + 2, country_ie[1]);
        kfree(country_ie);
 }
 
index 2134576f426e0f5853255b3d76fb2e3bff5fe24c..8bc5531996867c9d47df9e6709f9cc76c0d7b7b9 100644 (file)
@@ -1767,6 +1767,24 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device,
        TP_ARGS(wiphy, wdev)
 );
 
+TRACE_EVENT(rdev_set_mac_acl,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+                struct cfg80211_acl_data *params),
+       TP_ARGS(wiphy, netdev, params),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               NETDEV_ENTRY
+               __field(u32, acl_policy)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WIPHY_ASSIGN;
+               __entry->acl_policy = params->acl_policy;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", acl policy: %d",
+                 WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->acl_policy)
+);
+
 /*************************************************************
  *          cfg80211 exported functions traces              *
  *************************************************************/
index 16d76a807c2fac022294aa33cb0336bb89bab38b..d7873c7ae0ec7135c5b3882ccd9ea389cffcbeed 100644 (file)
@@ -1184,7 +1184,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
                                 struct wireless_dev *wdev,
                                 enum nl80211_iftype iftype,
                                 struct ieee80211_channel *chan,
-                                enum cfg80211_chan_mode chanmode)
+                                enum cfg80211_chan_mode chanmode,
+                                u8 radar_detect)
 {
        struct wireless_dev *wdev_iter;
        u32 used_iftypes = BIT(iftype);
@@ -1195,14 +1196,46 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        enum cfg80211_chan_mode chmode;
        int num_different_channels = 0;
        int total = 1;
+       bool radar_required;
        int i, j;
 
        ASSERT_RTNL();
        lockdep_assert_held(&rdev->devlist_mtx);
 
+       if (WARN_ON(hweight32(radar_detect) > 1))
+               return -EINVAL;
+
+       switch (iftype) {
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_MESH_POINT:
+       case NL80211_IFTYPE_P2P_GO:
+       case NL80211_IFTYPE_WDS:
+               radar_required = !!(chan &&
+                                   (chan->flags & IEEE80211_CHAN_RADAR));
+               break;
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_MONITOR:
+               radar_required = false;
+               break;
+       case NL80211_IFTYPE_P2P_DEVICE:
+       case NUM_NL80211_IFTYPES:
+       case NL80211_IFTYPE_UNSPECIFIED:
+       default:
+               return -EINVAL;
+       }
+
+       if (radar_required && !radar_detect)
+               return -EINVAL;
+
        /* Always allow software iftypes */
-       if (rdev->wiphy.software_iftypes & BIT(iftype))
+       if (rdev->wiphy.software_iftypes & BIT(iftype)) {
+               if (radar_detect)
+                       return -EINVAL;
                return 0;
+       }
 
        memset(num, 0, sizeof(num));
        memset(used_channels, 0, sizeof(used_channels));
@@ -1275,7 +1308,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
                used_iftypes |= BIT(wdev_iter->iftype);
        }
 
-       if (total == 1)
+       if (total == 1 && !radar_detect)
                return 0;
 
        for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) {
@@ -1308,6 +1341,9 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
                        }
                }
 
+               if (radar_detect && !(c->radar_detect_widths & radar_detect))
+                       goto cont;
+
                /*
                 * Finally check that all iftypes that we're currently
                 * using are actually part of this combination. If they
index fff7753e35c15ba6f82612594fbda19e7203b66e..e6f4633b8dd5b6b9cdfebc6409fc6db279393aed 100644 (file)
@@ -34,7 +34,7 @@ static struct clk *ac97_clk;
 static struct clk *ac97conf_clk;
 static int reset_gpio;
 
-extern void pxa27x_assert_ac97reset(int reset_gpio, int on);
+extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio);
 
 /*
  * Beware PXA27x bugs:
@@ -140,10 +140,10 @@ static inline void pxa_ac97_warm_pxa27x(void)
        gsr_bits = 0;
 
        /* warm reset broken on Bulverde, so manually keep AC97 reset high */
-       pxa27x_assert_ac97reset(reset_gpio, 1);
+       pxa27x_configure_ac97reset(reset_gpio, true);
        udelay(10);
        GCR |= GCR_WARM_RST;
-       pxa27x_assert_ac97reset(reset_gpio, 0);
+       pxa27x_configure_ac97reset(reset_gpio, false);
        udelay(500);
 }
 
@@ -358,7 +358,7 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev)
                               __func__, ret);
                        goto err_conf;
                }
-               pxa27x_assert_ac97reset(reset_gpio, 0);
+               pxa27x_configure_ac97reset(reset_gpio, false);
 
                ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
                if (IS_ERR(ac97conf_clk)) {
index c4840ff75d00a97ebbe12e3820ab43dd743a785e..357b894e1174069c65a397b71f1a0851bd5118b4 100644 (file)
@@ -1110,7 +1110,7 @@ int snd_interval_list(struct snd_interval *i, unsigned int count,
 
 EXPORT_SYMBOL(snd_interval_list);
 
-static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step)
+int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step)
 {
        unsigned int n;
        int changed = 0;
@@ -1130,6 +1130,7 @@ static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned
        }
        return changed;
 }
+EXPORT_SYMBOL(snd_interval_step);
 
 /* Info constraints helpers */
 
@@ -2050,6 +2051,9 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime;
        if (PCM_RUNTIME_CHECK(substream))
                return -ENXIO;
+       /* TODO: consider and -EINVAL here */
+       if (substream->hw_no_buffer)
+               snd_printd("%s: warning this PCM is host less\n", __func__);
        runtime = substream->runtime;
        if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
                return -EINVAL;
index 09b4286c65f9d7d6e7ab8abed498ad2d7f96e737..0a305e41ad203a6aee672cb42133b575d8c979e0 100644 (file)
@@ -853,6 +853,7 @@ static int snd_pcm_pre_start(struct snd_pcm_substream *substream, int state)
        if (runtime->status->state != SNDRV_PCM_STATE_PREPARED)
                return -EBADFD;
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+           !substream->hw_no_buffer &&
            !snd_pcm_playback_data(substream))
                return -EPIPE;
        runtime->trigger_master = substream;
index 99f32f7c0692713f90ca1d917dd23103bcebc601..4df20e5679c3ca1cba612233b296414afeaf3a50 100644 (file)
@@ -1,5 +1,5 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
-snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o
+snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-fw.o
 
 ifneq ($(CONFIG_SND_SOC_DMAENGINE_PCM),)
 snd-soc-core-objs += soc-dmaengine-pcm.o
index d1b691bf8e2d918cb3e8fd97e61cce74c464cd76..3fdd87fa18a970db151fdadcc578ca61c87d3241 100644 (file)
@@ -1,6 +1,6 @@
 config SND_ATMEL_SOC
        tristate "SoC Audio for the Atmel System-on-Chip"
-       depends on ARCH_AT91
+       depends on HAS_IOMEM
        help
          Say Y or M if you want to add support for codecs attached to
          the ATMEL SSC interface. You will also need
@@ -24,7 +24,7 @@ config SND_ATMEL_SOC_SSC
 
 config SND_AT91_SOC_SAM9G20_WM8731
        tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
-       depends on ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS
+       depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS
        select SND_ATMEL_SOC_PDC
        select SND_ATMEL_SOC_SSC
        select SND_SOC_WM8731
@@ -34,7 +34,7 @@ config SND_AT91_SOC_SAM9G20_WM8731
 
 config SND_AT91_SOC_AFEB9260
        tristate "SoC Audio support for AFEB9260 board"
-       depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
+       depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
        select SND_ATMEL_SOC_PDC
        select SND_ATMEL_SOC_SSC
        select SND_SOC_TLV320AIC23
index 6a293c713a38889a80fae114553b5d38263c3a0b..054ea4d9326a2e34f8f9bc6f6ae51f18fb1d98dd 100644 (file)
@@ -159,7 +159,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
 
        pr_debug("atmel-pcm: "
                "hw_params: DMA for %s initialized "
-               "(dma_bytes=%u, period_size=%u)\n",
+               "(dma_bytes=%zu, period_size=%zu)\n",
                prtd->params->name,
                runtime->dma_bytes,
                prtd->period_size);
@@ -201,7 +201,7 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
        int ret = 0;
 
        pr_debug("atmel-pcm:buffer_size = %ld,"
-               "dma_area = %p, dma_bytes = %u\n",
+               "dma_area = %p, dma_bytes = %zu\n",
                rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
 
        switch (cmd) {
index e99f1811300aef179a5c1d5c33dc702249175681..3109db7b9017cc08dd23ef6f9c70f9c605bcdf3e 100644 (file)
@@ -49,7 +49,7 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
        buf->private_data = NULL;
        buf->area = dma_alloc_coherent(pcm->card->dev, size,
                        &buf->addr, GFP_KERNEL);
-       pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d\n",
+       pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n",
                        (void *)buf->area, (void *)buf->addr, size);
 
        if (!buf->area)
index 1c7663422054049265957743bad8c6f8678e9e17..2755750bda46ae6c95a610532c3f0fc0c0c85c2d 100644 (file)
@@ -42,8 +42,6 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 
-#include <mach/hardware.h>
-
 #include "atmel-pcm.h"
 #include "atmel_ssc_dai.h"
 
index 1f0cdab03294725b4ba02f6a28e4b6ce68eb6e89..2d037870970221b2c2a87f4f96cac4886d606cb9 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
+#include <linux/of_device.h>
 #include <linux/module.h>
 #include <sound/soc.h>
 #include <sound/initval.h>
@@ -513,12 +514,31 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = {
 };
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static struct of_device_id ak4642_of_match[];
 static int ak4642_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
+       struct device_node *np = i2c->dev.of_node;
+       const struct snd_soc_codec_driver *driver;
+
+       driver = NULL;
+       if (np) {
+               const struct of_device_id *of_id;
+
+               of_id = of_match_device(ak4642_of_match, &i2c->dev);
+               if (of_id)
+                       driver = of_id->data;
+       } else {
+               driver = (struct snd_soc_codec_driver *)id->driver_data;
+       }
+
+       if (!driver) {
+               dev_err(&i2c->dev, "no driver\n");
+               return -EINVAL;
+       }
+
        return snd_soc_register_codec(&i2c->dev,
-                               (struct snd_soc_codec_driver *)id->driver_data,
-                               &ak4642_dai, 1);
+                                     driver, &ak4642_dai, 1);
 }
 
 static int ak4642_i2c_remove(struct i2c_client *client)
@@ -527,6 +547,14 @@ static int ak4642_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+static struct of_device_id ak4642_of_match[] = {
+       { .compatible = "asahi-kasei,ak4642",   .data = &soc_codec_dev_ak4642},
+       { .compatible = "asahi-kasei,ak4643",   .data = &soc_codec_dev_ak4642},
+       { .compatible = "asahi-kasei,ak4648",   .data = &soc_codec_dev_ak4648},
+       {},
+};
+MODULE_DEVICE_TABLE(of, ak4642_of_match);
+
 static const struct i2c_device_id ak4642_i2c_id[] = {
        { "ak4642", (kernel_ulong_t)&soc_codec_dev_ak4642 },
        { "ak4643", (kernel_ulong_t)&soc_codec_dev_ak4642 },
@@ -539,6 +567,7 @@ static struct i2c_driver ak4642_i2c_driver = {
        .driver = {
                .name = "ak4642-codec",
                .owner = THIS_MODULE,
+               .of_match_table = ak4642_of_match,
        },
        .probe          = ak4642_i2c_probe,
        .remove         = ak4642_i2c_remove,
index 2899cb909d1e1fb9c99e65700210c323eb70ac4e..5e246c5b1ed6927d28900dae78db4463bb42d9e6 100644 (file)
@@ -141,6 +141,30 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
        "ASRC1R",
        "ASRC2L",
        "ASRC2R",
+       "ISRC1INT1",
+       "ISRC1INT2",
+       "ISRC1INT3",
+       "ISRC1INT4",
+       "ISRC1DEC1",
+       "ISRC1DEC2",
+       "ISRC1DEC3",
+       "ISRC1DEC4",
+       "ISRC2INT1",
+       "ISRC2INT2",
+       "ISRC2INT3",
+       "ISRC2INT4",
+       "ISRC2DEC1",
+       "ISRC2DEC2",
+       "ISRC2DEC3",
+       "ISRC2DEC4",
+       "ISRC3INT1",
+       "ISRC3INT2",
+       "ISRC3INT3",
+       "ISRC3INT4",
+       "ISRC3DEC1",
+       "ISRC3DEC2",
+       "ISRC3DEC3",
+       "ISRC3DEC4",
 };
 EXPORT_SYMBOL_GPL(arizona_mixer_texts);
 
@@ -220,6 +244,30 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
        0x91,
        0x92,
        0x93,
+       0xa0,  /* ISRC1INT1 */
+       0xa1,
+       0xa2,
+       0xa3,
+       0xa4,  /* ISRC1DEC1 */
+       0xa5,
+       0xa6,
+       0xa7,
+       0xa8,  /* ISRC2DEC1 */
+       0xa9,
+       0xaa,
+       0xab,
+       0xac,  /* ISRC2INT1 */
+       0xad,
+       0xae,
+       0xaf,
+       0xb0,  /* ISRC3DEC1 */
+       0xb1,
+       0xb2,
+       0xb3,
+       0xb4,  /* ISRC3INT1 */
+       0xb5,
+       0xb6,
+       0xb7,
 };
 EXPORT_SYMBOL_GPL(arizona_mixer_values);
 
@@ -275,6 +323,15 @@ const struct soc_enum arizona_lhpf4_mode =
                        arizona_lhpf_mode_text);
 EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
 
+static const char *arizona_ng_hold_text[] = {
+       "30ms", "120ms", "250ms", "500ms",
+};
+
+const struct soc_enum arizona_ng_hold =
+       SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT,
+                       4, arizona_ng_hold_text);
+EXPORT_SYMBOL_GPL(arizona_ng_hold);
+
 int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
                  int event)
 {
@@ -417,6 +474,10 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
        case 147456000:
                val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT;
                break;
+       case 0:
+               dev_dbg(arizona->dev, "%s cleared\n", name);
+               *clk = freq;
+               return 0;
        default:
                return -EINVAL;
        }
@@ -635,6 +696,9 @@ static int arizona_startup(struct snd_pcm_substream *substream,
                return 0;
        }
 
+       if (base_rate == 0)
+               return 0;
+
        if (base_rate % 8000)
                constraint = &arizona_44k1_constraint;
        else
@@ -645,25 +709,81 @@ static int arizona_startup(struct snd_pcm_substream *substream,
                                          constraint);
 }
 
+static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *params,
+                                 struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+       int base = dai->driver->base;
+       int i, sr_val;
+
+       /*
+        * We will need to be more flexible than this in future,
+        * currently we use a single sample rate for SYSCLK.
+        */
+       for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++)
+               if (arizona_sr_vals[i] == params_rate(params))
+                       break;
+       if (i == ARRAY_SIZE(arizona_sr_vals)) {
+               arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
+                               params_rate(params));
+               return -EINVAL;
+       }
+       sr_val = i;
+
+       switch (dai_priv->clk) {
+       case ARIZONA_CLK_SYSCLK:
+               snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
+                                   ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
+               if (base)
+                       snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
+                                           ARIZONA_AIF1_RATE_MASK, 0);
+               break;
+       case ARIZONA_CLK_ASYNCCLK:
+               snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
+                                   ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val);
+               if (base)
+                       snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
+                                           ARIZONA_AIF1_RATE_MASK,
+                                           8 << ARIZONA_AIF1_RATE_SHIFT);
+               break;
+       default:
+               arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int arizona_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *params,
                             struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
        struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
-       struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+       struct arizona *arizona = priv->arizona;
        int base = dai->driver->base;
        const int *rates;
-       int i;
-       int bclk, lrclk, wl, frame, sr_val;
+       int i, ret;
+       int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1];
+       int bclk, lrclk, wl, frame, bclk_target;
 
        if (params_rate(params) % 8000)
                rates = &arizona_44k1_bclk_rates[0];
        else
                rates = &arizona_48k_bclk_rates[0];
 
+       bclk_target = snd_soc_params_to_bclk(params);
+       if (chan_limit && chan_limit < params_channels(params)) {
+               arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit);
+               bclk_target /= params_channels(params);
+               bclk_target *= chan_limit;
+       }
+
        for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) {
-               if (rates[i] >= snd_soc_params_to_bclk(params) &&
+               if (rates[i] >= bclk_target &&
                    rates[i] % params_rate(params) == 0) {
                        bclk = i;
                        break;
@@ -675,16 +795,6 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++)
-               if (arizona_sr_vals[i] == params_rate(params))
-                       break;
-       if (i == ARRAY_SIZE(arizona_sr_vals)) {
-               arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
-                               params_rate(params));
-               return -EINVAL;
-       }
-       sr_val = i;
-
        lrclk = rates[bclk] / params_rate(params);
 
        arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n",
@@ -693,28 +803,9 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
        wl = snd_pcm_format_width(params_format(params));
        frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl;
 
-       /*
-        * We will need to be more flexible than this in future,
-        * currently we use a single sample rate for SYSCLK.
-        */
-       switch (dai_priv->clk) {
-       case ARIZONA_CLK_SYSCLK:
-               snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
-                                   ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
-               snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
-                                   ARIZONA_AIF1_RATE_MASK, 0);
-               break;
-       case ARIZONA_CLK_ASYNCCLK:
-               snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
-                                   ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val);
-               snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
-                                   ARIZONA_AIF1_RATE_MASK,
-                                   8 << ARIZONA_AIF1_RATE_SHIFT);
-               break;
-       default:
-               arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
-               return -EINVAL;
-       }
+       ret = arizona_hw_params_rate(substream, params, dai);
+       if (ret != 0)
+               return ret;
 
        snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL,
                            ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
@@ -789,11 +880,27 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
        return snd_soc_dapm_sync(&codec->dapm);
 }
 
+static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       int base = dai->driver->base;
+       unsigned int reg;
+
+       if (tristate)
+               reg = ARIZONA_AIF1_TRI;
+       else
+               reg = 0;
+
+       return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
+                                  ARIZONA_AIF1_TRI, reg);
+}
+
 const struct snd_soc_dai_ops arizona_dai_ops = {
        .startup = arizona_startup,
        .set_fmt = arizona_set_fmt,
        .hw_params = arizona_hw_params,
        .set_sysclk = arizona_dai_set_sysclk,
+       .set_tristate = arizona_set_tristate,
 };
 EXPORT_SYMBOL_GPL(arizona_dai_ops);
 
@@ -807,17 +914,6 @@ int arizona_init_dai(struct arizona_priv *priv, int id)
 }
 EXPORT_SYMBOL_GPL(arizona_init_dai);
 
-static irqreturn_t arizona_fll_lock(int irq, void *data)
-{
-       struct arizona_fll *fll = data;
-
-       arizona_fll_dbg(fll, "Lock status changed\n");
-
-       complete(&fll->lock);
-
-       return IRQ_HANDLED;
-}
-
 static irqreturn_t arizona_fll_clock_ok(int irq, void *data)
 {
        struct arizona_fll *fll = data;
@@ -1066,7 +1162,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
 {
        int ret;
 
-       init_completion(&fll->lock);
        init_completion(&fll->ok);
 
        fll->id = id;
@@ -1077,13 +1172,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
        snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name),
                 "FLL%d clock OK", id);
 
-       ret = arizona_request_irq(arizona, lock_irq, fll->lock_name,
-                                 arizona_fll_lock, fll);
-       if (ret != 0) {
-               dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n",
-                       id, ret);
-       }
-
        ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name,
                                  arizona_fll_clock_ok, fll);
        if (ret != 0) {
@@ -1098,6 +1186,40 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
 }
 EXPORT_SYMBOL_GPL(arizona_init_fll);
 
+/**
+ * arizona_set_output_mode - Set the mode of the specified output
+ *
+ * @codec: Device to configure
+ * @output: Output number
+ * @diff: True to set the output to differential mode
+ *
+ * Some systems use external analogue switches to connect more
+ * analogue devices to the CODEC than are supported by the device.  In
+ * some systems this requires changing the switched output from single
+ * ended to differential mode dynamically at runtime, an operation
+ * supported using this function.
+ *
+ * Most systems have a single static configuration and should use
+ * platform data instead.
+ */
+int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
+{
+       unsigned int reg, val;
+
+       if (output < 1 || output > 6)
+               return -EINVAL;
+
+       reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8;
+
+       if (diff)
+               val = ARIZONA_OUT1_MONO;
+       else
+               val = 0;
+
+       return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val);
+}
+EXPORT_SYMBOL_GPL(arizona_set_output_mode);
+
 MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");
index 4deebeb0717754085edceb38e7c00b141f4502fb..116372c91f5de3f60eafd9a738cd4a31e1ab0bcb 100644 (file)
@@ -66,7 +66,7 @@ struct arizona_priv {
        struct arizona_dai_priv dai[ARIZONA_MAX_DAI];
 };
 
-#define ARIZONA_NUM_MIXER_INPUTS 75
+#define ARIZONA_NUM_MIXER_INPUTS 99
 
 extern const unsigned int arizona_mixer_tlv[];
 extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
@@ -176,6 +176,8 @@ extern const struct soc_enum arizona_lhpf2_mode;
 extern const struct soc_enum arizona_lhpf3_mode;
 extern const struct soc_enum arizona_lhpf4_mode;
 
+extern const struct soc_enum arizona_ng_hold;
+
 extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
                         struct snd_kcontrol *kcontrol,
                         int event);
@@ -195,7 +197,6 @@ struct arizona_fll {
        int id;
        unsigned int base;
        unsigned int vco_mult;
-       struct completion lock;
        struct completion ok;
        unsigned int fref;
        unsigned int fout;
@@ -211,4 +212,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source,
 
 extern int arizona_init_dai(struct arizona_priv *priv, int dai);
 
+int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
+                           bool diff);
+
 #endif
index ac8742a1f25ab7c69fcca2b22458ae02acf3d67e..2415a4118dbd84ad8dee6a74692edff2be6dc743 100644 (file)
@@ -167,6 +167,8 @@ struct cs4271_private {
        int                             gpio_nreset;
        /* GPIO that disable serial bus, if any */
        int                             gpio_disable;
+       /* enable soft reset workaround */
+       bool                            enable_soft_reset;
 };
 
 /*
@@ -325,6 +327,33 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
        int i, ret;
        unsigned int ratio, val;
 
+       if (cs4271->enable_soft_reset) {
+               /*
+                * Put the codec in soft reset and back again in case it's not
+                * currently streaming data. This way of bringing the codec in
+                * sync to the current clocks is not explicitly documented in
+                * the data sheet, but it seems to work fine, and in contrast
+                * to a read hardware reset, we don't have to sync back all
+                * registers every time.
+                */
+
+               if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+                    !dai->capture_active) ||
+                   (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+                    !dai->playback_active)) {
+                       ret = snd_soc_update_bits(codec, CS4271_MODE2,
+                                                 CS4271_MODE2_PDN,
+                                                 CS4271_MODE2_PDN);
+                       if (ret < 0)
+                               return ret;
+
+                       ret = snd_soc_update_bits(codec, CS4271_MODE2,
+                                                 CS4271_MODE2_PDN, 0);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
        cs4271->rate = params_rate(params);
 
        /* Configure DAC */
@@ -484,6 +513,10 @@ static int cs4271_probe(struct snd_soc_codec *codec)
                if (of_get_property(codec->dev->of_node,
                                     "cirrus,amutec-eq-bmutec", NULL))
                        amutec_eq_bmutec = true;
+
+               if (of_get_property(codec->dev->of_node,
+                                    "cirrus,enable-soft-reset", NULL))
+                       cs4271->enable_soft_reset = true;
        }
 #endif
 
@@ -492,6 +525,7 @@ static int cs4271_probe(struct snd_soc_codec *codec)
                        gpio_nreset = cs4271plat->gpio_nreset;
 
                amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
+               cs4271->enable_soft_reset = cs4271plat->enable_soft_reset;
        }
 
        if (gpio_nreset >= 0)
index 9811a5478c87649da6c42806a3a43bfcc5a5767e..0f6f481cec09cd941688ed790ecd31e10e207496 100644 (file)
@@ -1038,7 +1038,7 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec)
        struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
        int ret;
 
-       cs42l52->beep = input_allocate_device();
+       cs42l52->beep = devm_input_allocate_device(codec->dev);
        if (!cs42l52->beep) {
                dev_err(codec->dev, "Failed to allocate beep device\n");
                return;
@@ -1059,7 +1059,6 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec)
 
        ret = input_register_device(cs42l52->beep);
        if (ret != 0) {
-               input_free_device(cs42l52->beep);
                cs42l52->beep = NULL;
                dev_err(codec->dev, "Failed to register beep device\n");
        }
@@ -1076,7 +1075,6 @@ static void cs42l52_free_beep(struct snd_soc_codec *codec)
        struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
 
        device_remove_file(codec->dev, &dev_attr_beep);
-       input_unregister_device(cs42l52->beep);
        cancel_work_sync(&cs42l52->beep_work);
        cs42l52->beep = NULL;
 
index 782b0cded2e69350046d942cfa67c836e521ce1a..4f358393d6d63b721495ccc8330da431a9c38a20 100644 (file)
@@ -1452,20 +1452,6 @@ static int dac33_soc_remove(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int dac33_soc_suspend(struct snd_soc_codec *codec)
-{
-       dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int dac33_soc_resume(struct snd_soc_codec *codec)
-{
-       dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_tlv320dac33 = {
        .read = dac33_read_reg_cache,
        .write = dac33_write_locked,
@@ -1476,8 +1462,6 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320dac33 = {
        .reg_cache_default = dac33_reg,
        .probe = dac33_soc_probe,
        .remove = dac33_soc_remove,
-       .suspend = dac33_soc_suspend,
-       .resume = dac33_soc_resume,
 
        .controls = dac33_snd_controls,
        .num_controls = ARRAY_SIZE(dac33_snd_controls),
index 63b280b060359f12ffda4f018f4f9e012f32eac2..8e6e5b0160219ef140748a3dbf31bc7561b9a8d1 100644 (file)
 /* Register descriptions are here */
 #include <linux/mfd/twl4030-audio.h>
 
+/* TWL4030 PMBR1 Register */
+#define TWL4030_PMBR1_REG              0x0D
+/* TWL4030 PMBR1 Register GPIO6 mux bits */
+#define TWL4030_GPIO6_PWM0_MUTE(value) ((value & 0x03) << 2)
+
 /* Shadow register used by the audio driver */
 #define TWL4030_REG_SW_SHADOW          0x4A
 #define TWL4030_CACHEREGNUM    (TWL4030_REG_SW_SHADOW + 1)
@@ -348,19 +353,32 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
 
        pdata = twl4030_get_pdata(codec);
 
-       if (pdata && pdata->hs_extmute &&
-           gpio_is_valid(pdata->hs_extmute_gpio)) {
-               int ret;
-
-               if (!pdata->hs_extmute_gpio)
-                       dev_warn(codec->dev,
-                                "Extmute GPIO is 0 is this correct?\n");
-
-               ret = gpio_request_one(pdata->hs_extmute_gpio,
-                                      GPIOF_OUT_INIT_LOW, "hs_extmute");
-               if (ret) {
-                       dev_err(codec->dev, "Failed to get hs_extmute GPIO\n");
-                       pdata->hs_extmute_gpio = -1;
+       if (pdata && pdata->hs_extmute) {
+               if (gpio_is_valid(pdata->hs_extmute_gpio)) {
+                       int ret;
+
+                       if (!pdata->hs_extmute_gpio)
+                               dev_warn(codec->dev,
+                                       "Extmute GPIO is 0 is this correct?\n");
+
+                       ret = gpio_request_one(pdata->hs_extmute_gpio,
+                                              GPIOF_OUT_INIT_LOW,
+                                              "hs_extmute");
+                       if (ret) {
+                               dev_err(codec->dev,
+                                       "Failed to get hs_extmute GPIO\n");
+                               pdata->hs_extmute_gpio = -1;
+                       }
+               } else {
+                       u8 pin_mux;
+
+                       /* Set TWL4030 GPIO6 as EXTMUTE signal */
+                       twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux,
+                                       TWL4030_PMBR1_REG);
+                       pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03);
+                       pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02);
+                       twl_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux,
+                                        TWL4030_PMBR1_REG);
                }
        }
 
@@ -1306,6 +1324,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        SND_SOC_DAPM_DAC("DAC Left2", NULL, SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_DAC("DAC Voice", NULL, SND_SOC_NOPM, 0, 0),
 
+       SND_SOC_DAPM_AIF_IN("VAIFIN", "Voice Playback", 0,
+                           TWL4030_REG_VOICE_IF, 6, 0),
+
        /* Analog bypasses */
        SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
                        &twl4030_dapm_abypassr1_control),
@@ -1438,6 +1459,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        SND_SOC_DAPM_ADC("ADC Virtual Left2", NULL, SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_ADC("ADC Virtual Right2", NULL, SND_SOC_NOPM, 0, 0),
 
+       SND_SOC_DAPM_AIF_OUT("VAIFOUT", "Voice Capture", 0,
+                            TWL4030_REG_VOICE_IF, 5, 0),
+
        /* Analog/Digital mic path selection.
           TX1 Left/Right: either analog Left/Right or Digimic0
           TX2 Left/Right: either analog Left/Right or Digimic1 */
@@ -1473,10 +1497,15 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        SND_SOC_DAPM_SUPPLY("micbias2 select", TWL4030_REG_MICBIAS_CTL, 6, 0,
                            NULL, 0),
 
-       SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0),
-       SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0),
-       SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0),
+       /* Microphone bias */
+       SND_SOC_DAPM_SUPPLY("Mic Bias 1",
+                           TWL4030_REG_MICBIAS_CTL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Mic Bias 2",
+                           TWL4030_REG_MICBIAS_CTL, 1, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Headset Mic Bias",
+                           TWL4030_REG_MICBIAS_CTL, 2, 0, NULL, 0),
 
+       SND_SOC_DAPM_SUPPLY("VIF Enable", TWL4030_REG_VOICE_IF, 0, 0, NULL, 0),
 };
 
 static const struct snd_soc_dapm_route intercon[] = {
@@ -1485,17 +1514,16 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"DAC Left1", NULL, "HiFi Playback"},
        {"DAC Right2", NULL, "HiFi Playback"},
        {"DAC Left2", NULL, "HiFi Playback"},
-       {"DAC Voice", NULL, "Voice Playback"},
+       {"DAC Voice", NULL, "VAIFIN"},
 
        /* ADC -> Stream mapping */
        {"HiFi Capture", NULL, "ADC Virtual Left1"},
        {"HiFi Capture", NULL, "ADC Virtual Right1"},
        {"HiFi Capture", NULL, "ADC Virtual Left2"},
        {"HiFi Capture", NULL, "ADC Virtual Right2"},
-       {"Voice Capture", NULL, "ADC Virtual Left1"},
-       {"Voice Capture", NULL, "ADC Virtual Right1"},
-       {"Voice Capture", NULL, "ADC Virtual Left2"},
-       {"Voice Capture", NULL, "ADC Virtual Right2"},
+       {"VAIFOUT", NULL, "ADC Virtual Left2"},
+       {"VAIFOUT", NULL, "ADC Virtual Right2"},
+       {"VAIFOUT", NULL, "VIF Enable"},
 
        {"Digital L1 Playback Mixer", NULL, "DAC Left1"},
        {"Digital R1 Playback Mixer", NULL, "DAC Right1"},
@@ -1510,6 +1538,7 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"DAC Right1", NULL, "AIF Enable"},
        {"DAC Left2", NULL, "AIF Enable"},
        {"DAC Right1", NULL, "AIF Enable"},
+       {"DAC Voice", NULL, "VIF Enable"},
 
        {"Digital R2 Playback Mixer", NULL, "AIF Enable"},
        {"Digital L2 Playback Mixer", NULL, "AIF Enable"},
@@ -2267,18 +2296,6 @@ static struct snd_soc_dai_driver twl4030_dai[] = {
 },
 };
 
-static int twl4030_soc_suspend(struct snd_soc_codec *codec)
-{
-       twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int twl4030_soc_resume(struct snd_soc_codec *codec)
-{
-       twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int twl4030_soc_probe(struct snd_soc_codec *codec)
 {
        struct twl4030_priv *twl4030;
@@ -2316,8 +2333,6 @@ static int twl4030_soc_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_twl4030 = {
        .probe = twl4030_soc_probe,
        .remove = twl4030_soc_remove,
-       .suspend = twl4030_soc_suspend,
-       .resume = twl4030_soc_resume,
        .read = twl4030_read_reg_cache,
        .write = twl4030_write,
        .set_bias_level = twl4030_set_bias_level,
index 3fc3fc64dd8b47a85ca142d3ef95dfe637908754..9b9a6e587610540c310c39387791f588592e1a4c 100644 (file)
@@ -69,13 +69,8 @@ struct twl6040_data {
        int hs_power_mode_locked;
        unsigned int clk_in;
        unsigned int sysclk;
-       u16 hs_left_step;
-       u16 hs_right_step;
-       u16 hf_left_step;
-       u16 hf_right_step;
        struct twl6040_jack_data hs_jack;
        struct snd_soc_codec *codec;
-       struct workqueue_struct *workqueue;
        struct mutex mutex;
 };
 
@@ -404,8 +399,7 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data)
        struct snd_soc_codec *codec = data;
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 
-       queue_delayed_work(priv->workqueue, &priv->hs_jack.work,
-                          msecs_to_jiffies(200));
+       schedule_delayed_work(&priv->hs_jack.work, msecs_to_jiffies(200));
 
        return IRQ_HANDLED;
 }
@@ -1115,7 +1109,6 @@ static int twl6040_suspend(struct snd_soc_codec *codec)
 static int twl6040_resume(struct snd_soc_codec *codec)
 {
        twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       twl6040_set_bias_level(codec, codec->dapm.suspend_bias_level);
 
        return 0;
 }
@@ -1127,83 +1120,46 @@ static int twl6040_resume(struct snd_soc_codec *codec)
 static int twl6040_probe(struct snd_soc_codec *codec)
 {
        struct twl6040_data *priv;
-       struct twl6040_codec_data *pdata = dev_get_platdata(codec->dev);
        struct platform_device *pdev = container_of(codec->dev,
                                                   struct platform_device, dev);
        int ret = 0;
 
-       priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL);
+       priv = devm_kzalloc(codec->dev, sizeof(*priv), GFP_KERNEL);
        if (priv == NULL)
                return -ENOMEM;
+
        snd_soc_codec_set_drvdata(codec, priv);
 
        priv->codec = codec;
        codec->control_data = dev_get_drvdata(codec->dev->parent);
 
-       if (pdata && pdata->hs_left_step && pdata->hs_right_step) {
-               priv->hs_left_step = pdata->hs_left_step;
-               priv->hs_right_step = pdata->hs_right_step;
-       } else {
-               priv->hs_left_step = 1;
-               priv->hs_right_step = 1;
-       }
-
-       if (pdata && pdata->hf_left_step && pdata->hf_right_step) {
-               priv->hf_left_step = pdata->hf_left_step;
-               priv->hf_right_step = pdata->hf_right_step;
-       } else {
-               priv->hf_left_step = 1;
-               priv->hf_right_step = 1;
-       }
-
        priv->plug_irq = platform_get_irq(pdev, 0);
        if (priv->plug_irq < 0) {
                dev_err(codec->dev, "invalid irq\n");
-               ret = -EINVAL;
-               goto work_err;
-       }
-
-       priv->workqueue = alloc_workqueue("twl6040-codec", 0, 0);
-       if (!priv->workqueue) {
-               ret = -ENOMEM;
-               goto work_err;
+               return -EINVAL;
        }
 
        INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work);
 
        mutex_init(&priv->mutex);
 
-       ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler,
-                                  0, "twl6040_irq_plug", codec);
+       ret = devm_request_threaded_irq(codec->dev, priv->plug_irq, NULL,
+                                       twl6040_audio_handler, IRQF_NO_SUSPEND,
+                                       "twl6040_irq_plug", codec);
        if (ret) {
                dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret);
-               goto plugirq_err;
+               return ret;
        }
 
        twl6040_init_chip(codec);
 
        /* power on device */
-       ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       if (!ret)
-               return 0;
-
-       /* Error path */
-       free_irq(priv->plug_irq, codec);
-plugirq_err:
-       destroy_workqueue(priv->workqueue);
-work_err:
-       kfree(priv);
-       return ret;
+       return twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 }
 
 static int twl6040_remove(struct snd_soc_codec *codec)
 {
-       struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-
        twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       free_irq(priv->plug_irq, codec);
-       destroy_workqueue(priv->workqueue);
-       kfree(priv);
 
        return 0;
 }
index 12bcae63a7f020f2dab718b535ad8b3d41d49ccd..655f43ea0ede601fbc1041bf93086a94ef98684c 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/firmware.h>
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
@@ -62,6 +63,7 @@ enum wm2000_anc_mode {
 struct wm2000_priv {
        struct i2c_client *i2c;
        struct regmap *regmap;
+       struct clk *mclk;
 
        struct regulator_bulk_data supplies[WM2000_NUM_SUPPLIES];
 
@@ -71,7 +73,6 @@ struct wm2000_priv {
        unsigned int anc_eng_ena:1;
        unsigned int spk_ena:1;
 
-       unsigned int mclk_div:1;
        unsigned int speech_clarity:1;
 
        int anc_download_size;
@@ -131,6 +132,7 @@ static int wm2000_poll_bit(struct i2c_client *i2c,
 static int wm2000_power_up(struct i2c_client *i2c, int analogue)
 {
        struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+       unsigned long rate;
        int ret;
 
        BUG_ON(wm2000->anc_mode != ANC_OFF);
@@ -143,7 +145,8 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)
                return ret;
        }
 
-       if (!wm2000->mclk_div) {
+       rate = clk_get_rate(wm2000->mclk);
+       if (rate <= 13500000) {
                dev_dbg(&i2c->dev, "Disabling MCLK divider\n");
                wm2000_write(i2c, WM2000_REG_SYS_CTL2,
                             WM2000_MCLK_DIV2_ENA_CLR);
@@ -550,6 +553,15 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000,
                return -EINVAL;
        }
 
+       /* Maintain clock while active */
+       if (anc_transitions[i].source == ANC_OFF) {
+               ret = clk_prepare_enable(wm2000->mclk);
+               if (ret != 0) {
+                       dev_err(&i2c->dev, "Failed to enable MCLK: %d\n", ret);
+                       return ret;
+               }
+       }
+
        for (j = 0; j < ARRAY_SIZE(anc_transitions[j].step); j++) {
                if (!anc_transitions[i].step[j])
                        break;
@@ -559,7 +571,10 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000,
                        return ret;
        }
 
-       return 0;
+       if (anc_transitions[i].dest == ANC_OFF)
+               clk_disable_unprepare(wm2000->mclk);
+
+       return ret;
 }
 
 static int wm2000_anc_set_mode(struct wm2000_priv *wm2000)
@@ -702,6 +717,9 @@ static bool wm2000_readable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
        case WM2000_REG_SYS_START:
+       case WM2000_REG_ANC_GAIN_CTRL:
+       case WM2000_REG_MSE_TH1:
+       case WM2000_REG_MSE_TH2:
        case WM2000_REG_SPEECH_CLARITY:
        case WM2000_REG_SYS_WATCHDOG:
        case WM2000_REG_ANA_VMID_PD_TIME:
@@ -823,10 +841,16 @@ static int wm2000_i2c_probe(struct i2c_client *i2c,
        reg = wm2000_read(i2c, WM2000_REG_REVISON);
        dev_info(&i2c->dev, "revision %c\n", reg + 'A');
 
+       wm2000->mclk = devm_clk_get(&i2c->dev, "MCLK");
+       if (IS_ERR(wm2000->mclk)) {
+               ret = PTR_ERR(wm2000->mclk);
+               dev_err(&i2c->dev, "Failed to get MCLK: %d\n", ret);
+               goto err_supplies;
+       }
+
        filename = "wm2000_anc.bin";
        pdata = dev_get_platdata(&i2c->dev);
        if (pdata) {
-               wm2000->mclk_div = pdata->mclkdiv2;
                wm2000->speech_clarity = !pdata->speech_enh_disable;
 
                if (pdata->download_file)
index abcd82a93995b357ba8052b0559178912990923b..fb812cd9e77dd7ea8e44cd7bc48bfd328fc0eadd 100644 (file)
@@ -10,6 +10,9 @@
 #define _WM2000_H
 
 #define WM2000_REG_SYS_START       0x8000
+#define WM2000_REG_ANC_GAIN_CTRL    0x8fa2
+#define WM2000_REG_MSE_TH2          0x8fdf
+#define WM2000_REG_MSE_TH1          0x8fe0
 #define WM2000_REG_SPEECH_CLARITY   0x8fef
 #define WM2000_REG_SYS_WATCHDOG     0x8ff6
 #define WM2000_REG_ANA_VMID_PD_TIME 0x8ff7
index d5371e0d890917b34e0d6b2b65a28f441d80a1c5..85cd9aff8d908e8789410450c29d42992ffe05cc 100644 (file)
@@ -1141,6 +1141,12 @@ SOC_DOUBLE_R_TLV("IN3 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_3L,
                 WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_DIG_VOL_SHIFT,
                 0xbf, 0, digital_tlv),
 
+SND_SOC_BYTES_MASK("EQL Coefficients", WM2200_EQL_1, 20, WM2200_EQL_ENA),
+SND_SOC_BYTES_MASK("EQR Coefficients", WM2200_EQR_1, 20, WM2200_EQR_ENA),
+
+SND_SOC_BYTES("LHPF1 Coefficeints", WM2200_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficeints", WM2200_HPLPF2_2, 1),
+
 SOC_SINGLE("OUT1 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_1L,
           WM2200_OUT1_OSR_SHIFT, 1, 0),
 SOC_SINGLE("OUT2 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_2L,
@@ -1548,6 +1554,10 @@ static int wm2200_probe(struct snd_soc_codec *codec)
                return ret;
        }
 
+       ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 2);
+       if (ret != 0)
+               return ret;
+
        return ret;
 }
 
@@ -2205,6 +2215,9 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
                wm2200->dsp[i].num = i + 1;
                wm2200->dsp[i].dev = &i2c->dev;
                wm2200->dsp[i].regmap = wm2200->regmap;
+               wm2200->dsp[i].sysclk_reg = WM2200_CLOCKING_3;
+               wm2200->dsp[i].sysclk_mask = WM2200_SYSCLK_FREQ_MASK;
+               wm2200->dsp[i].sysclk_shift =  WM2200_SYSCLK_FREQ_SHIFT;
        }
 
        wm2200->dsp[0].base = WM2200_DSP1_CONTROL_1;
@@ -2215,6 +2228,9 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
        wm2200->dsp[1].mem = wm2200_dsp2_regions;
        wm2200->dsp[1].num_mems = ARRAY_SIZE(wm2200_dsp2_regions);
 
+       for (i = 0; i < ARRAY_SIZE(wm2200->dsp); i++)
+               wm_adsp1_init(&wm2200->dsp[i]);
+
        if (pdata)
                wm2200->pdata = *pdata;
 
index 54397a50807379bf2e625b4f980773ad72515164..ac1745d030d6256525f8d05aebe14cd542de89b3 100644 (file)
@@ -563,6 +563,19 @@ SOC_DOUBLE_R("IN3 Switch", WM5100_ADC_DIGITAL_VOLUME_3L,
 SOC_DOUBLE_R("IN4 Switch", WM5100_ADC_DIGITAL_VOLUME_4L,
             WM5100_ADC_DIGITAL_VOLUME_4R, WM5100_IN4L_MUTE_SHIFT, 1, 1),
 
+SND_SOC_BYTES_MASK("EQ1 Coefficients", WM5100_EQ1_1, 20, WM5100_EQ1_ENA),
+SND_SOC_BYTES_MASK("EQ2 Coefficients", WM5100_EQ2_1, 20, WM5100_EQ2_ENA),
+SND_SOC_BYTES_MASK("EQ3 Coefficients", WM5100_EQ3_1, 20, WM5100_EQ3_ENA),
+SND_SOC_BYTES_MASK("EQ4 Coefficients", WM5100_EQ4_1, 20, WM5100_EQ4_ENA),
+
+SND_SOC_BYTES_MASK("DRC Coefficients", WM5100_DRC1_CTRL1, 5,
+                  WM5100_DRCL_ENA | WM5100_DRCR_ENA),
+
+SND_SOC_BYTES("LHPF1 Coefficeints", WM5100_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficeints", WM5100_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficeints", WM5100_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficeints", WM5100_HPLPF4_2, 1),
+
 SOC_SINGLE("HPOUT1 High Performance Switch", WM5100_OUT_VOLUME_1L,
           WM5100_OUT1_OSR_SHIFT, 1, 0),
 SOC_SINGLE("HPOUT2 High Performance Switch", WM5100_OUT_VOLUME_2L,
index 1440b3f9b7bbe1e04afdfd3d6857808f52972fa9..5e85b645f2ebad7693e414085547bdbf130ceb24 100644 (file)
@@ -45,6 +45,7 @@ static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
 static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
 static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
 
 static const struct wm_adsp_region wm5102_dsp1_regions[] = {
        { .type = WMFW_ADSP2_PM, .base = 0x100000 },
@@ -603,6 +604,17 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+#define WM5102_NG_SRC(name, base) \
+       SOC_SINGLE(name " NG HPOUT1L Switch",  base, 0, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT1R Switch",  base, 1, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT2L Switch",  base, 2, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT2R Switch",  base, 3, 1, 0), \
+       SOC_SINGLE(name " NG EPOUT Switch",    base, 4, 1, 0), \
+       SOC_SINGLE(name " NG SPKOUTL Switch",  base, 6, 1, 0), \
+       SOC_SINGLE(name " NG SPKOUTR Switch",  base, 7, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
 static const struct snd_kcontrol_new wm5102_snd_controls[] = {
 SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
           ARIZONA_IN1_OSR_SHIFT, 1, 0),
@@ -611,32 +623,44 @@ SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
 SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
           ARIZONA_IN3_OSR_SHIFT, 1, 0),
 
-SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL,
-                      ARIZONA_IN1R_CONTROL,
-                      ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL,
-                      ARIZONA_IN2R_CONTROL,
-                      ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL,
-                      ARIZONA_IN3R_CONTROL,
-                      ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-
-SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
-            ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
-            ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
-            ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
-
-SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
-                ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT,
-                0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
-                ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT,
-                0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
-                ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT,
-                0xbf, 0, digital_tlv),
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+                    ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+                    ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+                    ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+                    ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+                    ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+                    ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+          ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+          ARIZONA_IN1R_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+          ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+          ARIZONA_IN2R_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+          ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+          ARIZONA_IN3R_MUTE_SHIFT, 1, 1),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+              ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+              ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+              ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+              ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+              ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+              ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
 
 SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
 SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
@@ -774,6 +798,22 @@ SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
 SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
           ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
 
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+          ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+              ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L),
+WM5102_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R),
+WM5102_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L),
+WM5102_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM5102_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM5102_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM5102_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R),
+
 ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
@@ -880,6 +920,18 @@ ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
 ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
 ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
 
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
 ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE);
 ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE);
 
@@ -1002,6 +1054,26 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
 SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
                 NULL, 0),
 
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+
 SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
                     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
 SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
@@ -1138,6 +1210,18 @@ ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
 ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
 ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
 
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
 WM_ADSP2("DSP1", 0),
 
 SND_SOC_DAPM_OUTPUT("HPOUT1L"),
@@ -1193,6 +1277,14 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
        { name, "ASRC1R", "ASRC1R" }, \
        { name, "ASRC2L", "ASRC2L" }, \
        { name, "ASRC2R", "ASRC2R" }, \
+       { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+       { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+       { name, "ISRC1INT1", "ISRC1INT1" }, \
+       { name, "ISRC1INT2", "ISRC1INT2" }, \
+       { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+       { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+       { name, "ISRC2INT1", "ISRC2INT1" }, \
+       { name, "ISRC2INT2", "ISRC2INT2" }, \
        { name, "DSP1.1", "DSP1" }, \
        { name, "DSP1.2", "DSP1" }, \
        { name, "DSP1.3", "DSP1" }, \
@@ -1289,6 +1381,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        { "ASRC2L", NULL, "ASRC2L Input" },
        { "ASRC2R", NULL, "ASRC2R Input" },
 
+       { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" },
+       { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" },
+
+       { "ISRC1INT1", NULL, "ISRC1INT1 Input" },
+       { "ISRC1INT2", NULL, "ISRC1INT2 Input" },
+
+       { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" },
+       { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" },
+
+       { "ISRC2INT1", NULL, "ISRC2INT1 Input" },
+       { "ISRC2INT2", NULL, "ISRC2INT2 Input" },
+
        ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
        ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
        ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
@@ -1336,6 +1440,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        ARIZONA_MUX_ROUTES("ASRC2L"),
        ARIZONA_MUX_ROUTES("ASRC2R"),
 
+       ARIZONA_MUX_ROUTES("ISRC1INT1"),
+       ARIZONA_MUX_ROUTES("ISRC1INT2"),
+
+       ARIZONA_MUX_ROUTES("ISRC1DEC1"),
+       ARIZONA_MUX_ROUTES("ISRC1DEC2"),
+
+       ARIZONA_MUX_ROUTES("ISRC2INT1"),
+       ARIZONA_MUX_ROUTES("ISRC2INT2"),
+
+       ARIZONA_MUX_ROUTES("ISRC2DEC1"),
+       ARIZONA_MUX_ROUTES("ISRC2DEC2"),
+
        ARIZONA_DSP_ROUTES("DSP1"),
 
        { "AEC Loopback", "HPOUT1L", "OUT1L" },
@@ -1463,6 +1579,10 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
        if (ret != 0)
                return ret;
 
+       ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 1);
+       if (ret != 0)
+               return ret;
+
        snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
 
        priv->core.arizona->dapm = &codec->dapm;
index 7a090968c4f737bd360f05c2c7a66dc362350d21..23199372d51861500a58dc37e9f194deebd43682 100644 (file)
@@ -41,6 +41,21 @@ static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
 static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
 static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+#define WM5110_NG_SRC(name, base) \
+       SOC_SINGLE(name " NG HPOUT1L Switch",  base,  0, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT1R Switch",  base,  1, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT2L Switch",  base,  2, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT2R Switch",  base,  3, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT3L Switch",  base,  4, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT3R Switch",  base,  5, 1, 0), \
+       SOC_SINGLE(name " NG SPKOUTL Switch",  base,  6, 1, 0), \
+       SOC_SINGLE(name " NG SPKOUTR Switch",  base,  7, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT1L Switch", base,  8, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT1R Switch", base,  9, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
 
 static const struct snd_kcontrol_new wm5110_snd_controls[] = {
 SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
@@ -52,37 +67,52 @@ SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
 SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL,
           ARIZONA_IN4_OSR_SHIFT, 1, 0),
 
-SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL,
-                      ARIZONA_IN1R_CONTROL,
-                      ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL,
-                      ARIZONA_IN2R_CONTROL,
-                      ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL,
-                      ARIZONA_IN3R_CONTROL,
-                      ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-
-SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
-            ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
-            ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
-            ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN4 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L,
-            ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_MUTE_SHIFT, 1, 1),
-
-SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
-                ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT,
-                0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
-                ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT,
-                0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
-                ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT,
-                0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN4 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L,
-                ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_DIG_VOL_SHIFT,
-                0xbf, 0, digital_tlv),
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+                    ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+                    ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+                    ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+                    ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+                    ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+                    ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+          ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+          ARIZONA_IN1R_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+          ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+          ARIZONA_IN2R_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+          ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+          ARIZONA_IN3R_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN4L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L,
+          ARIZONA_IN4L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN4R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4R,
+          ARIZONA_IN4R_MUTE_SHIFT, 1, 1),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+              ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+              ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+              ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+              ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+              ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+              ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN4L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L,
+              ARIZONA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R,
+              ARIZONA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
 
 SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
 SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
@@ -263,6 +293,25 @@ SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT,
 SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
 SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
 
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+          ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+              ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM5110_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM5110_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM5110_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L),
+WM5110_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R),
+WM5110_NG_SRC("HPOUT3L", ARIZONA_NOISE_GATE_SELECT_3L),
+WM5110_NG_SRC("HPOUT3R", ARIZONA_NOISE_GATE_SELECT_3R),
+WM5110_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM5110_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM5110_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM5110_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R),
+WM5110_NG_SRC("SPKDAT2L", ARIZONA_NOISE_GATE_SELECT_6L),
+WM5110_NG_SRC("SPKDAT2R", ARIZONA_NOISE_GATE_SELECT_6R),
+
 ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
index fb92fb47d636c1a92d04d1633b9aacf2d42b31d7..ec0efc1443ba770a6c40a2e18c90b117931823bd 100644 (file)
@@ -283,18 +283,16 @@ static int pga_event(struct snd_soc_dapm_widget *w,
                out->ramp = WM8350_RAMP_UP;
                out->active = 1;
 
-               if (!delayed_work_pending(&codec->dapm.delayed_work))
-                       schedule_delayed_work(&codec->dapm.delayed_work,
-                                             msecs_to_jiffies(1));
+               schedule_delayed_work(&codec->dapm.delayed_work,
+                                     msecs_to_jiffies(1));
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
                out->ramp = WM8350_RAMP_DOWN;
                out->active = 0;
 
-               if (!delayed_work_pending(&codec->dapm.delayed_work))
-                       schedule_delayed_work(&codec->dapm.delayed_work,
-                                             msecs_to_jiffies(1));
+               schedule_delayed_work(&codec->dapm.delayed_work,
+                                     msecs_to_jiffies(1));
                break;
        }
 
index d321a875b029dc693fb2b7483dd73ceea1ce4ee8..1704b1e119cb021db8fced2be8482c5da40810d9 100644 (file)
@@ -395,9 +395,6 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
                /* power down the PLL before reprogramming it */
                snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1);
 
-               if (!freq_in || !freq_out)
-                       return 0;
-
                /* set PLLN and PRESCALE */
                snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10,
                                    pll_div.n | (pll_div.prescale << 4));
index bd4b0db4cdaadb11fe12caed12b8e8e2f5bab7b1..e9710280e5e18b882242f8d5b4cb61a2492e8eae 100644 (file)
@@ -2873,22 +2873,20 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
 
        ret = 0;
 
-       if (fll1 & WM8962_FLL_ENA) {
-               /* This should be a massive overestimate but go even
-                * higher if we'll error out
-                */
-               if (wm8962->irq)
-                       timeout = msecs_to_jiffies(5);
-               else
-                       timeout = msecs_to_jiffies(1);
+       /* This should be a massive overestimate but go even
+        * higher if we'll error out
+        */
+       if (wm8962->irq)
+               timeout = msecs_to_jiffies(5);
+       else
+               timeout = msecs_to_jiffies(1);
 
-               timeout = wait_for_completion_timeout(&wm8962->fll_lock,
-                                                     timeout);
+       timeout = wait_for_completion_timeout(&wm8962->fll_lock,
+                                             timeout);
 
-               if (timeout == 0 && wm8962->irq) {
-                       dev_err(codec->dev, "FLL lock timed out");
-                       ret = -ETIMEDOUT;
-               }
+       if (timeout == 0 && wm8962->irq) {
+               dev_err(codec->dev, "FLL lock timed out");
+               ret = -ETIMEDOUT;
        }
 
        wm8962->fll_fref = Fref;
@@ -3189,7 +3187,7 @@ static void wm8962_init_beep(struct snd_soc_codec *codec)
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
        int ret;
 
-       wm8962->beep = input_allocate_device();
+       wm8962->beep = devm_input_allocate_device(codec->dev);
        if (!wm8962->beep) {
                dev_err(codec->dev, "Failed to allocate beep device\n");
                return;
@@ -3210,7 +3208,6 @@ static void wm8962_init_beep(struct snd_soc_codec *codec)
 
        ret = input_register_device(wm8962->beep);
        if (ret != 0) {
-               input_free_device(wm8962->beep);
                wm8962->beep = NULL;
                dev_err(codec->dev, "Failed to register beep device\n");
        }
@@ -3227,7 +3224,6 @@ static void wm8962_free_beep(struct snd_soc_codec *codec)
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
 
        device_remove_file(codec->dev, &dev_attr_beep);
-       input_unregister_device(wm8962->beep);
        cancel_work_sync(&wm8962->beep_work);
        wm8962->beep = NULL;
 
@@ -3758,10 +3754,17 @@ static const struct i2c_device_id wm8962_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, wm8962_i2c_id);
 
+static const struct of_device_id wm8962_of_match[] = {
+       { .compatible = "wlf,wm8962", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8962_of_match);
+
 static struct i2c_driver wm8962_i2c_driver = {
        .driver = {
                .name = "wm8962",
                .owner = THIS_MODULE,
+               .of_match_table = wm8962_of_match,
                .pm = &wm8962_pm,
        },
        .probe =    wm8962_i2c_probe,
index 9fe1e041da498cec9bc335959c92d569ca0102b9..c9c707b8698f36f160e63f94e9a91e5bad989103 100644 (file)
@@ -851,30 +851,33 @@ static int wm8983_set_pll(struct snd_soc_dai *dai, int pll_id,
        struct pll_div pll_div;
 
        codec = dai->codec;
-       if (freq_in && freq_out) {
+       if (!freq_in || !freq_out) {
+               /* disable the PLL */
+               snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+                                   WM8983_PLLEN_MASK, 0);
+               return 0;
+       } else {
                ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in);
                if (ret)
                        return ret;
-       }
-
-       /* disable the PLL before re-programming it */
-       snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
-                           WM8983_PLLEN_MASK, 0);
 
-       if (!freq_in || !freq_out)
-               return 0;
+               /* disable the PLL before re-programming it */
+               snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+                                   WM8983_PLLEN_MASK, 0);
+
+               /* set PLLN and PRESCALE */
+               snd_soc_write(codec, WM8983_PLL_N,
+                       (pll_div.div2 << WM8983_PLL_PRESCALE_SHIFT)
+                       | pll_div.n);
+               /* set PLLK */
+               snd_soc_write(codec, WM8983_PLL_K_3, pll_div.k & 0x1ff);
+               snd_soc_write(codec, WM8983_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
+               snd_soc_write(codec, WM8983_PLL_K_1, (pll_div.k >> 18));
+               /* enable the PLL */
+               snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+                                       WM8983_PLLEN_MASK, WM8983_PLLEN);
+       }
 
-       /* set PLLN and PRESCALE */
-       snd_soc_write(codec, WM8983_PLL_N,
-                     (pll_div.div2 << WM8983_PLL_PRESCALE_SHIFT)
-                     | pll_div.n);
-       /* set PLLK */
-       snd_soc_write(codec, WM8983_PLL_K_3, pll_div.k & 0x1ff);
-       snd_soc_write(codec, WM8983_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
-       snd_soc_write(codec, WM8983_PLL_K_1, (pll_div.k >> 18));
-       /* enable the PLL */
-       snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
-                           WM8983_PLLEN_MASK, WM8983_PLLEN);
        return 0;
 }
 
index ab3782657ac81e7ae11afa0142e1d2d5a1e4d483..dd6ce3bc01cf2828279b2fc13f39b3953506d391 100644 (file)
@@ -830,33 +830,30 @@ static int wm8985_set_pll(struct snd_soc_dai *dai, int pll_id,
        struct pll_div pll_div;
 
        codec = dai->codec;
-       if (freq_in && freq_out) {
+       if (!freq_in || !freq_out) {
+               /* disable the PLL */
+               snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+                                   WM8985_PLLEN_MASK, 0);
+       } else {
                ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in);
                if (ret)
                        return ret;
-       }
 
-       /* disable the PLL before reprogramming it */
-       snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
-                           WM8985_PLLEN_MASK, 0);
-       
-       if (!freq_in || !freq_out)
-               return 0;
-
-       /* set PLLN and PRESCALE */
-       snd_soc_write(codec, WM8985_PLL_N,
-                     (pll_div.div2 << WM8985_PLL_PRESCALE_SHIFT)
-                     | pll_div.n);
-       /* set PLLK */
-       snd_soc_write(codec, WM8985_PLL_K_3, pll_div.k & 0x1ff);
-       snd_soc_write(codec, WM8985_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
-       snd_soc_write(codec, WM8985_PLL_K_1, (pll_div.k >> 18));
-       /* set the source of the clock to be the PLL */
-       snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
-                           WM8985_CLKSEL_MASK, WM8985_CLKSEL);
-       /* enable the PLL */
-       snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
-                           WM8985_PLLEN_MASK, WM8985_PLLEN);
+               /* set PLLN and PRESCALE */
+               snd_soc_write(codec, WM8985_PLL_N,
+                             (pll_div.div2 << WM8985_PLL_PRESCALE_SHIFT)
+                             | pll_div.n);
+               /* set PLLK */
+               snd_soc_write(codec, WM8985_PLL_K_3, pll_div.k & 0x1ff);
+               snd_soc_write(codec, WM8985_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
+               snd_soc_write(codec, WM8985_PLL_K_1, (pll_div.k >> 18));
+               /* set the source of the clock to be the PLL */
+               snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+                                   WM8985_CLKSEL_MASK, WM8985_CLKSEL);
+               /* enable the PLL */
+               snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+                                   WM8985_PLLEN_MASK, WM8985_PLLEN);
+       }
        return 0;
 }
 
index b6b65483758518f8b8cab7672af882ef923d2768..c294859511a6f44534e9ba1e0e54e83d51844295 100644 (file)
 #define ADSP1_START_SHIFT                      0  /* DSP1_START */
 #define ADSP1_START_WIDTH                      1  /* DSP1_START */
 
+/*
+ * ADSP1 Control 31
+ */
+#define ADSP1_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
+#define ADSP1_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
+#define ADSP1_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
+
 #define ADSP2_CONTROL  0
 #define ADSP2_CLOCKING 1
 #define ADSP2_STATUS1  4
 #define ADSP2_RAM_RDY_SHIFT                    0
 #define ADSP2_RAM_RDY_WIDTH                    1
 
+#define WM_ADSP_NUM_FW 4
+
+static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
+       "MBC/VSS", "Tx", "Tx Speaker" "Rx ANC"
+};
+
+static struct {
+       const char *file;
+} wm_adsp_fw[WM_ADSP_NUM_FW] = {
+       { .file = "mbc-vss" },
+       { .file = "tx" },
+       { .file = "tx-spk" },
+       { .file = "rx-anc" },
+};
+
+static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
+
+       return 0;
+}
+
+static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+
+       if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
+               return 0;
+
+       if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
+               return -EINVAL;
+
+       if (adsp[e->shift_l].running)
+               return -EBUSY;
+
+       adsp->fw = ucontrol->value.integer.value[0];
+
+       return 0;
+}
+
+static const struct soc_enum wm_adsp_fw_enum[] = {
+       SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+       SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+       SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+       SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+};
+
+const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
+       SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
+                    wm_adsp_fw_get, wm_adsp_fw_put),
+       SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
+                    wm_adsp_fw_get, wm_adsp_fw_put),
+       SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
+                    wm_adsp_fw_get, wm_adsp_fw_put),
+       SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
+                    wm_adsp_fw_get, wm_adsp_fw_put),
+};
+EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
 
 static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
                                                        int type)
@@ -156,6 +229,26 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
        return NULL;
 }
 
+static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
+                                         unsigned int offset)
+{
+       switch (region->type) {
+       case WMFW_ADSP1_PM:
+               return region->base + (offset * 3);
+       case WMFW_ADSP1_DM:
+               return region->base + (offset * 2);
+       case WMFW_ADSP2_XM:
+               return region->base + (offset * 2);
+       case WMFW_ADSP2_YM:
+               return region->base + (offset * 2);
+       case WMFW_ADSP1_ZM:
+               return region->base + (offset * 2);
+       default:
+               WARN_ON(NULL != "Unknown memory region type");
+               return offset;
+       }
+}
+
 static int wm_adsp_load(struct wm_adsp *dsp)
 {
        const struct firmware *firmware;
@@ -178,7 +271,8 @@ static int wm_adsp_load(struct wm_adsp *dsp)
        if (file == NULL)
                return -ENOMEM;
 
-       snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num);
+       snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
+                wm_adsp_fw[dsp->fw].file);
        file[PAGE_SIZE - 1] = '\0';
 
        ret = request_firmware(&firmware, file, dsp->dev);
@@ -283,27 +377,27 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                case WMFW_ADSP1_PM:
                        BUG_ON(!mem);
                        region_name = "PM";
-                       reg = mem->base + (offset * 3);
+                       reg = wm_adsp_region_to_reg(mem, offset);
                        break;
                case WMFW_ADSP1_DM:
                        BUG_ON(!mem);
                        region_name = "DM";
-                       reg = mem->base + (offset * 2);
+                       reg = wm_adsp_region_to_reg(mem, offset);
                        break;
                case WMFW_ADSP2_XM:
                        BUG_ON(!mem);
                        region_name = "XM";
-                       reg = mem->base + (offset * 2);
+                       reg = wm_adsp_region_to_reg(mem, offset);
                        break;
                case WMFW_ADSP2_YM:
                        BUG_ON(!mem);
                        region_name = "YM";
-                       reg = mem->base + (offset * 2);
+                       reg = wm_adsp_region_to_reg(mem, offset);
                        break;
                case WMFW_ADSP1_ZM:
                        BUG_ON(!mem);
                        region_name = "ZM";
-                       reg = mem->base + (offset * 2);
+                       reg = wm_adsp_region_to_reg(mem, offset);
                        break;
                default:
                        adsp_warn(dsp,
@@ -361,12 +455,209 @@ out:
        return ret;
 }
 
+static int wm_adsp_setup_algs(struct wm_adsp *dsp)
+{
+       struct regmap *regmap = dsp->regmap;
+       struct wmfw_adsp1_id_hdr adsp1_id;
+       struct wmfw_adsp2_id_hdr adsp2_id;
+       struct wmfw_adsp1_alg_hdr *adsp1_alg;
+       struct wmfw_adsp2_alg_hdr *adsp2_alg;
+       void *alg, *buf;
+       struct wm_adsp_alg_region *region;
+       const struct wm_adsp_region *mem;
+       unsigned int pos, term;
+       size_t algs, buf_size;
+       __be32 val;
+       int i, ret;
+
+       switch (dsp->type) {
+       case WMFW_ADSP1:
+               mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
+               break;
+       case WMFW_ADSP2:
+               mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
+               break;
+       default:
+               mem = NULL;
+               break;
+       }
+
+       if (mem == NULL) {
+               BUG_ON(mem != NULL);
+               return -EINVAL;
+       }
+
+       switch (dsp->type) {
+       case WMFW_ADSP1:
+               ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
+                                     sizeof(adsp1_id));
+               if (ret != 0) {
+                       adsp_err(dsp, "Failed to read algorithm info: %d\n",
+                                ret);
+                       return ret;
+               }
+
+               buf = &adsp1_id;
+               buf_size = sizeof(adsp1_id);
+
+               algs = be32_to_cpu(adsp1_id.algs);
+               adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+                         be32_to_cpu(adsp1_id.fw.id),
+                         (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
+                         (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
+                         be32_to_cpu(adsp1_id.fw.ver) & 0xff,
+                         algs);
+
+               pos = sizeof(adsp1_id) / 2;
+               term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
+               break;
+
+       case WMFW_ADSP2:
+               ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
+                                     sizeof(adsp2_id));
+               if (ret != 0) {
+                       adsp_err(dsp, "Failed to read algorithm info: %d\n",
+                                ret);
+                       return ret;
+               }
+
+               buf = &adsp2_id;
+               buf_size = sizeof(adsp2_id);
+
+               algs = be32_to_cpu(adsp2_id.algs);
+               adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+                         be32_to_cpu(adsp2_id.fw.id),
+                         (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
+                         (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
+                         be32_to_cpu(adsp2_id.fw.ver) & 0xff,
+                         algs);
+
+               pos = sizeof(adsp2_id) / 2;
+               term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
+               break;
+
+       default:
+               BUG_ON(NULL == "Unknown DSP type");
+               return -EINVAL;
+       }
+
+       if (algs == 0) {
+               adsp_err(dsp, "No algorithms\n");
+               return -EINVAL;
+       }
+
+       if (algs > 1024) {
+               adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
+               print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
+                                    buf, buf_size);
+               return -EINVAL;
+       }
+
+       /* Read the terminator first to validate the length */
+       ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
+       if (ret != 0) {
+               adsp_err(dsp, "Failed to read algorithm list end: %d\n",
+                       ret);
+               return ret;
+       }
+
+       if (be32_to_cpu(val) != 0xbedead)
+               adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
+                         term, be32_to_cpu(val));
+
+       alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
+       if (!alg)
+               return -ENOMEM;
+
+       ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
+       if (ret != 0) {
+               adsp_err(dsp, "Failed to read algorithm list: %d\n",
+                       ret);
+               goto out;
+       }
+
+       adsp1_alg = alg;
+       adsp2_alg = alg;
+
+       for (i = 0; i < algs; i++) {
+               switch (dsp->type) {
+               case WMFW_ADSP1:
+                       adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
+                                 i, be32_to_cpu(adsp1_alg[i].alg.id),
+                                 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
+                                 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
+                                 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
+                                 be32_to_cpu(adsp1_alg[i].dm),
+                                 be32_to_cpu(adsp1_alg[i].zm));
+
+                       region = kzalloc(sizeof(*region), GFP_KERNEL);
+                       if (!region)
+                               return -ENOMEM;
+                       region->type = WMFW_ADSP1_DM;
+                       region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
+                       region->base = be32_to_cpu(adsp1_alg[i].dm);
+                       list_add_tail(&region->list, &dsp->alg_regions);
+
+                       region = kzalloc(sizeof(*region), GFP_KERNEL);
+                       if (!region)
+                               return -ENOMEM;
+                       region->type = WMFW_ADSP1_ZM;
+                       region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
+                       region->base = be32_to_cpu(adsp1_alg[i].zm);
+                       list_add_tail(&region->list, &dsp->alg_regions);
+                       break;
+
+               case WMFW_ADSP2:
+                       adsp_info(dsp,
+                                 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
+                                 i, be32_to_cpu(adsp2_alg[i].alg.id),
+                                 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
+                                 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
+                                 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
+                                 be32_to_cpu(adsp2_alg[i].xm),
+                                 be32_to_cpu(adsp2_alg[i].ym),
+                                 be32_to_cpu(adsp2_alg[i].zm));
+
+                       region = kzalloc(sizeof(*region), GFP_KERNEL);
+                       if (!region)
+                               return -ENOMEM;
+                       region->type = WMFW_ADSP2_XM;
+                       region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+                       region->base = be32_to_cpu(adsp2_alg[i].xm);
+                       list_add_tail(&region->list, &dsp->alg_regions);
+
+                       region = kzalloc(sizeof(*region), GFP_KERNEL);
+                       if (!region)
+                               return -ENOMEM;
+                       region->type = WMFW_ADSP2_YM;
+                       region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+                       region->base = be32_to_cpu(adsp2_alg[i].ym);
+                       list_add_tail(&region->list, &dsp->alg_regions);
+
+                       region = kzalloc(sizeof(*region), GFP_KERNEL);
+                       if (!region)
+                               return -ENOMEM;
+                       region->type = WMFW_ADSP2_ZM;
+                       region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+                       region->base = be32_to_cpu(adsp2_alg[i].zm);
+                       list_add_tail(&region->list, &dsp->alg_regions);
+                       break;
+               }
+       }
+
+out:
+       kfree(alg);
+       return ret;
+}
+
 static int wm_adsp_load_coeff(struct wm_adsp *dsp)
 {
        struct regmap *regmap = dsp->regmap;
        struct wmfw_coeff_hdr *hdr;
        struct wmfw_coeff_item *blk;
        const struct firmware *firmware;
+       const struct wm_adsp_region *mem;
+       struct wm_adsp_alg_region *alg_region;
        const char *region_name;
        int ret, pos, blocks, type, offset, reg;
        char *file;
@@ -376,7 +667,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
        if (file == NULL)
                return -ENOMEM;
 
-       snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num);
+       snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
+                wm_adsp_fw[dsp->fw].file);
        file[PAGE_SIZE - 1] = '\0';
 
        ret = request_firmware(&firmware, file, dsp->dev);
@@ -399,6 +691,16 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                goto out_fw;
        }
 
+       switch (be32_to_cpu(hdr->rev) & 0xff) {
+       case 1:
+               break;
+       default:
+               adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
+                        file, be32_to_cpu(hdr->rev) & 0xff);
+               ret = -EINVAL;
+               goto out_fw;
+       }
+
        adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
                (le32_to_cpu(hdr->ver) >> 16) & 0xff,
                (le32_to_cpu(hdr->ver) >>  8) & 0xff,
@@ -411,8 +713,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
               pos - firmware->size > sizeof(*blk)) {
                blk = (void*)(&firmware->data[pos]);
 
-               type = be32_to_cpu(blk->type) & 0xff;
-               offset = le32_to_cpu(blk->offset) & 0xffffff;
+               type = le16_to_cpu(blk->type);
+               offset = le16_to_cpu(blk->offset);
 
                adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
                         file, blocks, le32_to_cpu(blk->id),
@@ -425,15 +727,48 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                reg = 0;
                region_name = "Unknown";
                switch (type) {
-               case WMFW_NAME_TEXT:
-               case WMFW_INFO_TEXT:
+               case (WMFW_NAME_TEXT << 8):
+               case (WMFW_INFO_TEXT << 8):
                        break;
-               case WMFW_ABSOLUTE:
+               case (WMFW_ABSOLUTE << 8):
                        region_name = "register";
                        reg = offset;
                        break;
+
+               case WMFW_ADSP1_DM:
+               case WMFW_ADSP1_ZM:
+               case WMFW_ADSP2_XM:
+               case WMFW_ADSP2_YM:
+                       adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
+                                file, blocks, le32_to_cpu(blk->len),
+                                type, le32_to_cpu(blk->id));
+
+                       mem = wm_adsp_find_region(dsp, type);
+                       if (!mem) {
+                               adsp_err(dsp, "No base for region %x\n", type);
+                               break;
+                       }
+
+                       reg = 0;
+                       list_for_each_entry(alg_region,
+                                           &dsp->alg_regions, list) {
+                               if (le32_to_cpu(blk->id) == alg_region->alg &&
+                                   type == alg_region->type) {
+                                       reg = alg_region->base;
+                                       reg = wm_adsp_region_to_reg(mem,
+                                                                   reg);
+                                       reg += offset;
+                               }
+                       }
+
+                       if (reg == 0)
+                               adsp_err(dsp, "No %x for algorithm %x\n",
+                                        type, le32_to_cpu(blk->id));
+                       break;
+
                default:
-                       adsp_err(dsp, "Unknown region type %x\n", type);
+                       adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
+                                file, blocks, type, pos);
                        break;
                }
 
@@ -445,6 +780,9 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                                return -ENOMEM;
                        }
 
+                       adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
+                                file, blocks, le32_to_cpu(blk->len),
+                                reg);
                        ret = regmap_raw_write(regmap, reg, blk->data,
                                               le32_to_cpu(blk->len));
                        if (ret != 0) {
@@ -471,6 +809,14 @@ out:
        return 0;
 }
 
+int wm_adsp1_init(struct wm_adsp *adsp)
+{
+       INIT_LIST_HEAD(&adsp->alg_regions);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp1_init);
+
 int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                   struct snd_kcontrol *kcontrol,
                   int event)
@@ -479,16 +825,46 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
        struct wm_adsp *dsp = &dsps[w->shift];
        int ret;
+       int val;
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
                regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
                                   ADSP1_SYS_ENA, ADSP1_SYS_ENA);
 
+               /*
+                * For simplicity set the DSP clock rate to be the
+                * SYSCLK rate rather than making it configurable.
+                */
+               if(dsp->sysclk_reg) {
+                       ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
+                       if (ret != 0) {
+                               adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
+                               ret);
+                               return ret;
+                       }
+
+                       val = (val & dsp->sysclk_mask)
+                               >> dsp->sysclk_shift;
+
+                       ret = regmap_update_bits(dsp->regmap,
+                                                dsp->base + ADSP1_CONTROL_31,
+                                                ADSP1_CLK_SEL_MASK, val);
+                       if (ret != 0) {
+                               adsp_err(dsp, "Failed to set clock rate: %d\n",
+                                        ret);
+                               return ret;
+                       }
+               }
+
                ret = wm_adsp_load(dsp);
                if (ret != 0)
                        goto err;
 
+               ret = wm_adsp_setup_algs(dsp);
+               if (ret != 0)
+                       goto err;
+
                ret = wm_adsp_load_coeff(dsp);
                if (ret != 0)
                        goto err;
@@ -560,6 +936,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
        struct snd_soc_codec *codec = w->codec;
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
        struct wm_adsp *dsp = &dsps[w->shift];
+       struct wm_adsp_alg_region *alg_region;
        unsigned int val;
        int ret;
 
@@ -625,6 +1002,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                if (ret != 0)
                        goto err;
 
+               ret = wm_adsp_setup_algs(dsp);
+               if (ret != 0)
+                       goto err;
+
                ret = wm_adsp_load_coeff(dsp);
                if (ret != 0)
                        goto err;
@@ -635,9 +1016,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                                         ADSP2_CORE_ENA | ADSP2_START);
                if (ret != 0)
                        goto err;
+
+               dsp->running = true;
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
+               dsp->running = false;
+
                regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
                                   ADSP2_SYS_ENA | ADSP2_CORE_ENA |
                                   ADSP2_START, 0);
@@ -656,6 +1041,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                                        "Failed to enable supply: %d\n",
                                        ret);
                }
+
+               while (!list_empty(&dsp->alg_regions)) {
+                       alg_region = list_first_entry(&dsp->alg_regions,
+                                                     struct wm_adsp_alg_region,
+                                                     list);
+                       list_del(&alg_region->list);
+                       kfree(alg_region);
+               }
                break;
 
        default:
@@ -685,6 +1078,8 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
                return ret;
        }
 
+       INIT_LIST_HEAD(&adsp->alg_regions);
+
        if (dvfs) {
                adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
                if (IS_ERR(adsp->dvfs)) {
index ffd29a4609e2a50b1214fc5389a73906973d9538..cb8871a3ec0015854c6d13bebb1055661b7cf4be 100644 (file)
@@ -25,6 +25,13 @@ struct wm_adsp_region {
        unsigned int base;
 };
 
+struct wm_adsp_alg_region {
+       struct list_head list;
+       unsigned int alg;
+       int type;
+       unsigned int base;
+};
+
 struct wm_adsp {
        const char *part;
        int num;
@@ -33,10 +40,18 @@ struct wm_adsp {
        struct regmap *regmap;
 
        int base;
+       int sysclk_reg;
+       int sysclk_mask;
+       int sysclk_shift;
+
+       struct list_head alg_regions;
 
        const struct wm_adsp_region *mem;
        int num_mems;
 
+       int fw;
+       bool running;
+
        struct regulator *dvfs;
 };
 
@@ -50,6 +65,9 @@ struct wm_adsp {
        .shift = num, .event = wm_adsp2_event, \
        .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
 
+extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
+
+int wm_adsp1_init(struct wm_adsp *adsp);
 int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs);
 int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                   struct snd_kcontrol *kcontrol, int event);
index 5632ded67fdd1bee57cb66ebbbff3c2de3289991..ef163360a7459ebd7a8a70af00118cbcdb95717e 100644 (file)
@@ -93,15 +93,20 @@ struct wmfw_adsp2_alg_hdr {
 struct wmfw_coeff_hdr {
        u8 magic[4];
        __le32 len;
-       __le32 ver;
+       union {
+               __be32 rev;
+               __le32 ver;
+       };
+       union {
+               __be32 core;
+               __le32 core_ver;
+       };
        u8 data[];
 } __packed;
 
 struct wmfw_coeff_item {
-       union {
-               __be32 type;
-               __le32 offset;
-       };
+       __le16 offset;
+       __le16 type;
        __le32 id;
        __le32 ver;
        __le32 sr;
index d55e6477bff0c9a855379d26db50538ff7b5e178..591f547dd394a84b8d4a7ff05be2166a85f4a209 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/platform_data/edma.h>
 #include <linux/i2c.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
index afab81f844ae82c32418d6d1f83301c2d29d1f4e..9bdd71b881e20092d3cfa7247d29d11273b4e285 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/kernel.h>
 #include <linux/genalloc.h>
+#include <linux/platform_data/edma.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
index b6ef7039dd097b4ac99c6e9b14a0c35ed59fd203..fbb710c76c083ef90b9c442e1776ec00fcb8aa08 100644 (file)
@@ -14,7 +14,7 @@
 
 #include <linux/genalloc.h>
 #include <linux/platform_data/davinci_asp.h>
-#include <mach/edma.h>
+#include <linux/platform_data/edma.h>
 
 struct davinci_pcm_dma_params {
        int channel;                    /* sync dma channel ID */
index 5be65aae7e0e538b552f9f4bf2bb99c9ed0ad3f8..a45af6487830d0836ffef27e57d511e85d941823 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/platform_data/edma.h>
 #include <linux/gpio.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <asm/plat-sffsdr/sffsdr-fpga.h>
 #endif
 
-#include <mach/edma.h>
 
 #include "../codecs/pcm3008.h"
 #include "davinci-pcm.h"
 #include "davinci-i2s.h"
 
+#define DAVINCI_DMA_MCBSP_TX   2
+#define DAVINCI_DMA_MCBSP_RX   3
+
 /*
  * CLKX and CLKR are the inputs for the Sample Rate Generator.
  * FSX and FSR are outputs, driven by the sample Rate Generator.
@@ -124,7 +127,7 @@ static struct resource sffsdr_snd_resources[] = {
 
 static struct evm_snd_platform_data sffsdr_snd_data = {
        .tx_dma_ch      = DAVINCI_DMA_MCBSP_TX,
-       .rx_dma_ch      = DAVINCI_DMA_MCBSP_RX,
+       .rx_dma_ch      = DAVINCI_DMA_MCBAP_RX,
 };
 
 static struct platform_device *sffsdr_snd_device;
index 1aa51300c564b3d7b6e3c4a55eb61268d5e1f9b0..deb30d59965e770a9c8096f5c92b1767f51f96cf 100644 (file)
@@ -210,15 +210,19 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
        switch (config->chan_nr) {
        case EIGHT_CHANNEL_SUPPORT:
                ch_reg = 3;
+               break;
        case SIX_CHANNEL_SUPPORT:
                ch_reg = 2;
+               break;
        case FOUR_CHANNEL_SUPPORT:
                ch_reg = 1;
+               break;
        case TWO_CHANNEL_SUPPORT:
                ch_reg = 0;
                break;
        default:
                dev_err(dev->dev, "channel not supported\n");
+               return -EINVAL;
        }
 
        i2s_disable_channels(dev, substream->stream);
index b4b4cab30232ad2a6c712cf35c84521872d7ebfa..6cf8355a8542656c89dda5f1564c721d3f2cb4aa 100644 (file)
 #define asoc_simple_get_card_info(p) \
        container_of(p->dai_link, struct asoc_simple_card_info, snd_link)
 
+static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
+                                      struct asoc_simple_dai *set,
+                                      unsigned int daifmt)
+{
+       int ret = 0;
+
+       daifmt |= set->fmt;
+
+       if (!ret && daifmt)
+               ret = snd_soc_dai_set_fmt(dai, daifmt);
+
+       if (!ret && set->sysclk)
+               ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
+
+       return ret;
+}
+
 static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct asoc_simple_card_info *cinfo = asoc_simple_get_card_info(rtd);
-       struct asoc_simple_dai_init_info *iinfo = cinfo->init;
+       struct asoc_simple_card_info *info = asoc_simple_get_card_info(rtd);
        struct snd_soc_dai *codec = rtd->codec_dai;
        struct snd_soc_dai *cpu = rtd->cpu_dai;
-       unsigned int cpu_daifmt = iinfo->fmt | iinfo->cpu_daifmt;
-       unsigned int codec_daifmt = iinfo->fmt | iinfo->codec_daifmt;
+       unsigned int daifmt = info->daifmt;
        int ret;
 
-       if (codec_daifmt) {
-               ret = snd_soc_dai_set_fmt(codec, codec_daifmt);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (iinfo->sysclk) {
-               ret = snd_soc_dai_set_sysclk(codec, 0, iinfo->sysclk, 0);
-               if (ret < 0)
-                       return ret;
-       }
+       ret = __asoc_simple_card_dai_init(codec, &info->codec_dai, daifmt);
+       if (ret < 0)
+               return ret;
 
-       if (cpu_daifmt) {
-               ret = snd_soc_dai_set_fmt(cpu, cpu_daifmt);
-               if (ret < 0)
-                       return ret;
-       }
+       ret = __asoc_simple_card_dai_init(cpu, &info->cpu_dai, daifmt);
+       if (ret < 0)
+               return ret;
 
        return 0;
 }
@@ -50,19 +55,20 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 static int asoc_simple_card_probe(struct platform_device *pdev)
 {
        struct asoc_simple_card_info *cinfo = pdev->dev.platform_data;
+       struct device *dev = &pdev->dev;
 
        if (!cinfo) {
-               dev_err(&pdev->dev, "no info for asoc-simple-card\n");
+               dev_err(dev, "no info for asoc-simple-card\n");
                return -EINVAL;
        }
 
        if (!cinfo->name        ||
            !cinfo->card        ||
-           !cinfo->cpu_dai     ||
            !cinfo->codec       ||
            !cinfo->platform    ||
-           !cinfo->codec_dai) {
-               dev_err(&pdev->dev, "insufficient asoc_simple_card_info settings\n");
+           !cinfo->cpu_dai.name ||
+           !cinfo->codec_dai.name) {
+               dev_err(dev, "insufficient asoc_simple_card_info settings\n");
                return -EINVAL;
        }
 
@@ -71,14 +77,11 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
         */
        cinfo->snd_link.name            = cinfo->name;
        cinfo->snd_link.stream_name     = cinfo->name;
-       cinfo->snd_link.cpu_dai_name    = cinfo->cpu_dai;
+       cinfo->snd_link.cpu_dai_name    = cinfo->cpu_dai.name;
        cinfo->snd_link.platform_name   = cinfo->platform;
        cinfo->snd_link.codec_name      = cinfo->codec;
-       cinfo->snd_link.codec_dai_name  = cinfo->codec_dai;
-
-       /* enable snd_link.init if cinfo has settings */
-       if (cinfo->init)
-               cinfo->snd_link.init    = asoc_simple_card_dai_init;
+       cinfo->snd_link.codec_dai_name  = cinfo->codec_dai.name;
+       cinfo->snd_link.init            = asoc_simple_card_dai_init;
 
        /*
         * init snd_soc_card
index 365d9d27a3216b2c8969451c83b0e49e0bbc9520..e70e6c844f96b29ca631e69841aea53b51886d00 100644 (file)
@@ -32,7 +32,6 @@
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
-#include <sound/saif.h>
 #include <asm/mach-types.h>
 #include <mach/hardware.h>
 #include <mach/mxs.h>
@@ -662,46 +661,40 @@ static int mxs_saif_probe(struct platform_device *pdev)
        struct device_node *np = pdev->dev.of_node;
        struct resource *iores, *dmares;
        struct mxs_saif *saif;
-       struct mxs_saif_platform_data *pdata;
        struct pinctrl *pinctrl;
        int ret = 0;
+       struct device_node *master;
 
-
-       if (!np && pdev->id >= ARRAY_SIZE(mxs_saif))
+       if (!np)
                return -EINVAL;
 
        saif = devm_kzalloc(&pdev->dev, sizeof(*saif), GFP_KERNEL);
        if (!saif)
                return -ENOMEM;
 
-       if (np) {
-               struct device_node *master;
-               saif->id = of_alias_get_id(np, "saif");
-               if (saif->id < 0)
-                       return saif->id;
-               /*
-                * If there is no "fsl,saif-master" phandle, it's a saif
-                * master.  Otherwise, it's a slave and its phandle points
-                * to the master.
-                */
-               master = of_parse_phandle(np, "fsl,saif-master", 0);
-               if (!master) {
-                       saif->master_id = saif->id;
-               } else {
-                       saif->master_id = of_alias_get_id(master, "saif");
-                       if (saif->master_id < 0)
-                               return saif->master_id;
-               }
+       ret = of_alias_get_id(np, "saif");
+       if (ret < 0)
+               return ret;
+       else
+               saif->id = ret;
+
+       /*
+        * If there is no "fsl,saif-master" phandle, it's a saif
+        * master.  Otherwise, it's a slave and its phandle points
+        * to the master.
+        */
+       master = of_parse_phandle(np, "fsl,saif-master", 0);
+       if (!master) {
+               saif->master_id = saif->id;
        } else {
-               saif->id = pdev->id;
-               pdata = pdev->dev.platform_data;
-               if (pdata && !pdata->master_mode)
-                       saif->master_id = pdata->master_id;
+               ret = of_alias_get_id(master, "saif");
+               if (ret < 0)
+                       return ret;
                else
-                       saif->master_id = saif->id;
+                       saif->master_id = ret;
        }
 
-       if (saif->master_id < 0 || saif->master_id >= ARRAY_SIZE(mxs_saif)) {
+       if (saif->master_id >= ARRAY_SIZE(mxs_saif)) {
                dev_err(&pdev->dev, "get wrong master id\n");
                return -EINVAL;
        }
index 7048137f9a337340e0e0a78a7a55859220718538..3c9887f4597d9689da6e7eadbeb321729d518f58 100644 (file)
@@ -6,6 +6,10 @@ config SND_OMAP_SOC
 config SND_OMAP_SOC_DMIC
        tristate
 
+config SND_OMAP_SOC_MCASP
+       tristate
+       select SND_SOC_SPDIF
+
 config SND_OMAP_SOC_MCBSP
        tristate
 
@@ -15,6 +19,9 @@ config SND_OMAP_SOC_MCPDM
 config SND_OMAP_SOC_HDMI
        tristate
 
+config SND_OMAP_SOC_ABE
+       tristate
+
 config SND_OMAP_SOC_N810
        tristate "SoC Audio support for Nokia N810"
        depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
@@ -70,15 +77,6 @@ config SND_OMAP_SOC_AM3517EVM
          Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517
          EVM.
 
-config SND_OMAP_SOC_SDP3430
-       tristate "SoC Audio support for Texas Instruments SDP3430"
-       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
-       select SND_OMAP_SOC_MCBSP
-       select SND_SOC_TWL4030
-       help
-         Say Y if you want to add support for SoC audio on Texas Instruments
-         SDP3430.
-
 config SND_OMAP_SOC_OMAP_TWL4030
        tristate "SoC Audio support for TI SoC based boards with twl4030 codec"
        depends on TWL4030_CORE && SND_OMAP_SOC
@@ -91,6 +89,8 @@ config SND_OMAP_SOC_OMAP_TWL4030
          - Gumstix Overo or CompuLab CM-T35/CM-T3730
          - IGEP v2
          - OMAP3EVM
+         - SDP3430
+         - Zoom2
 
 config SND_OMAP_SOC_OMAP_ABE_TWL6040
        tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
@@ -99,6 +99,11 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040
        select SND_OMAP_SOC_MCPDM
        select SND_SOC_TWL6040
        select SND_SOC_DMIC
+       select SND_OMAP_SOC_MCASP
+       select SND_OMAP_SOC_MCBSP
+       select SND_OMAP_SOC_ABE
+       select SND_DYNAMIC_MINORS
+
        help
          Say Y if you want to add support for SoC audio on OMAP boards using
          ABE and twl6040 codec. This driver currently supports:
@@ -108,13 +113,14 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040
 
 config SND_OMAP_SOC_OMAP_HDMI
        tristate "SoC Audio support for Texas Instruments OMAP HDMI"
-       depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS
+       depends on SND_OMAP_SOC && (OMAP4_DSS_HDMI || OMAP5_DSS_HDMI) && OMAP2_DSS
        select SND_OMAP_SOC_HDMI
        select SND_SOC_OMAP_HDMI_CODEC
-       select OMAP4_DSS_HDMI_AUDIO
+       select OMAP4_DSS_HDMI_AUDIO if OMAP4_DSS_HDMI
+       select OMAP5_DSS_HDMI_AUDIO if OMAP5_DSS_HDMI
        help
          Say Y if you want to add support for SoC HDMI audio on Texas Instruments
-         OMAP4 chips
+         OMAP chips
 
 config SND_OMAP_SOC_OMAP3_PANDORA
        tristate "SoC Audio support for OMAP3 Pandora"
@@ -123,11 +129,3 @@ config SND_OMAP_SOC_OMAP3_PANDORA
        select SND_SOC_TWL4030
        help
          Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
-
-config SND_OMAP_SOC_ZOOM2
-       tristate "SoC Audio support for Zoom2"
-       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_ZOOM2
-       select SND_OMAP_SOC_MCBSP
-       select SND_SOC_TWL4030
-       help
-         Say Y if you want to add support for Soc audio on Zoom2 board.
index 19637e55ea48c7ac78e5fa90cef1916b0f1f7d3d..4e89a7fd3e8f5b7a83750c8a0f5492a55c7b30f9 100644 (file)
@@ -1,15 +1,21 @@
 # OMAP Platform Support
 snd-soc-omap-objs := omap-pcm.o
 snd-soc-omap-dmic-objs := omap-dmic.o
+snd-soc-omap-mcasp-objs := omap-mcasp.o
 snd-soc-omap-mcbsp-objs := omap-mcbsp.o mcbsp.o
 snd-soc-omap-mcpdm-objs := omap-mcpdm.o
 snd-soc-omap-hdmi-objs := omap-hdmi.o
+snd-soc-omap-abe-objs := omap-abe-core.o omap-abe-dbg.o omap-abe-mixer.o \
+                       omap-abe-mmap.o omap-abe-opp.o omap-abe-pcm.o \
+                       omap-abe-pm.o
 
 obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
 obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o
+obj-$(CONFIG_SND_OMAP_SOC_MCASP) += snd-soc-omap-mcasp.o
 obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
 obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
 obj-$(CONFIG_SND_OMAP_SOC_HDMI) += snd-soc-omap-hdmi.o
+obj-$(CONFIG_SND_OMAP_SOC_ABE) += snd-soc-omap-abe.o aess/
 
 # OMAP Machine Support
 snd-soc-n810-objs := n810.o
@@ -17,11 +23,9 @@ snd-soc-rx51-objs := rx51.o
 snd-soc-ams-delta-objs := ams-delta.o
 snd-soc-osk5912-objs := osk5912.o
 snd-soc-am3517evm-objs := am3517evm.o
-snd-soc-sdp3430-objs := sdp3430.o
 snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o
 snd-soc-omap-twl4030-objs := omap-twl4030.o
 snd-soc-omap3pandora-objs := omap3pandora.o
-snd-soc-zoom2-objs := zoom2.o
 snd-soc-omap-hdmi-card-objs := omap-hdmi-card.o
 
 obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
@@ -30,9 +34,7 @@ obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
 obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP2EVM) += snd-soc-omap2evm.o
 obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o
-obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
-obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP_HDMI) += snd-soc-omap-hdmi-card.o
diff --git a/sound/soc/omap/aess/Makefile b/sound/soc/omap/aess/Makefile
new file mode 100644 (file)
index 0000000..cb0b4e8
--- /dev/null
@@ -0,0 +1,10 @@
+snd-soc-abe-hal-objs += abe_aess.o \
+                       abe_asrc.o \
+                       abe_core.o \
+                       abe_ini.o \
+                       abe_gain.o \
+                       abe_port.o \
+                       abe_seq.o \
+                       port_mgr.o \
+
+obj-$(CONFIG_SND_OMAP_SOC_ABE) += snd-soc-abe-hal.o
diff --git a/sound/soc/omap/aess/abe.h b/sound/soc/omap/aess/abe.h
new file mode 100644 (file)
index 0000000..0a25f6c
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _ABE_H_
+#define _ABE_H_
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include "abe_def.h"
+#include "abe_typ.h"
+#include "abe_ext.h"
+
+#include <linux/debugfs.h>
+
+/*
+ * OS DEPENDENT MMU CONFIGURATION
+ */
+#define ABE_DMEM_BASE_OFFSET_MPU       0x80000
+#define ABE_ATC_BASE_OFFSET_MPU                0xF1000
+
+/* default base address for io_base */
+#define ABE_DEFAULT_BASE_ADDRESS_L3 0x49000000L
+#define ABE_DEFAULT_BASE_ADDRESS_L4 0x40100000L
+#define ABE_DEFAULT_BASE_ADDRESS_DEFAULT ABE_DEFAULT_BASE_ADDRESS_L3
+
+/*
+ * TODO: These structures, enums and port ID macros should be moved to the
+ * new public ABE API header.
+ */
+
+/* Logical PORT IDs - Backend */
+#define OMAP_ABE_BE_PORT_DMIC0                 0
+#define OMAP_ABE_BE_PORT_DMIC1                 1
+#define OMAP_ABE_BE_PORT_DMIC2                 2
+#define OMAP_ABE_BE_PORT_PDM_DL1               3
+#define OMAP_ABE_BE_PORT_PDM_DL2               4
+#define OMAP_ABE_BE_PORT_MCASP                 5
+#define OMAP_ABE_BE_PORT_PDM_UL1               6
+#define OMAP_ABE_BE_PORT_BT_VX_DL              7
+#define OMAP_ABE_BE_PORT_BT_VX_UL              8
+#define OMAP_ABE_BE_PORT_MM_EXT_UL             9
+#define OMAP_ABE_BE_PORT_MM_EXT_DL             10
+
+/* Logical PORT IDs - Frontend */
+#define OMAP_ABE_FE_PORT_MM_DL1                11
+#define OMAP_ABE_FE_PORT_MM_UL1                12
+#define OMAP_ABE_FE_PORT_MM_UL2                13
+#define OMAP_ABE_FE_PORT_VX_DL         14
+#define OMAP_ABE_FE_PORT_VX_UL         15
+#define OMAP_ABE_FE_PORT_TONES         16
+#define OMAP_ABE_FE_PORT_MM_DL_LP      17
+
+#define OMAP_ABE_MAX_PORT_ID   OMAP_ABE_FE_PORT_MM_DL_LP
+
+/* ABE copy function IDs */
+#define OMAP_AESS_COPY_FCT_NULL_ID                     0
+#define OMAP_AESS_COPY_FCT_S2D_STEREO_16_16_ID         1
+#define OMAP_AESS_COPY_FCT_S2D_MONO_MSB_ID             2
+#define OMAP_AESS_COPY_FCT_S2D_STEREO_MSB_ID           3
+#define OMAP_AESS_COPY_FCT_S2D_STEREO_RSHIFTED_16_ID   4
+#define OMAP_AESS_COPY_FCT_S2D_MONO_RSHIFTED_16_ID     5
+#define OMAP_AESS_COPY_FCT_D2S_STEREO_16_16_ID         6
+#define OMAP_AESS_COPY_FCT_D2S_MONO_MSB_ID             7
+#define OMAP_AESS_COPY_FCT_D2S_MONO_RSHIFTED_16_ID     8
+#define OMAP_AESS_COPY_FCT_D2S_STEREO_RSHIFTED_16_ID   9
+#define OMAP_AESS_COPY_FCT_D2S_STEREO_MSB_ID           10
+#define OMAP_AESS_COPY_FCT_DMIC_ID                     11
+#define OMAP_AESS_COPY_FCT_MCPDM_DL_ID                 12
+#define OMAP_AESS_COPY_FCT_MM_UL_ID                    13
+#define OMAP_AESS_COPY_FCT_SPLIT_SMEM_ID               14
+#define OMAP_AESS_COPY_FCT_MERGE_SMEM_ID               15
+#define OMAP_AESS_COPY_FCT_SPLIT_TDM_ID                        16
+#define OMAP_AESS_COPY_FCT_MERGE_TDM_ID                        17
+#define OMAP_AESS_COPY_FCT_ROUTE_MM_UL_ID              18
+#define OMAP_AESS_COPY_FCT_IO_IP_ID                    19
+#define OMAP_AESS_COPY_FCT_COPY_UNDERFLOW_ID           20
+#define OMAP_AESS_COPY_FCT_COPY_MCPDM_DL_HF_PDL1_ID    21
+#define OMAP_AESS_COPY_FCT_COPY_MCPDM_DL_HF_PDL2_ID    22
+#define OMAP_AESS_COPY_FCT_S2D_MONO_16_16_ID           23
+#define OMAP_AESS_COPY_FCT_D2S_MONO_16_16_ID           24
+#define OMAP_AESS_COPY_FCT_DMIC_NO_PRESCALE_ID         25
+
+/* ABE buffer IDs */
+#define OMAP_AESS_BUFFER_ZERO_ID               0
+#define OMAP_AESS_BUFFER_DMIC1_L_ID            1
+#define OMAP_AESS_BUFFER_DMIC1_R_ID            2
+#define OMAP_AESS_BUFFER_DMIC2_L_ID            3
+#define OMAP_AESS_BUFFER_DMIC2_R_ID            4
+#define OMAP_AESS_BUFFER_DMIC3_L_ID            5
+#define OMAP_AESS_BUFFER_DMIC3_R_ID            6
+#define OMAP_AESS_BUFFER_BT_UL_L_ID            7
+#define OMAP_AESS_BUFFER_BT_UL_R_ID            8
+#define OMAP_AESS_BUFFER_MM_EXT_IN_L_ID                9
+#define OMAP_AESS_BUFFER_MM_EXT_IN_R_ID                10
+#define OMAP_AESS_BUFFER_AMIC_L_ID             11
+#define OMAP_AESS_BUFFER_AMIC_R_ID             12
+#define OMAP_AESS_BUFFER_VX_REC_L_ID           13
+#define OMAP_AESS_BUFFER_VX_REC_R_ID           14
+#define OMAP_AESS_BUFFER_MCU_IRQ_FIFO_PTR_ID   15
+#define OMAP_AESS_BUFFER_DMIC_ATC_PTR_ID       16
+#define OMAP_AESS_BUFFER_MM_EXT_IN_ID          17
+
+
+#define OMAP_ABE_D_MCUIRQFIFO_SIZE     0x40
+
+/* ports can either be enabled or disabled */
+enum port_state {
+       PORT_DISABLED = 0,
+       PORT_ENABLED,
+};
+
+struct omap_aess_mapping {
+       struct omap_aess_addr *map;
+       int *fct_id;
+       int *label_id;
+       struct omap_aess_init_task *init_table;
+       struct omap_aess_port *port;
+       struct omap_aess_port *ping_pong;
+       struct omap_aess_task *dl1_mono_mixer;
+       struct omap_aess_task *dl2_mono_mixer;
+       struct omap_aess_task *audul_mono_mixer;
+       int *asrc;
+};
+
+/* structure used for client port info */
+struct omap_abe_port {
+
+       /* logical and physical port IDs that correspond this port */
+       int logical_id;
+       int physical_id;
+       int physical_users;
+
+       /* enabled or disabled */
+       enum port_state state;
+
+       /* logical port ref count */
+       int users;
+
+       struct list_head list;
+       struct omap_aess *abe;
+
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs_lstate;
+       struct dentry *debugfs_lphy;
+       struct dentry *debugfs_lusers;
+#endif
+};
+
+struct omap_abe_port *omap_abe_port_open(struct omap_aess *abe, int logical_id);
+void omap_abe_port_close(struct omap_aess *abe, struct omap_abe_port *port);
+int omap_abe_port_enable(struct omap_aess *abe, struct omap_abe_port *port);
+int omap_abe_port_disable(struct omap_aess *abe, struct omap_abe_port *port);
+int omap_abe_port_is_enabled(struct omap_aess *abe, struct omap_abe_port *port);
+struct omap_aess *omap_abe_port_mgr_get(void);
+void omap_abe_port_mgr_put(struct omap_aess *abe);
+
+struct omap_aess_seq {
+       u32 write_pointer;
+       u32 irq_pingpong_player_id;
+};
+
+/* main ABE structure */
+struct omap_aess {
+       struct device *dev;
+       void __iomem *io_base[5];
+       u32 firmware_version_number;
+       u16 MultiFrame[25][8];
+       u32 compensated_mixer_gain;
+       u8  muted_gains_indicator[MAX_NBGAIN_CMEM];
+       u32 desired_gains_decibel[MAX_NBGAIN_CMEM];
+       u32 muted_gains_decibel[MAX_NBGAIN_CMEM];
+       u32 desired_gains_linear[MAX_NBGAIN_CMEM];
+       u32 desired_ramp_delay_ms[MAX_NBGAIN_CMEM];
+       int pp_buf_id;
+       int pp_buf_id_next;
+       int pp_buf_addr[4];
+       int pp_first_irq;
+       struct mutex mutex;
+
+       /* base addresses of the ping pong buffers in bytes addresses */
+       u32 base_address_pingpong[MAX_PINGPONG_BUFFERS];
+       /* size of each ping/pong buffers */
+       u32 size_pingpong;
+       /* number of ping/pong buffer being used */
+       u32 nb_pingpong;
+       struct snd_pcm_substream *substream_pp;
+
+       u32 irq_dbg_read_ptr;
+       struct omap_aess_seq seq;
+       struct omap_aess_mapping *fw_info;
+
+       /* List of open ABE logical ports */
+       struct list_head ports;
+
+       /* spinlock */
+       spinlock_t lock;
+
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs_root;
+#endif
+};
+
+#include "abe_gain.h"
+
+struct omap_aess_equ {
+       /* type of filter */
+       u32 equ_type;
+       /* filter length */
+       u32 equ_length;
+       union {
+               /* parameters are the direct and recursive coefficients in */
+               /* Q6.26 integer fixed-point format. */
+               s32 type1[NBEQ1];
+               struct {
+                       /* center frequency of the band [Hz] */
+                       s32 freq[NBEQ2];
+                       /* gain of each band. [dB] */
+                       s32 gain[NBEQ2];
+                       /* Q factor of this band [dB] */
+                       s32 q[NBEQ2];
+               } type2;
+       } coef;
+       s32 equ_param3;
+};
+
+
+struct omap_aess_dma {
+       /* OCP L3 pointer to the first address of the */
+       void *data;
+       /* destination buffer (either DMA or Ping-Pong read/write pointers). */
+       /* address L3 when addressing the DMEM buffer instead of CBPr */
+       void *l3_dmem;
+       /* address L3 translated to L4 the ARM memory space */
+       void *l4_dmem;
+       /* number of iterations for the DMA data moves. */
+       u32 iter;
+};
+
+int omap_aess_set_opp_processing(struct omap_aess *abe, u32 opp);
+int omap_aess_connect_debug_trace(struct omap_aess *abe,
+                                 struct omap_aess_dma *dma2);
+
+/* gain */
+int omap_aess_use_compensated_gain(struct omap_aess *abe, int on_off);
+int omap_aess_write_equalizer(struct omap_aess *abe, u32 id,
+                             struct omap_aess_equ *param);
+
+int omap_aess_disable_gain(struct omap_aess *abe, u32 id);
+int omap_aess_enable_gain(struct omap_aess *abe, u32 id);
+int omap_aess_mute_gain(struct omap_aess *abe, u32 id);
+int omap_aess_unmute_gain(struct omap_aess *abe, u32 id);
+
+int omap_aess_write_gain(struct omap_aess *abe,        u32 id, s32 f_g);
+int omap_aess_write_mixer(struct omap_aess *abe, u32 id, s32 f_g);
+int omap_aess_read_gain(struct omap_aess *abe, u32 id, u32 *f_g);
+int omap_aess_read_mixer(struct omap_aess *abe, u32 id, u32 *f_g);
+
+int omap_aess_init_mem(struct omap_aess *abe, struct device *dev,
+       void __iomem **_io_base, u32 *fw_header);
+int omap_aess_reset_hal(struct omap_aess *abe);
+int omap_aess_load_fw(struct omap_aess *abe, u32 *firmware);
+int omap_aess_reload_fw(struct omap_aess *abe, u32 *firmware);
+u32 omap_abe_get_supported_fw_version(void);
+
+/* port */
+int omap_aess_mono_mixer(struct omap_aess *abe, u32 id, u32 on_off);
+int omap_aess_connect_serial_port(struct omap_aess *abe, u32 id,
+                                 struct omap_aess_data_format *f,
+                                 u32 mcbsp_id);
+int omap_aess_connect_cbpr_dmareq_port(struct omap_aess *abe, u32 id,
+                                      struct omap_aess_data_format *f, u32 d,
+                                      struct omap_aess_dma *returned_dma_t);
+int omap_aess_read_port_address(struct omap_aess *abe,
+                               u32 port, struct omap_aess_dma *dma2);
+int omap_aess_connect_irq_ping_pong_port(struct omap_aess *abe, u32 id,
+                                        struct omap_aess_data_format *f,
+                                        u32 subroutine_id, u32 size,
+                                        u32 *sink, u32 dsp_mcu_flag);
+void omap_aess_write_pdmdl_offset(struct omap_aess *abe, u32 path,
+                                 u32 offset_left, u32 offset_right);
+int omap_aess_enable_data_transfer(struct omap_aess *abe, u32 id);
+int omap_aess_disable_data_transfer(struct omap_aess *abe, u32 id);
+
+/* core */
+int omap_aess_check_activity(struct omap_aess *abe);
+int omap_aess_wakeup(struct omap_aess *abe);
+int omap_aess_set_router_configuration(struct omap_aess *abe, u32 *param);
+int omap_abe_read_next_ping_pong_buffer(struct omap_aess *abe,
+                                       u32 port, u32 *p, u32 *n);
+int omap_aess_read_next_ping_pong_buffer(struct omap_aess *abe,
+                                        u32 port, u32 *p, u32 *n);
+int omap_aess_irq_processing(struct omap_aess *abe);
+int omap_aess_set_ping_pong_buffer(struct omap_aess *abe,
+                                  u32 port, u32 n_bytes);
+int omap_aess_read_offset_from_ping_buffer(struct omap_aess *abe,
+                                          u32 id, u32 *n);
+/* seq */
+int omap_aess_plug_subroutine(struct omap_aess *abe, u32 *id,
+                             abe_subroutine2 f, u32 n, u32 *params);
+
+#endif /* _ABE_H_ */
diff --git a/sound/soc/omap/aess/abe_aess.c b/sound/soc/omap/aess/abe_aess.c
new file mode 100644 (file)
index 0000000..22f5a66
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include "abe.h"
+#include "abe_dbg.h"
+#include "abe_mem.h"
+#include "abe_aess.h"
+
+/**
+ * omap_aess_hw_configuration
+ * @abe: Pointer on aess handle
+ *
+ * Initialize the AESS HW registers for MPU and DMA
+ * request visibility.
+ */
+void omap_aess_hw_configuration(struct omap_aess *abe)
+{
+       /* enable AESS auto gating (required to release all AESS clocks) */
+       omap_aess_reg_writel(abe, AESS_AUTO_GATING_ENABLE, 1);
+       /* enables the DMAreq from AESS AESS_DMAENABLE_SET = 255 */
+       omap_aess_reg_writel(abe, AESS_DMAENABLE_SET, DMA_ENABLE_ALL);
+       /* enables the MCU IRQ from AESS to Cortex A9 */
+       omap_aess_reg_writel(abe, AESS_MCU_IRQENABLE_SET, INT_SET);
+}
+
+/**
+ * omap_aess_clear_irq - clear ABE interrupt
+ * @abe: Pointer on aess handle
+ *
+ * This subroutine is called to clear MCU Irq
+ */
+int omap_aess_clear_irq(struct omap_aess *abe)
+{
+       omap_aess_reg_writel(abe, ABE_MCU_IRQSTATUS, INT_CLR);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_clear_irq);
+
+/**
+ * abe_write_event_generator - Selects event generator source
+ * @abe: Pointer on abe handle
+ * @e: Event Generation Counter, McPDM, DMIC or default.
+ *
+ * Loads the AESS event generator hardware source.
+ * Indicates to the FW which data stream is the most important to preserve
+ * in case all the streams are asynchronous.
+ *
+ * the Audio Engine will generaly use its own timer EVENT generator programmed
+ * with the EVENT_COUNTER. The event counter will be tuned in order to deliver
+ * a pulse frequency at 96 kHz.
+ * The DPLL output at 100% OPP is MCLK = (32768kHz x6000) = 196.608kHz
+ * The ratio is (MCLK/96000)+(1<<1) = 2050
+ * (1<<1) in order to have the same speed at 50% and 100% OPP
+ * (only 15 MSB bits are used at OPP50%)
+ */
+int omap_aess_write_event_generator(struct omap_aess *abe, u32 e)
+{
+       u32 event, selection;
+       u32 counter = EVENT_GENERATOR_COUNTER_DEFAULT;
+
+       switch (e) {
+       case EVENT_TIMER:
+               selection = EVENT_SOURCE_COUNTER;
+               event = 0;
+               break;
+       case EVENT_44100:
+               selection = EVENT_SOURCE_COUNTER;
+               event = 0;
+               counter = EVENT_GENERATOR_COUNTER_44100;
+               break;
+       default:
+               aess_err("Bad event generator selection");
+               return -AESS_EINVAL;
+       }
+       omap_aess_reg_writel(abe, EVENT_GENERATOR_COUNTER, counter);
+       omap_aess_reg_writel(abe, EVENT_SOURCE_SELECTION, selection);
+       omap_aess_reg_writel(abe, EVENT_GENERATOR_START, EVENT_GENERATOR_ON);
+       omap_aess_reg_writel(abe, AUDIO_ENGINE_SCHEDULER, event);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_write_event_generator);
+
+/**
+ * omap_aess_start_event_generator - Starts event generator source
+ * @abe: Pointer on abe handle
+ *
+ * Start the event genrator of AESS. No more event will be send to AESS engine.
+ * Upper layer must wait 1/96kHz to be sure that engine reaches
+ * the IDLE instruction.
+ */
+int omap_aess_start_event_generator(struct omap_aess *abe)
+{
+       /* Start the event Generator */
+       omap_aess_reg_writel(abe, EVENT_GENERATOR_START, 1);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_start_event_generator);
+
+/**
+ * omap_aess_stop_event_generator - Stops event generator source
+ * @abe: Pointer on abe handle
+ *
+ * Stop the event genrator of AESS. No more event will be send to AESS engine.
+ * Upper layer must wait 1/96kHz to be sure that engine reaches
+ * the IDLE instruction.
+ */
+int omap_aess_stop_event_generator(struct omap_aess *abe)
+{
+       /* Stop the event Generator */
+       omap_aess_reg_writel(abe, EVENT_GENERATOR_START, 0);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_stop_event_generator);
+
+/**
+ * omap_aess_disable_irq - disable MCU/DSP ABE interrupt
+ * @abe: Pointer on abe handle
+ *
+ * This subroutine is disabling ABE MCU/DSP Irq
+ */
+int omap_aess_disable_irq(struct omap_aess *abe)
+{
+       /* disables the DMAreq from AESS AESS_DMAENABLE_CLR = 127
+        * DMA_Req7 will still be enabled as it is used for ABE trace */
+       omap_aess_reg_writel(abe, AESS_DMAENABLE_CLR, 0x7F);
+       /* disables the MCU IRQ from AESS to Cortex A9 */
+       omap_aess_reg_writel(abe, AESS_MCU_IRQENABLE_CLR, 0x01);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_disable_irq);
diff --git a/sound/soc/omap/aess/abe_aess.h b/sound/soc/omap/aess/abe_aess.h
new file mode 100644 (file)
index 0000000..c109eb7
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _ABE_AESS_H_
+#define _ABE_AESS_H_
+
+#define AESS_REVISION                  0x00
+#define AESS_MCU_IRQSTATUS             0x28
+#define AESS_MCU_IRQENABLE_SET         0x3C
+#define AESS_MCU_IRQENABLE_CLR         0x40
+#define AESS_DMAENABLE_SET             0x60
+#define AESS_DMAENABLE_CLR             0x64
+#define EVENT_GENERATOR_COUNTER                0x68
+#define EVENT_GENERATOR_START          0x6C
+#define EVENT_SOURCE_SELECTION         0x70
+#define AUDIO_ENGINE_SCHEDULER         0x74
+#define AESS_AUTO_GATING_ENABLE                0x7C
+
+/*
+ * AESS_MCU_IRQSTATUS bit field
+ */
+#define INT_CLEAR                      0x01
+
+/*
+ * AESS_MCU_IRQENABLE_SET bit field
+ */
+#define INT_SET                                0x01
+
+/*
+ * AESS_MCU_IRQENABLE_CLR bit field
+ */
+#define INT_CLR                                0x01
+
+/*
+ * AESS_DMAENABLE_SET bit fields
+ */
+#define DMA_ENABLE_ALL         0xFF
+
+/*
+ * AESS_DMAENABLE_CLR bit fields
+ */
+#define DMA_DISABLE_ALL                0xFF
+
+/*
+ * EVENT_GENERATOR_COUNTER COUNTER_VALUE bit field
+ */
+/* PLL output/desired sampling rate = (32768 * 6000)/96000 */
+#define EVENT_GENERATOR_COUNTER_DEFAULT        (2048-1)
+/* PLL output/desired sampling rate = (32768 * 6000)/88200 */
+#define EVENT_GENERATOR_COUNTER_44100  (2228-1)
+
+
+int omap_aess_start_event_generator(struct omap_aess *abe);
+int omap_aess_stop_event_generator(struct omap_aess *abe);
+int omap_aess_write_event_generator(struct omap_aess *abe, u32 e);
+
+int omap_aess_disable_irq(struct omap_aess *abe);
+int omap_aess_clear_irq(struct omap_aess *abe);
+
+void omap_aess_hw_configuration(struct omap_aess *abe);
+
+#endif /* _ABE_AESS_H_ */
diff --git a/sound/soc/omap/aess/abe_asrc.c b/sound/soc/omap/aess/abe_asrc.c
new file mode 100644 (file)
index 0000000..e9d90e8
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include "abe.h"
+#include "abe_dbg.h"
+#include "abe_mem.h"
+
+/**
+ * omap_aess_write_fifo
+ * @abe: Pointer on aess handle
+ * @memory_bank: currently only ABE_DMEM supported
+ * @descr_addr: FIFO descriptor address (descriptor fields: READ ptr, WRITE ptr,
+ * FIFO START_ADDR, FIFO END_ADDR)
+ * @data: data to write to FIFO
+ * @nb_data32: number of 32-bit words to write to DMEM FIFO
+ *
+ * write DMEM FIFO and update FIFO descriptor,
+ * it is assumed that FIFO descriptor is located in DMEM
+ */
+static void omap_aess_write_fifo(struct omap_aess *abe, u32 memory_bank,
+                                u32 descr_addr, u32 *data, u32 nb_data32)
+{
+       u32 fifo_addr[4];
+       u32 i;
+
+       /* read FIFO descriptor from DMEM */
+       omap_abe_mem_read(abe, OMAP_ABE_DMEM, descr_addr,
+                         &fifo_addr[0], 4 * sizeof(u32));
+
+       /* WRITE ptr < FIFO start address */
+       if ((fifo_addr[1] < fifo_addr[2]) || (fifo_addr[1] > fifo_addr[3]))
+               aess_err("FIFO write pointer Error");
+
+       switch (memory_bank) {
+       case OMAP_ABE_DMEM:
+               for (i = 0; i < nb_data32; i++) {
+                       omap_abe_mem_write(abe, OMAP_ABE_DMEM,
+                                          (s32)fifo_addr[1],
+                                          (u32 *)(data + i),
+                                          4);
+                       /* increment WRITE pointer */
+                       fifo_addr[1] = fifo_addr[1] + 4;
+                       if (fifo_addr[1] > fifo_addr[3])
+                               fifo_addr[1] = fifo_addr[2];
+                       if (fifo_addr[1] == fifo_addr[0])
+                               aess_err("FIFO write pointer Error");
+               }
+               /* update WRITE pointer in DMEM */
+               omap_abe_mem_write(abe, OMAP_ABE_DMEM, descr_addr +
+                                  sizeof(u32), &fifo_addr[1], 4);
+               break;
+       default:
+               break;
+       }
+}
+
+/**
+ * abe_init_asrc_vx_dl
+ * @abe: Pointer on aess handle
+ * @dppm: PPM drift value
+ *
+ * Initialize the following ASRC VX_DL parameters :
+ * 1. DriftSign = D_AsrcVars[1] = 1 or -1
+ * 2. Subblock = D_AsrcVars[2] = 0
+ * 3. DeltaAlpha = D_AsrcVars[3] =
+ *     (round(nb_phases * drift[ppm] * 10^-6 * 2^20)) << 2
+ * 4. MinusDeltaAlpha = D_AsrcVars[4] =
+ *     (-round(nb_phases * drift[ppm] * 10^-6 * 2^20)) << 2
+ * 5. OneMinusEpsilon = D_AsrcVars[5] = 1 - DeltaAlpha/2
+ * 6. AlphaCurrent = 0x000020 (CMEM), initial value of Alpha parameter
+ * 7. BetaCurrent = 0x3fffe0 (CMEM), initial value of Beta parameter
+ * AlphaCurrent + BetaCurrent = 1 (=0x400000 in CMEM = 2^20 << 2)
+ * 8. drift_ASRC = 0 & drift_io = 0
+ * 9. SMEM for ASRC_DL_VX_Coefs pointer
+ * 10. CMEM for ASRC_DL_VX_Coefs pointer
+ * ASRC_DL_VX_Coefs = C_CoefASRC16_VX_ADDR/C_CoefASRC16_VX_sizeof/0/1/
+ * C_CoefASRC15_VX_ADDR/C_CoefASRC15_VX_sizeof/0/1
+ * 11. SMEM for XinASRC_DL_VX pointer
+ * 12. CMEM for XinASRC_DL_VX pointer
+ * XinASRC_DL_VX = S_XinASRC_DL_VX_ADDR/S_XinASRC_DL_VX_sizeof/0/1/0/0/0/0
+ * 13. SMEM for IO_VX_DL_ASRC pointer
+ * 14. CMEM for IO_VX_DL_ASRC pointer
+ * IO_VX_DL_ASRC =
+ *     S_XinASRC_DL_VX_ADDR/S_XinASRC_DL_VX_sizeof/
+ *     ASRC_DL_VX_FIR_L+ASRC_margin/1/0/0/0/0
+ */
+void omap_aess_init_asrc_vx_dl(struct omap_aess *abe, s32 dppm)
+{
+       int offset, n_fifo_el;
+
+       n_fifo_el = 42;
+       if (dppm == 250)
+               offset = 48 * 2 + n_fifo_el;
+       else
+               offset = 48 * 2;
+
+       omap_aess_write_fifo(abe, OMAP_ABE_DMEM,
+                            abe->fw_info->map[OMAP_AESS_DMEM_FWMEMINITDESCR_ID].offset,
+                            &abe->fw_info->asrc[offset], n_fifo_el);
+}
+/**
+ * abe_init_asrc_vx_ul
+ * @abe: Pointer on aess handle
+ * @dppm: PPM drift value
+ *
+ * Initialize the following ASRC VX_UL parameters :
+ * 1. DriftSign = D_AsrcVars[1] = 1 or -1
+ * 2. Subblock = D_AsrcVars[2] = 0
+ * 3. DeltaAlpha = D_AsrcVars[3] =
+ *     (round(nb_phases * drift[ppm] * 10^-6 * 2^20)) << 2
+ * 4. MinusDeltaAlpha = D_AsrcVars[4] =
+ *     (-round(nb_phases * drift[ppm] * 10^-6 * 2^20)) << 2
+ * 5. OneMinusEpsilon = D_AsrcVars[5] = 1 - DeltaAlpha/2
+ * 6. AlphaCurrent = 0x000020 (CMEM), initial value of Alpha parameter
+ * 7. BetaCurrent = 0x3fffe0 (CMEM), initial value of Beta parameter
+ * AlphaCurrent + BetaCurrent = 1 (=0x400000 in CMEM = 2^20 << 2)
+ * 8. drift_ASRC = 0 & drift_io = 0
+ * 9. SMEM for ASRC_UL_VX_Coefs pointer
+ * 10. CMEM for ASRC_UL_VX_Coefs pointer
+ * ASRC_UL_VX_Coefs = C_CoefASRC16_VX_ADDR/C_CoefASRC16_VX_sizeof/0/1/
+ *     C_CoefASRC15_VX_ADDR/C_CoefASRC15_VX_sizeof/0/1
+ * 11. SMEM for XinASRC_UL_VX pointer
+ * 12. CMEM for XinASRC_UL_VX pointer
+ * XinASRC_UL_VX = S_XinASRC_UL_VX_ADDR/S_XinASRC_UL_VX_sizeof/0/1/0/0/0/0
+ * 13. SMEM for UL_48_8_DEC pointer
+ * 14. CMEM for UL_48_8_DEC pointer
+ * UL_48_8_DEC = S_XinASRC_UL_VX_ADDR/S_XinASRC_UL_VX_sizeof/
+ *     ASRC_UL_VX_FIR_L+ASRC_margin/1/0/0/0/0
+ * 15. SMEM for UL_48_16_DEC pointer
+ * 16. CMEM for UL_48_16_DEC pointer
+ * UL_48_16_DEC = S_XinASRC_UL_VX_ADDR/S_XinASRC_UL_VX_sizeof/
+ *     ASRC_UL_VX_FIR_L+ASRC_margin/1/0/0/0/0
+ */
+void omap_aess_init_asrc_vx_ul(struct omap_aess *abe, s32 dppm)
+{
+       int offset, n_fifo_el;
+
+       n_fifo_el = 48;
+       if (dppm == -250)
+               offset = n_fifo_el;
+       else
+               offset = 0;
+
+       omap_aess_write_fifo(abe, OMAP_ABE_DMEM,
+                            abe->fw_info->map[OMAP_AESS_DMEM_FWMEMINITDESCR_ID].offset,
+                            &abe->fw_info->asrc[offset], n_fifo_el);
+}
diff --git a/sound/soc/omap/aess/abe_core.c b/sound/soc/omap/aess/abe_core.c
new file mode 100644 (file)
index 0000000..69fbd3e
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include "abe.h"
+#include "abe_gain.h"
+#include "abe_aess.h"
+#include "abe_port.h"
+#include "abe_mem.h"
+#include "abe_dbg.h"
+#include "abe_seq.h"
+
+#define IRQtag_COUNT                                       0x000c
+#define IRQtag_PP                                          0x000d
+#define OMAP_ABE_IRQ_FIFO_MASK ((OMAP_ABE_D_MCUIRQFIFO_SIZE >> 2) - 1)
+
+/**
+ * abe_omap_aess_reset_hal - reset the ABE/HAL
+ * @abe: Pointer on aess handle
+ *
+ * Operations : reset the ABE by reloading the static variables and
+ * default AESS registers.
+ * Called after a PRCM cold-start reset of ABE
+ */
+int omap_aess_reset_hal(struct omap_aess *abe)
+{
+       u32 i;
+
+       /* IRQ & DBG circular read pointer in DMEM */
+       abe->irq_dbg_read_ptr = 0;
+
+       /* default = disable the mixer's adaptive gain control */
+       omap_aess_use_compensated_gain(abe, 0);
+
+       /* reset the default gain values */
+       for (i = 0; i < MAX_NBGAIN_CMEM; i++) {
+               abe->muted_gains_indicator[i] = 0;
+               abe->desired_gains_decibel[i] = (u32) GAIN_MUTE;
+               abe->desired_gains_linear[i] = 0;
+               abe->desired_ramp_delay_ms[i] = 0;
+               abe->muted_gains_decibel[i] = (u32) GAIN_TOOLOW;
+       }
+       omap_aess_hw_configuration(abe);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_reset_hal);
+
+/**
+ * omap_aess_wakeup - Wakeup ABE
+ * @abe: Pointer on aess handle
+ *
+ * Wakeup ABE in case of retention
+ */
+int omap_aess_wakeup(struct omap_aess *abe)
+{
+       /* Restart event generator */
+       omap_aess_write_event_generator(abe, EVENT_TIMER);
+
+       /* reconfigure DMA Req and MCU Irq visibility */
+       omap_aess_hw_configuration(abe);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_wakeup);
+
+/**
+ * omap_aess_monitoring
+ * @abe: Pointer on aess handle
+ *
+ * checks the internal status of ABE and HAL
+ */
+static void omap_aess_monitoring(struct omap_aess *abe)
+{
+
+}
+
+/**
+ * omap_aess_irq_processing - Process ABE interrupt
+ * @abe: Pointer on aess handle
+ *
+ * This subroutine is call upon reception of "MA_IRQ_99 ABE_MPU_IRQ" Audio
+ * back-end interrupt. This subroutine will check the ATC Hrdware, the
+ * IRQ_FIFO from the AE and act accordingly. Some IRQ source are originated
+ * for the delivery of "end of time sequenced tasks" notifications, some are
+ * originated from the Ping-Pong protocols, some are generated from
+ * the embedded debugger when the firmware stops on programmable break-points,
+ * etc ...
+ */
+int omap_aess_irq_processing(struct omap_aess *abe)
+{
+       u32 abe_irq_dbg_write_ptr, i, cmem_src, sm_cm;
+       struct omap_aess_irq_data IRQ_data;
+       struct omap_aess_addr addr;
+
+       /* extract the write pointer index from CMEM memory (INITPTR format) */
+       /* CMEM address of the write pointer in bytes */
+       cmem_src = abe->fw_info->label_id[OMAP_AESS_BUFFER_MCU_IRQ_FIFO_PTR_ID] << 2;
+       omap_abe_mem_read(abe, OMAP_ABE_CMEM, cmem_src,
+                         &sm_cm, sizeof(abe_irq_dbg_write_ptr));
+       /* AESS left-pointer index located on MSBs */
+       abe_irq_dbg_write_ptr = sm_cm >> 16;
+       abe_irq_dbg_write_ptr &= 0xFF;
+       /* loop on the IRQ FIFO content */
+       for (i = 0; i < OMAP_ABE_D_MCUIRQFIFO_SIZE; i++) {
+               /* stop when the FIFO is empty */
+               if (abe_irq_dbg_write_ptr == abe->irq_dbg_read_ptr)
+                       break;
+               /* read the IRQ/DBG FIFO */
+               memcpy(&addr, &abe->fw_info->map[OMAP_AESS_DMEM_MCUIRQFIFO_ID],
+                      sizeof(struct omap_aess_addr));
+               addr.offset += (abe->irq_dbg_read_ptr << 2);
+               addr.bytes = sizeof(IRQ_data);
+               omap_aess_mem_read(abe, addr, (u32 *)&IRQ_data);
+               abe->irq_dbg_read_ptr = (abe->irq_dbg_read_ptr + 1) & OMAP_ABE_IRQ_FIFO_MASK;
+               /* select the source of the interrupt */
+               switch (IRQ_data.tag) {
+               case IRQtag_PP:
+                       omap_aess_irq_ping_pong(abe);
+                       break;
+               case IRQtag_COUNT:
+                       /*abe_irq_check_for_sequences(IRQ_data.data);*/
+                       omap_aess_monitoring(abe);
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_irq_processing);
+
+/**
+ * abe_set_router_configuration
+ * @abe: Pointer on aess handle
+ * @param: list of output index of the route
+ *
+ * The uplink router takes its input from DMIC (6 samples), AMIC (2 samples)
+ * and PORT1/2 (2 stereo ports). Each sample will be individually stored in
+ * an intermediate table of 10 elements.
+ *
+ * Example of router table parameter for voice uplink with phoenix microphones
+ *
+ * indexes 0 .. 9 = MM_UL description (digital MICs and MMEXTIN)
+ *     DMIC1_L_labelID, DMIC1_R_labelID, DMIC2_L_labelID, DMIC2_R_labelID,
+ *     MM_EXT_IN_L_labelID, MM_EXT_IN_R_labelID, ZERO_labelID, ZERO_labelID,
+ *     ZERO_labelID, ZERO_labelID,
+ * indexes 10 .. 11 = MM_UL2 description (recording on DMIC3)
+ *     DMIC3_L_labelID, DMIC3_R_labelID,
+ * indexes 12 .. 13 = VX_UL description (VXUL based on PDMUL data)
+ *     AMIC_L_labelID, AMIC_R_labelID,
+ * indexes 14 .. 15 = RESERVED (NULL)
+ *     ZERO_labelID, ZERO_labelID,
+ */
+int omap_aess_set_router_configuration(struct omap_aess *abe, u32 *param)
+{
+       omap_aess_mem_write(abe,
+                           abe->fw_info->map[OMAP_AESS_DMEM_AUPLINKROUTING_ID],
+                           param);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_set_router_configuration);
+
+/**
+ * abe_set_opp_processing - Set OPP mode for ABE Firmware
+ * @abe: Pointer on aess handle
+ * @opp: OOPP mode
+ *
+ * New processing network and OPP:
+ * 0: Ultra Lowest power consumption audio player (no post-processing, no mixer)
+ * 1: OPP 25% (simple multimedia features, including low-power player)
+ * 2: OPP 50% (multimedia and voice calls)
+ * 3: OPP100% ( multimedia complex use-cases)
+ *
+ * Rearranges the FW task network to the corresponding OPP list of features.
+ * The corresponding AE ports are supposed to be set/reset accordingly before
+ * this switch.
+ *
+ */
+int omap_aess_set_opp_processing(struct omap_aess *abe, u32 opp)
+{
+       u32 dOppMode32;
+
+       switch (opp) {
+       case ABE_OPP25:
+               /* OPP25% */
+               dOppMode32 = DOPPMODE32_OPP25;
+               break;
+       case ABE_OPP50:
+               /* OPP50% */
+               dOppMode32 = DOPPMODE32_OPP50;
+               break;
+       default:
+               aess_warm("Bad OPP value requested");
+       case ABE_OPP100:
+               /* OPP100% */
+               dOppMode32 = DOPPMODE32_OPP100;
+               break;
+       }
+       /* Write Multiframe inside DMEM */
+       omap_aess_mem_write(abe,
+                           abe->fw_info->map[OMAP_AESS_DMEM_MAXTASKBYTESINSLOT_ID],
+                           &dOppMode32);
+
+       return 0;
+
+}
+EXPORT_SYMBOL(omap_aess_set_opp_processing);
diff --git a/sound/soc/omap/aess/abe_dbg.h b/sound/soc/omap/aess/abe_dbg.h
new file mode 100644 (file)
index 0000000..5f72688
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _ABE_DBG_H_
+#define _ABE_DBG_H_
+
+#define DEBUG
+
+#include <linux/printk.h>
+
+#ifdef DEBUG
+
+#define aess_err(format, ...) \
+       pr_err(format, ## __VA_ARGS__)
+#define aess_warm(format, ...) \
+       pr_warn(format, ## __VA_ARGS__)
+#define aess_info(format, ...) \
+       pr_info(format, ## __VA_ARGS__)
+#else
+
+#define aess_err(format, ...)
+#define aess_warm(format, ...)
+#define aess_info(format, ...)
+
+#endif
+
+#define AESS_EINVAL    EINVAL
+
+#endif /* _ABE_DBG_H_ */
diff --git a/sound/soc/omap/aess/abe_def.h b/sound/soc/omap/aess/abe_def.h
new file mode 100644 (file)
index 0000000..a788810
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _ABE_DEF_H_
+#define _ABE_DEF_H_
+/*
+ * HARDWARE AND PERIPHERAL DEFINITIONS
+ */
+/* MM_DL */
+#define ABE_CBPR0_IDX 0
+/* VX_DL */
+#define ABE_CBPR1_IDX 1
+/* VX_UL */
+#define ABE_CBPR2_IDX 2
+/* MM_UL */
+#define ABE_CBPR3_IDX 3
+/* MM_UL2 */
+#define ABE_CBPR4_IDX 4
+/* TONES */
+#define ABE_CBPR5_IDX 5
+/* TDB */
+#define ABE_CBPR6_IDX 6
+/* DEBUG/CTL */
+#define ABE_CBPR7_IDX 7
+#define CIRCULAR_BUFFER_PERIPHERAL_R__0 (0x100 + ABE_CBPR0_IDX*4)
+#define CIRCULAR_BUFFER_PERIPHERAL_R__1 (0x100 + ABE_CBPR1_IDX*4)
+#define CIRCULAR_BUFFER_PERIPHERAL_R__2 (0x100 + ABE_CBPR2_IDX*4)
+#define CIRCULAR_BUFFER_PERIPHERAL_R__3 (0x100 + ABE_CBPR3_IDX*4)
+#define CIRCULAR_BUFFER_PERIPHERAL_R__4 (0x100 + ABE_CBPR4_IDX*4)
+#define CIRCULAR_BUFFER_PERIPHERAL_R__5 (0x100 + ABE_CBPR5_IDX*4)
+#define CIRCULAR_BUFFER_PERIPHERAL_R__6 (0x100 + ABE_CBPR6_IDX*4)
+#define CIRCULAR_BUFFER_PERIPHERAL_R__7 (0x100 + ABE_CBPR7_IDX*4)
+#define PING_PONG_WITH_MCU_IRQ  1
+#define PING_PONG_WITH_DSP_IRQ  2
+/* ID used for LIB memory copy subroutines */
+#define COPY_FROM_ABE_TO_HOST 1
+#define COPY_FROM_HOST_TO_ABE 2
+/*
+ * INTERNAL DEFINITIONS
+ */
+#define ABE_FIRMWARE_MAX_SIZE 26629
+/* 24 Q6.26 coefficients */
+#define NBEQ1 25
+/* 2x12 Q6.26 coefficients */
+#define NBEQ2 13
+/* TBD APS first set of parameters */
+#define NBAPS1 10
+/* TBD APS second set of parameters */
+#define NBAPS2 10
+/* Mixer used for sending tones to the uplink voice path */
+#define NBMIX_AUDIO_UL 2
+/* Main downlink mixer */
+#define NBMIX_DL1 4
+/* Handsfree downlink mixer */
+#define NBMIX_DL2 4
+/* Side-tone mixer */
+#define NBMIX_SDT 2
+/* Echo reference mixer */
+#define NBMIX_ECHO 2
+/* Voice record mixer */
+#define NBMIX_VXREC 4
+/* unsigned version of (-1) */
+#define CC_M1 0xFF
+#define CS_M1 0xFFFF
+#define CL_M1 0xFFFFFFFFL
+/*
+       Mixer ID         Input port ID          Comments
+       DL1_MIXER        0 MMDL path
+        1 MMUL2 path
+        2 VXDL path
+        3 TONES path
+       SDT_MIXER        0 Uplink path
+        1 Downlink path
+       ECHO_MIXER       0 DL1_MIXER path
+        1 DL2_MIXER path
+       AUDUL_MIXER      0 TONES_DL path
+        1 Uplink path
+        2 MM_DL path
+       VXREC_MIXER      0 TONES_DL path
+        1 VX_DL path
+        2 MM_DL path
+        3 VX_UL path
+*/
+#define MIX_VXUL_INPUT_MM_DL 0
+#define MIX_VXUL_INPUT_TONES 1
+#define MIX_VXUL_INPUT_VX_UL 2
+#define MIX_VXUL_INPUT_VX_DL 3
+#define MIX_DL1_INPUT_MM_DL 0
+#define MIX_DL1_INPUT_MM_UL2 1
+#define MIX_DL1_INPUT_VX_DL 2
+#define MIX_DL1_INPUT_TONES 3
+#define MIX_DL2_INPUT_MM_DL 0
+#define MIX_DL2_INPUT_MM_UL2 1
+#define MIX_DL2_INPUT_VX_DL 2
+#define MIX_DL2_INPUT_TONES 3
+#define MIX_SDT_INPUT_UP_MIXER 0
+#define MIX_SDT_INPUT_DL1_MIXER 1
+#define MIX_AUDUL_INPUT_MM_DL 0
+#define MIX_AUDUL_INPUT_TONES 1
+#define MIX_AUDUL_INPUT_UPLINK 2
+#define MIX_AUDUL_INPUT_VX_DL 3
+#define MIX_VXREC_INPUT_MM_DL 0
+#define MIX_VXREC_INPUT_TONES 1
+#define MIX_VXREC_INPUT_VX_UL 2
+#define MIX_VXREC_INPUT_VX_DL 3
+#define MIX_ECHO_DL1   0
+#define MIX_ECHO_DL2   1
+/* nb of samples to route */
+#define NBROUTE_UL 16
+/* 10 routing tables max */
+#define NBROUTE_CONFIG_MAX 10
+/* 5 pre-computed routing tables */
+#define NBROUTE_CONFIG 6
+/* AMIC on VX_UL */
+#define UPROUTE_CONFIG_AMIC 0
+/* DMIC first pair on VX_UL */
+#define UPROUTE_CONFIG_DMIC1 1
+/* DMIC second pair on VX_UL */
+#define UPROUTE_CONFIG_DMIC2 2
+/* DMIC last pair on VX_UL */
+#define UPROUTE_CONFIG_DMIC3 3
+/* BT_UL on VX_UL */
+#define UPROUTE_CONFIG_BT 4
+/* ECHO_REF on MM_UL2 */
+#define UPROUTE_ECHO_MMUL2 5
+/* max number of feature associated to a port */
+#define MAXFEATUREPORT 12
+#define SUB_0_PARAM 0
+/* number of parameters per sequence calls */
+#define SUB_1_PARAM 1
+#define SUB_2_PARAM 2
+#define SUB_3_PARAM 3
+#define SUB_4_PARAM 4
+/* active sequence mask = 0 means the line is free */
+#define FREE_LINE 0
+/* no ask for collision protection */
+#define NOMASK (1 << 0)
+/* do not allow a PDM OFF during the execution of this sequence */
+#define MASK_PDM_OFF (1 << 1)
+/* do not allow a PDM ON during the execution of this sequence */
+#define MASK_PDM_ON (1 << 2)
+/* explicit name of the feature */
+#define NBCHARFEATURENAME 16
+/* explicit name of the port */
+#define NBCHARPORTNAME 16
+/* sink / input port from Host point of view (or AESS for DMIC/McPDM/.. */
+#define SNK_P ABE_ATC_DIRECTION_IN
+/* source / ouptut port */
+#define SRC_P ABE_ATC_DIRECTION_OUT
+/* no ASRC applied */
+#define NODRIFT 0
+/* for abe_set_asrc_drift_control */
+#define FORCED_DRIFT_CONTROL 1
+/* for abe_set_asrc_drift_control */
+#define ADPATIVE_DRIFT_CONTROL 2
+/* number of task/slot depending on the OPP value */
+#define DOPPMODE32_OPP100 (0x00000010)
+#define DOPPMODE32_OPP50 (0x0000000C)
+#define DOPPMODE32_OPP25 (0x0000004)
+/*
+ * ABE CONST AREA FOR PARAMETERS TRANSLATION
+ */
+#define GAIN_MAXIMUM 3000L
+#define GAIN_24dB 2400L
+#define GAIN_18dB 1800L
+#define GAIN_12dB 1200L
+#define GAIN_6dB 600L
+/* default gain = 1 */
+#define GAIN_0dB  0L
+#define GAIN_M6dB -600L
+#define GAIN_M7dB -700L
+#define GAIN_M12dB -1200L
+#define GAIN_M18dB -1800L
+#define GAIN_M24dB -2400L
+#define GAIN_M30dB -3000L
+#define GAIN_M40dB -4000L
+#define GAIN_M50dB -5000L
+/* muted gain = -120 decibels */
+#define MUTE_GAIN -12000L
+#define GAIN_TOOLOW -13000L
+#define GAIN_MUTE MUTE_GAIN
+#define RAMP_MINLENGTH 0L
+/* ramp_t is in milli- seconds */
+#define RAMP_0MS 0L
+#define RAMP_1MS 1L
+#define RAMP_2MS 2L
+#define RAMP_5MS 5L
+#define RAMP_10MS 10L
+#define RAMP_20MS 20L
+#define RAMP_50MS 50L
+#define RAMP_100MS 100L
+#define RAMP_200MS  200L
+#define RAMP_500MS  500L
+#define RAMP_1000MS  1000L
+#define RAMP_MAXLENGTH  10000L
+/* for abe_translate_gain_format */
+#define LINABE_TO_DECIBELS 1
+#define DECIBELS_TO_LINABE 2
+/* for abe_translate_ramp_format */
+#define IIRABE_TO_MICROS 1
+#define MICROS_TO_IIABE 2
+/*
+ * ABE CONST AREA FOR PERIPHERAL TUNING
+ */
+/* port idled IDLE_P */
+#define OMAP_ABE_PORT_ACTIVITY_IDLE    1
+/* port initialized, ready to be activated  */
+#define OMAP_ABE_PORT_INITIALIZED       3
+/* port activated RUN_P */
+#define OMAP_ABE_PORT_ACTIVITY_RUNNING  2
+#define NOCALLBACK 0
+#define NOPARAMETER 0
+/* number of ATC access upon AMIC DMArequests, all the FIFOs are enabled */
+#define MCPDM_UL_ITER 4
+/* All the McPDM FIFOs are enabled simultaneously */
+#define MCPDM_DL_ITER 24
+/* All the DMIC FIFOs are enabled simultaneously */
+#define DMIC_ITER 12
+/* TBD later if needed */
+#define MAX_PINGPONG_BUFFERS 2
+/*
+ * Indexes to the subroutines
+ */
+#define SUB_WRITE_MIXER 1
+#define SUB_WRITE_PORT_GAIN 2
+/* OLD WAY */
+#define c_feat_init_eq 1
+#define c_feat_read_eq1 2
+#define c_write_eq1 3
+#define c_feat_read_eq2 4
+#define c_write_eq2 5
+#define c_feat_read_eq3 6
+#define c_write_eq3 7
+/* max number of gain to be controlled by HAL */
+#define MAX_NBGAIN_CMEM 36
+/*
+ * MACROS
+ */
+#define maximum(a, b) (((a) < (b)) ? (b) : (a))
+#define minimum(a, b) (((a) > (b)) ? (b) : (a))
+#define absolute(a) (((a) > 0) ? (a) : ((-1)*(a)))
+#define HAL_VERSIONS 9
+#endif/* _ABE_DEF_H_ */
diff --git a/sound/soc/omap/aess/abe_ext.h b/sound/soc/omap/aess/abe_ext.h
new file mode 100644 (file)
index 0000000..cb522e9
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _ABE_EXT_H_
+#define _ABE_EXT_H_
+
+/*
+ * HARDWARE AND PERIPHERAL DEFINITIONS
+ */
+/* PMEM SIZE in bytes (1024 words of 64 bits: : #32bits words x 4)*/
+#define ABE_PMEM_SIZE 8192
+/* CMEM SIZE in bytes (2048 coeff : #32bits words x 4)*/
+#define ABE_CMEM_SIZE 8192
+/* SMEM SIZE in bytes (3072 stereo samples : #32bits words x 4)*/
+#define ABE_SMEM_SIZE 24576
+/* DMEM SIZE in bytes */
+#define ABE_DMEM_SIZE 65536L
+/* ATC REGISTERS SIZE in bytes */
+#define ABE_ATC_DESC_SIZE 512
+/* holds the MCU Irq signal */
+#define ABE_MCU_IRQSTATUS_RAW 0x24
+/* status : clear the IRQ */
+#define ABE_MCU_IRQSTATUS      0x28
+/* holds the DSP Irq signal */
+#define ABE_DSP_IRQSTATUS_RAW 0x4C
+/* holds the DMA req lines to the sDMA */
+#define ABE_DMASTATUS_RAW 0x84
+#define EVENT_GENERATOR_COUNTER 0x68
+/* PLL output/desired sampling rate = (32768 * 6000)/96000 */
+#define EVENT_GENERATOR_COUNTER_DEFAULT (2048-1)
+/* PLL output/desired sampling rate = (32768 * 6000)/88200 */
+#define EVENT_GENERATOR_COUNTER_44100 (2228-1)
+/* start / stop the EVENT generator */
+#define EVENT_GENERATOR_START 0x6C
+#define EVENT_GENERATOR_ON 1
+#define EVENT_GENERATOR_OFF 0
+/* selection of the EVENT generator source */
+#define EVENT_SOURCE_SELECTION 0x70
+#define EVENT_SOURCE_DMA 0
+#define EVENT_SOURCE_COUNTER 1
+/* selection of the ABE DMA req line from ATC */
+#define AUDIO_ENGINE_SCHEDULER 0x74
+#define ABE_ATC_DMIC_DMA_REQ 1
+#define ABE_ATC_MCPDMDL_DMA_REQ 2
+#define ABE_ATC_MCPDMUL_DMA_REQ 3
+/* Direction=0 means input from ABE point of view */
+#define ABE_ATC_DIRECTION_IN 0
+/* Direction=1 means output from ABE point of view */
+#define ABE_ATC_DIRECTION_OUT 1
+/*
+ * DMA requests
+ */
+/*Internal connection doesn't connect at ABE boundary */
+#define External_DMA_0 0
+/*Transmit request digital microphone */
+#define DMIC_DMA_REQ   1
+/*Multichannel PDM downlink */
+#define McPDM_DMA_DL   2
+/*Multichannel PDM uplink */
+#define McPDM_DMA_UP   3
+/*MCBSP module 1 - transmit request */
+#define MCBSP1_DMA_TX  4
+/*MCBSP module 1 - receive request */
+#define MCBSP1_DMA_RX  5
+/*MCBSP module 2 - transmit request */
+#define MCBSP2_DMA_TX  6
+/*MCBSP module 2 - receive request */
+#define MCBSP2_DMA_RX  7
+/*MCBSP module 3 - transmit request */
+#define MCBSP3_DMA_TX  8
+/*MCBSP module 3 - receive request */
+#define MCBSP3_DMA_RX  9
+/*SLIMBUS module 1 - transmit request channel 0 */
+#define SLIMBUS1_DMA_TX0       10
+/*SLIMBUS module 1 - transmit request channel 1 */
+#define SLIMBUS1_DMA_TX1       11
+/*SLIMBUS module 1 - transmit request channel 2 */
+#define SLIMBUS1_DMA_TX2       12
+/*SLIMBUS module 1 - transmit request channel 3 */
+#define SLIMBUS1_DMA_TX3       13
+/*SLIMBUS module 1 - transmit request channel 4 */
+#define SLIMBUS1_DMA_TX4       14
+/*SLIMBUS module 1 - transmit request channel 5 */
+#define SLIMBUS1_DMA_TX5       15
+/*SLIMBUS module 1 - transmit request channel 6 */
+#define SLIMBUS1_DMA_TX6       16
+/*SLIMBUS module 1 - transmit request channel 7 */
+#define SLIMBUS1_DMA_TX7       17
+/*SLIMBUS module 1 - receive request channel 0 */
+#define SLIMBUS1_DMA_RX0       18
+/*SLIMBUS module 1 - receive request channel 1 */
+#define SLIMBUS1_DMA_RX1       19
+/*SLIMBUS module 1 - receive request channel 2 */
+#define SLIMBUS1_DMA_RX2       20
+/*SLIMBUS module 1 - receive request channel 3 */
+#define SLIMBUS1_DMA_RX3       21
+/*SLIMBUS module 1 - receive request channel 4 */
+#define SLIMBUS1_DMA_RX4       22
+/*SLIMBUS module 1 - receive request channel 5 */
+#define SLIMBUS1_DMA_RX5       23
+/*SLIMBUS module 1 - receive request channel 6 */
+#define SLIMBUS1_DMA_RX6       24
+/*SLIMBUS module 1 - receive request channel 7 */
+#define SLIMBUS1_DMA_RX7       25
+/*McASP - Data transmit DMA request line */
+#define McASP1_AXEVT   26
+/*McASP - Data receive DMA request line */
+#define McASP1_AREVT   29
+/*DUMMY FIFO @@@ */
+#define _DUMMY_FIFO_   30
+/*DMA of the Circular buffer peripheral 0 */
+#define CBPr_DMA_RTX0  32
+/*DMA of the Circular buffer peripheral 1 */
+#define CBPr_DMA_RTX1  33
+/*DMA of the Circular buffer peripheral 2 */
+#define CBPr_DMA_RTX2  34
+/*DMA of the Circular buffer peripheral 3 */
+#define CBPr_DMA_RTX3  35
+/*DMA of the Circular buffer peripheral 4 */
+#define CBPr_DMA_RTX4  36
+/*DMA of the Circular buffer peripheral 5 */
+#define CBPr_DMA_RTX5  37
+/*DMA of the Circular buffer peripheral 6 */
+#define CBPr_DMA_RTX6  38
+/*DMA of the Circular buffer peripheral 7 */
+#define CBPr_DMA_RTX7  39
+/*
+ * ATC DESCRIPTORS - DESTINATIONS
+ */
+#define DEST_DMEM_access       0x00
+#define DEST_MCBSP1_TX  0x01
+#define DEST_MCBSP2_TX  0x02
+#define DEST_MCBSP3_TX  0x03
+#define DEST_SLIMBUS1_TX0 0x04
+#define DEST_SLIMBUS1_TX1 0x05
+#define DEST_SLIMBUS1_TX2 0x06
+#define DEST_SLIMBUS1_TX3 0x07
+#define DEST_SLIMBUS1_TX4 0x08
+#define DEST_SLIMBUS1_TX5 0x09
+#define DEST_SLIMBUS1_TX6 0x0A
+#define DEST_SLIMBUS1_TX7 0x0B
+#define DEST_MCPDM_DL 0x0C
+#define DEST_MCASP_TX0 0x0D
+#define DEST_MCASP_TX1 0x0E
+#define DEST_MCASP_TX2 0x0F
+#define DEST_MCASP_TX3 0x10
+#define DEST_EXTPORT0 0x11
+#define DEST_EXTPORT1 0x12
+#define DEST_EXTPORT2 0x13
+#define DEST_EXTPORT3 0x14
+#define DEST_MCPDM_ON 0x15
+#define DEST_CBP_CBPr 0x3F
+/*
+ * ATC DESCRIPTORS - SOURCES
+ */
+#define SRC_DMEM_access        0x0
+#define SRC_MCBSP1_RX 0x01
+#define SRC_MCBSP2_RX 0x02
+#define SRC_MCBSP3_RX 0x03
+#define SRC_SLIMBUS1_RX0 0x04
+#define SRC_SLIMBUS1_RX1 0x05
+#define SRC_SLIMBUS1_RX2 0x06
+#define SRC_SLIMBUS1_RX3 0x07
+#define SRC_SLIMBUS1_RX4 0x08
+#define SRC_SLIMBUS1_RX5 0x09
+#define SRC_SLIMBUS1_RX6 0x0A
+#define SRC_SLIMBUS1_RX7 0x0B
+#define SRC_DMIC_UP 0x0C
+#define SRC_MCPDM_UP 0x0D
+#define SRC_MCASP_RX0 0x0E
+#define SRC_MCASP_RX1 0x0F
+#define SRC_MCASP_RX2 0x10
+#define SRC_MCASP_RX3 0x11
+#define SRC_CBP_CBPr 0x3F
+#endif/* _ABE_EXT_H_ */
diff --git a/sound/soc/omap/aess/abe_gain.c b/sound/soc/omap/aess/abe_gain.c
new file mode 100644 (file)
index 0000000..c462786
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include "abe.h"
+#include "abe_gain.h"
+#include "abe_mem.h"
+#include "abe_dbg.h"
+
+/*
+ * ABE CONST AREA FOR PARAMETERS TRANSLATION
+ */
+#define min_mdb (-12000)
+#define max_mdb (3000)
+#define sizeof_db2lin_table (1 + ((max_mdb - min_mdb)/100))
+
+static const u32 abe_db2lin_table[sizeof_db2lin_table] = {
+       0x00000000,             /* SMEM coding of -120 dB */
+       0x00000000,             /* SMEM coding of -119 dB */
+       0x00000000,             /* SMEM coding of -118 dB */
+       0x00000000,             /* SMEM coding of -117 dB */
+       0x00000000,             /* SMEM coding of -116 dB */
+       0x00000000,             /* SMEM coding of -115 dB */
+       0x00000000,             /* SMEM coding of -114 dB */
+       0x00000000,             /* SMEM coding of -113 dB */
+       0x00000000,             /* SMEM coding of -112 dB */
+       0x00000000,             /* SMEM coding of -111 dB */
+       0x00000000,             /* SMEM coding of -110 dB */
+       0x00000000,             /* SMEM coding of -109 dB */
+       0x00000001,             /* SMEM coding of -108 dB */
+       0x00000001,             /* SMEM coding of -107 dB */
+       0x00000001,             /* SMEM coding of -106 dB */
+       0x00000001,             /* SMEM coding of -105 dB */
+       0x00000001,             /* SMEM coding of -104 dB */
+       0x00000001,             /* SMEM coding of -103 dB */
+       0x00000002,             /* SMEM coding of -102 dB */
+       0x00000002,             /* SMEM coding of -101 dB */
+       0x00000002,             /* SMEM coding of -100 dB */
+       0x00000002,             /* SMEM coding of -99 dB */
+       0x00000003,             /* SMEM coding of -98 dB */
+       0x00000003,             /* SMEM coding of -97 dB */
+       0x00000004,             /* SMEM coding of -96 dB */
+       0x00000004,             /* SMEM coding of -95 dB */
+       0x00000005,             /* SMEM coding of -94 dB */
+       0x00000005,             /* SMEM coding of -93 dB */
+       0x00000006,             /* SMEM coding of -92 dB */
+       0x00000007,             /* SMEM coding of -91 dB */
+       0x00000008,             /* SMEM coding of -90 dB */
+       0x00000009,             /* SMEM coding of -89 dB */
+       0x0000000A,             /* SMEM coding of -88 dB */
+       0x0000000B,             /* SMEM coding of -87 dB */
+       0x0000000D,             /* SMEM coding of -86 dB */
+       0x0000000E,             /* SMEM coding of -85 dB */
+       0x00000010,             /* SMEM coding of -84 dB */
+       0x00000012,             /* SMEM coding of -83 dB */
+       0x00000014,             /* SMEM coding of -82 dB */
+       0x00000017,             /* SMEM coding of -81 dB */
+       0x0000001A,             /* SMEM coding of -80 dB */
+       0x0000001D,             /* SMEM coding of -79 dB */
+       0x00000021,             /* SMEM coding of -78 dB */
+       0x00000025,             /* SMEM coding of -77 dB */
+       0x00000029,             /* SMEM coding of -76 dB */
+       0x0000002E,             /* SMEM coding of -75 dB */
+       0x00000034,             /* SMEM coding of -74 dB */
+       0x0000003A,             /* SMEM coding of -73 dB */
+       0x00000041,             /* SMEM coding of -72 dB */
+       0x00000049,             /* SMEM coding of -71 dB */
+       0x00000052,             /* SMEM coding of -70 dB */
+       0x0000005D,             /* SMEM coding of -69 dB */
+       0x00000068,             /* SMEM coding of -68 dB */
+       0x00000075,             /* SMEM coding of -67 dB */
+       0x00000083,             /* SMEM coding of -66 dB */
+       0x00000093,             /* SMEM coding of -65 dB */
+       0x000000A5,             /* SMEM coding of -64 dB */
+       0x000000B9,             /* SMEM coding of -63 dB */
+       0x000000D0,             /* SMEM coding of -62 dB */
+       0x000000E9,             /* SMEM coding of -61 dB */
+       0x00000106,             /* SMEM coding of -60 dB */
+       0x00000126,             /* SMEM coding of -59 dB */
+       0x0000014A,             /* SMEM coding of -58 dB */
+       0x00000172,             /* SMEM coding of -57 dB */
+       0x0000019F,             /* SMEM coding of -56 dB */
+       0x000001D2,             /* SMEM coding of -55 dB */
+       0x0000020B,             /* SMEM coding of -54 dB */
+       0x0000024A,             /* SMEM coding of -53 dB */
+       0x00000292,             /* SMEM coding of -52 dB */
+       0x000002E2,             /* SMEM coding of -51 dB */
+       0x0000033C,             /* SMEM coding of -50 dB */
+       0x000003A2,             /* SMEM coding of -49 dB */
+       0x00000413,             /* SMEM coding of -48 dB */
+       0x00000492,             /* SMEM coding of -47 dB */
+       0x00000521,             /* SMEM coding of -46 dB */
+       0x000005C2,             /* SMEM coding of -45 dB */
+       0x00000676,             /* SMEM coding of -44 dB */
+       0x0000073F,             /* SMEM coding of -43 dB */
+       0x00000822,             /* SMEM coding of -42 dB */
+       0x00000920,             /* SMEM coding of -41 dB */
+       0x00000A3D,             /* SMEM coding of -40 dB */
+       0x00000B7D,             /* SMEM coding of -39 dB */
+       0x00000CE4,             /* SMEM coding of -38 dB */
+       0x00000E76,             /* SMEM coding of -37 dB */
+       0x0000103A,             /* SMEM coding of -36 dB */
+       0x00001235,             /* SMEM coding of -35 dB */
+       0x0000146E,             /* SMEM coding of -34 dB */
+       0x000016EC,             /* SMEM coding of -33 dB */
+       0x000019B8,             /* SMEM coding of -32 dB */
+       0x00001CDC,             /* SMEM coding of -31 dB */
+       0x00002061,             /* SMEM coding of -30 dB */
+       0x00002455,             /* SMEM coding of -29 dB */
+       0x000028C4,             /* SMEM coding of -28 dB */
+       0x00002DBD,             /* SMEM coding of -27 dB */
+       0x00003352,             /* SMEM coding of -26 dB */
+       0x00003995,             /* SMEM coding of -25 dB */
+       0x0000409C,             /* SMEM coding of -24 dB */
+       0x0000487E,             /* SMEM coding of -23 dB */
+       0x00005156,             /* SMEM coding of -22 dB */
+       0x00005B43,             /* SMEM coding of -21 dB */
+       0x00006666,             /* SMEM coding of -20 dB */
+       0x000072E5,             /* SMEM coding of -19 dB */
+       0x000080E9,             /* SMEM coding of -18 dB */
+       0x000090A4,             /* SMEM coding of -17 dB */
+       0x0000A24B,             /* SMEM coding of -16 dB */
+       0x0000B618,             /* SMEM coding of -15 dB */
+       0x0000CC50,             /* SMEM coding of -14 dB */
+       0x0000E53E,             /* SMEM coding of -13 dB */
+       0x00010137,             /* SMEM coding of -12 dB */
+       0x0001209A,             /* SMEM coding of -11 dB */
+       0x000143D1,             /* SMEM coding of -10 dB */
+       0x00016B54,             /* SMEM coding of -9 dB */
+       0x000197A9,             /* SMEM coding of -8 dB */
+       0x0001C967,             /* SMEM coding of -7 dB */
+       0x00020137,             /* SMEM coding of -6 dB */
+       0x00023FD6,             /* SMEM coding of -5 dB */
+       0x00028619,             /* SMEM coding of -4 dB */
+       0x0002D4EF,             /* SMEM coding of -3 dB */
+       0x00032D64,             /* SMEM coding of -2 dB */
+       0x000390A4,             /* SMEM coding of -1 dB */
+       0x00040000,             /* SMEM coding of 0 dB */
+       0x00047CF2,             /* SMEM coding of 1 dB */
+       0x00050923,             /* SMEM coding of 2 dB */
+       0x0005A670,             /* SMEM coding of 3 dB */
+       0x000656EE,             /* SMEM coding of 4 dB */
+       0x00071CF5,             /* SMEM coding of 5 dB */
+       0x0007FB26,             /* SMEM coding of 6 dB */
+       0x0008F473,             /* SMEM coding of 7 dB */
+       0x000A0C2B,             /* SMEM coding of 8 dB */
+       0x000B4606,             /* SMEM coding of 9 dB */
+       0x000CA62C,             /* SMEM coding of 10 dB */
+       0x000E314A,             /* SMEM coding of 11 dB */
+       0x000FEC9E,             /* SMEM coding of 12 dB */
+       0x0011DE0A,             /* SMEM coding of 13 dB */
+       0x00140C28,             /* SMEM coding of 14 dB */
+       0x00167E60,             /* SMEM coding of 15 dB */
+       0x00193D00,             /* SMEM coding of 16 dB */
+       0x001C515D,             /* SMEM coding of 17 dB */
+       0x001FC5EB,             /* SMEM coding of 18 dB */
+       0x0023A668,             /* SMEM coding of 19 dB */
+       0x00280000,             /* SMEM coding of 20 dB */
+       0x002CE178,             /* SMEM coding of 21 dB */
+       0x00325B65,             /* SMEM coding of 22 dB */
+       0x00388062,             /* SMEM coding of 23 dB */
+       0x003F654E,             /* SMEM coding of 24 dB */
+       0x00472194,             /* SMEM coding of 25 dB */
+       0x004FCF7C,             /* SMEM coding of 26 dB */
+       0x00598C81,             /* SMEM coding of 27 dB */
+       0x006479B7,             /* SMEM coding of 28 dB */
+       0x0070BC3D,             /* SMEM coding of 29 dB */
+       0x007E7DB9,             /* SMEM coding of 30 dB */
+};
+
+static const u32 abe_1_alpha_iir[64] = {
+       0x040002, 0x040002, 0x040002, 0x040002, /* 0 */
+       0x50E955, 0x48CA65, 0x40E321, 0x72BE78, /* 1 [ms] */
+       0x64BA68, 0x57DF14, 0x4C3D60, 0x41D690, /* 2 */
+       0x38A084, 0x308974, 0x297B00, 0x235C7C, /* 4 */
+       0x1E14B0, 0x198AF0, 0x15A800, 0x125660, /* 8 */
+       0x0F82A0, 0x0D1B5C, 0x0B113C, 0x0956CC, /* 16 */
+       0x07E054, 0x06A3B8, 0x059844, 0x04B680, /* 32 */
+       0x03F80C, 0x035774, 0x02D018, 0x025E0C, /* 64 */
+       0x7F8057, 0x6B482F, 0x5A4297, 0x4BEECB, /* 128 */
+       0x3FE00B, 0x35BAA7, 0x2D3143, 0x2602AF, /* 256 */
+       0x1FF803, 0x1AE2FB, 0x169C9F, 0x13042B, /* 512 */
+       0x0FFE03, 0x0D72E7, 0x0B4F4F, 0x0982CB, /* 1.024 [s] */
+       0x07FF83, 0x06B9CF, 0x05A7E7, 0x04C193, /* 2.048 */
+       0x03FFE3, 0x035CFF, 0x02D403, 0x0260D7, /* 4.096 */
+       0x01FFFB, 0x01AE87, 0x016A07, 0x01306F, /* 8.192 */
+       0x00FFFF, 0x00D743, 0x00B503, 0x009837,
+};
+
+static const u32 abe_alpha_iir[64] = {
+       0x000000, 0x000000, 0x000000, 0x000000, /* 0 */
+       0x5E2D58, 0x6E6B3C, 0x7E39C0, 0x46A0C5, /* 1 [ms] */
+       0x4DA2CD, 0x541079, 0x59E151, 0x5F14B9, /* 2 */
+       0x63AFC1, 0x67BB45, 0x6B4281, 0x6E51C1, /* 4 */
+       0x70F5A9, 0x733A89, 0x752C01, 0x76D4D1, /* 8 */
+       0x783EB1, 0x797251, 0x7A7761, 0x7B549D, /* 16 */
+       0x7C0FD5, 0x7CAE25, 0x7D33DD, 0x7DA4C1, /* 32 */
+       0x7E03FD, 0x7E5449, 0x7E97F5, 0x7ED0F9, /* 64 */
+       0x7F0101, 0x7F2971, 0x7F4B7D, 0x7F6825, /* 128 */
+       0x7F8041, 0x7F948D, 0x7FA59D, 0x7FB3FD, /* 256 */
+       0x7FC011, 0x7FCA3D, 0x7FD2C9, 0x7FD9F9, /* 512 */
+       0x7FE005, 0x7FE51D, 0x7FE961, 0x7FECFD, /* 1.024 [s] */
+       0x7FF001, 0x7FF28D, 0x7FF4B1, 0x7FF67D, /* 2.048 */
+       0x7FF801, 0x7FF949, 0x7FFA59, 0x7FFB41, /* 4.096 */
+       0x7FFC01, 0x7FFCA5, 0x7FFD2D, 0x7FFDA1, /* 8.192 */
+       0x7FFE01, 0x7FFE51, 0x7FFE95, 0x7FFED1,
+};
+
+/**
+ * abe_use_compensated_gain
+ * @abe: Pointer on aess handle
+ * @on_off: Enable dynamic gain compensation.
+ *
+ * Selects the automatic Mixer's gain management
+ * on_off = 1 allows the "abe_write_gain" to adjust the overall
+ * gains of the mixer to be tuned not to create saturation
+ *
+ */
+int omap_aess_use_compensated_gain(struct omap_aess *abe, int on_off)
+{
+       abe->compensated_mixer_gain = on_off;
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_use_compensated_gain);
+
+/**
+ * oamp_abe_write_equalizer
+ * @abe: Pointer on aess handle
+ * @id: name of the equalizer
+ * @param: equalizer coefficients
+ *
+ * Load the coefficients in CMEM.
+ */
+int omap_aess_write_equalizer(struct omap_aess *abe,
+                            u32 id, struct omap_aess_equ *param)
+{
+       struct omap_aess_addr equ_addr;
+       u32 length, *src;
+
+       switch (id) {
+       case OMAP_AESS_CMEM_DL1_COEFS_ID:
+               memcpy(&equ_addr,
+                      &abe->fw_info->map[OMAP_AESS_SMEM_DL1_M_EQ_DATA_ID],
+                      sizeof(struct omap_aess_addr));
+               break;
+       case OMAP_AESS_CMEM_DL2_L_COEFS_ID:
+               memcpy(&equ_addr,
+                      &abe->fw_info->map[OMAP_AESS_SMEM_DL2_M_LR_EQ_DATA_ID],
+                      sizeof(struct omap_aess_addr));
+               break;
+       case OMAP_AESS_CMEM_DL2_R_COEFS_ID:
+               memcpy(&equ_addr,
+                      &abe->fw_info->map[OMAP_AESS_SMEM_DL2_M_LR_EQ_DATA_ID],
+                      sizeof(struct omap_aess_addr));
+               break;
+       case OMAP_AESS_CMEM_SDT_COEFS_ID:
+               memcpy(&equ_addr,
+                      &abe->fw_info->map[OMAP_AESS_SMEM_SDT_F_DATA_ID],
+                      sizeof(struct omap_aess_addr));
+               break;
+       case OMAP_AESS_CMEM_96_48_AMIC_COEFS_ID:
+               memcpy(&equ_addr,
+                      &abe->fw_info->map[OMAP_AESS_SMEM_AMIC_96_48_DATA_ID],
+                      sizeof(struct omap_aess_addr));
+               break;
+       case OMAP_AESS_CMEM_96_48_DMIC_COEFS_ID:
+               memcpy(&equ_addr,
+                      &abe->fw_info->map[OMAP_AESS_SMEM_DMIC0_96_48_DATA_ID],
+                      sizeof(struct omap_aess_addr));
+               /* three DMIC are clear at the same time DMIC0 DMIC1 DMIC2 */
+               equ_addr.bytes *= 3;
+               break;
+       }
+
+       /* reset SMEM buffers before the coefficients are loaded */
+       omap_aess_reset_mem(abe, equ_addr);
+
+       length = param->equ_length;
+       src = (u32 *)((param->coef).type1);
+       omap_aess_mem_write(abe, abe->fw_info->map[id], src);
+
+       /* reset SMEM buffers after the coefficients are loaded */
+       omap_aess_reset_mem(abe, equ_addr);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_write_equalizer);
+
+
+/**
+ * omap_aess_disable_gain
+ * @abe: Pointer on aess handle
+ * @id: name of the gain
+ *
+ * Set gain to silence if not already mute or disable.
+ */
+int omap_aess_disable_gain(struct omap_aess *abe, u32 id)
+{
+       u32 f_g;
+
+       f_g = GAIN_MUTE;
+       if (!(abe->muted_gains_indicator[id] & OMAP_ABE_GAIN_DISABLED)) {
+               /* Check if we are in mute */
+               if (!(abe->muted_gains_indicator[id] &
+                     OMAP_ABE_GAIN_MUTED)) {
+                       abe->muted_gains_decibel[id] =
+                               abe->desired_gains_decibel[id];
+                       /* mute the gain */
+                       omap_aess_write_gain(abe, id, f_g);
+               }
+               abe->muted_gains_indicator[id] |= OMAP_ABE_GAIN_DISABLED;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_disable_gain);
+
+/**
+ * omap_aess_enable_gain
+ * @abe: Pointer on aess handle
+ * @id: name of the gain
+ *
+ * Restore gain if we are in disable mode.
+ */
+int omap_aess_enable_gain(struct omap_aess *abe, u32 id)
+{
+       u32 f_g;
+
+       if ((abe->muted_gains_indicator[id] & OMAP_ABE_GAIN_DISABLED)) {
+               /* restore the input parameters for mute/unmute */
+               f_g = abe->muted_gains_decibel[id];
+               abe->muted_gains_indicator[id] &=
+                       ~OMAP_ABE_GAIN_DISABLED;
+               /* unmute the gain */
+               omap_aess_write_gain(abe, id, f_g);
+       }
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_enable_gain);
+
+/**
+ * omap_aess_mute_gain
+ * @abe: Pointer on aess handle
+ * @id: name of the gain
+ *
+ * Set gain to silence if not already mute.
+ */
+int omap_aess_mute_gain(struct omap_aess *abe, u32 id)
+{
+       u32 f_g;
+
+       f_g = GAIN_MUTE;
+       if (!abe->muted_gains_indicator[id]) {
+               abe->muted_gains_decibel[id] =
+                       abe->desired_gains_decibel[id];
+               /* mute the gain */
+               omap_aess_write_gain(abe, id, f_g);
+       }
+       abe->muted_gains_indicator[id] |= OMAP_ABE_GAIN_MUTED;
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_mute_gain);
+/**
+ * omap_aess_unmute_gain
+ * @abe: Pointer on aess handle
+ * @id: name of the gain
+ *
+ * Restore gain after mute.
+ */
+int omap_aess_unmute_gain(struct omap_aess *abe, u32 id)
+{
+       u32 f_g;
+       if ((abe->muted_gains_indicator[id] & OMAP_ABE_GAIN_MUTED)) {
+               /* restore the input parameters for mute/unmute */
+               f_g = abe->muted_gains_decibel[id];
+               abe->muted_gains_indicator[id] &=
+                       ~OMAP_ABE_GAIN_MUTED;
+               /* unmute the gain */
+               omap_aess_write_gain(abe, id, f_g);
+       }
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_unmute_gain);
+
+/**
+ * omap_aess_write_gain
+ * @abe: Pointer on aess handle
+ * @id: gain name or mixer name
+ * @f_g: input gain for the mixer
+ *
+ * Loads the gain coefficients to FW memory. This API can be called when
+ * the corresponding MIXER is not activated. After reloading the firmware
+ * the default coefficients corresponds to "all input and output mixer's gain
+ * in mute state". A mixer is disabled with a network reconfiguration
+ * corresponding to an OPP value.
+ */
+int omap_aess_write_gain(struct omap_aess *abe,
+                       u32 id, s32 f_g)
+{
+       u32 lin_g, mixer_target;
+       s32 gain_index;
+
+       gain_index = ((f_g - min_mdb) / 100);
+       gain_index = maximum(gain_index, 0);
+       gain_index = minimum(gain_index, sizeof_db2lin_table);
+       lin_g = abe_db2lin_table[gain_index];
+       /* save the input parameters for mute/unmute */
+       abe->desired_gains_linear[id] = lin_g;
+       abe->desired_gains_decibel[id] = f_g;
+
+       /* SMEM address in bytes */
+       mixer_target = abe->fw_info->map[OMAP_AESS_SMEM_GTARGET1_ID].offset;
+       mixer_target += (id<<2);
+
+       if (!abe->compensated_mixer_gain) {
+               if (!abe->muted_gains_indicator[id])
+                       /* load the S_G_Target SMEM table */
+                       omap_abe_mem_write(abe, OMAP_ABE_SMEM,
+                                          mixer_target, (u32 *)&lin_g,
+                                          sizeof(lin_g));
+               else
+                       /* update muted gain with new value */
+                       abe->muted_gains_decibel[id] = f_g;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_write_gain);
+
+/**
+ * omap_aess_write_gain_ramp
+ * @abe: Pointer on aess handle
+ * @id: gain name or mixer name
+ * @ramp: Gaim ramp time
+ *
+ * Loads the gain ramp for the associated gain.
+ */
+int omap_aess_write_gain_ramp(struct omap_aess *abe, u32 id, u32 ramp)
+{
+       u32 mixer_target;
+       u32 alpha, beta;
+       u32 ramp_index;
+
+       abe->desired_ramp_delay_ms[id] = ramp;
+
+       /* SMEM address in bytes */
+       mixer_target = abe->fw_info->map[OMAP_AESS_SMEM_GTARGET1_ID].offset;
+       mixer_target += (id<<2);
+
+       ramp = maximum(minimum(RAMP_MAXLENGTH, ramp), RAMP_MINLENGTH);
+       /* ramp data should be interpolated in the table instead */
+       ramp_index = 3;
+       if ((RAMP_2MS <= ramp) && (ramp < RAMP_5MS))
+               ramp_index = 8;
+       if ((RAMP_5MS <= ramp) && (ramp < RAMP_50MS))
+               ramp_index = 24;
+       if ((RAMP_50MS <= ramp) && (ramp < RAMP_500MS))
+               ramp_index = 36;
+       if (ramp > RAMP_500MS)
+               ramp_index = 48;
+       beta = abe_alpha_iir[ramp_index];
+       alpha = abe_1_alpha_iir[ramp_index];
+       /* CMEM bytes address */
+       mixer_target = abe->fw_info->map[OMAP_AESS_CMEM_1_ALPHA_ID].offset;
+       /* a pair of gains is updated once in the firmware */
+       mixer_target += ((id) >> 1) << 2;
+       /* load the ramp delay data */
+       omap_abe_mem_write(abe, OMAP_ABE_CMEM, mixer_target,
+                          (u32 *)&alpha, sizeof(alpha));
+       /* CMEM bytes address */
+       mixer_target = abe->fw_info->map[OMAP_AESS_CMEM_ALPHA_ID].offset;
+       /* a pair of gains is updated once in the firmware */
+       mixer_target += ((id) >> 1) << 2;
+       omap_abe_mem_write(abe, OMAP_ABE_CMEM, mixer_target,
+                          (u32 *)&beta, sizeof(beta));
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_write_gain_ramp);
+
+/**
+ * omap_aess_write_mixer
+ * @abe: Pointer on aess handle
+ * @id: name of the mixer
+ * @f_g: input gain for the mixer
+ *
+ * Load the gain coefficients in FW memory. This API can be called when
+ * the corresponding MIXER is not activated. After reloading the firmware
+ * the default coefficients corresponds to "all input and output mixer's
+ * gain in mute state". A mixer is disabled with a network reconfiguration
+ * corresponding to an OPP value.
+ */
+int omap_aess_write_mixer(struct omap_aess *abe, u32 id, s32 f_g)
+{
+
+       omap_aess_write_gain(abe, id, f_g);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_write_mixer);
+
+/**
+ * omap_aess_read_gain
+ * @abe: Pointer on aess handle
+ * @id: name of the mixer
+ * @f_g: pointer on the gain for the mixer
+ *
+ * Read the gain coefficients in FW memory. This API can be called when
+ * the corresponding MIXER is not activated. After reloading the firmware
+ * the default coefficients corresponds to "all input and output mixer's
+ * gain in mute state". A mixer is disabled with a network reconfiguration
+ * corresponding to an OPP value.
+ */
+int omap_aess_read_gain(struct omap_aess *abe, u32 id, u32 *f_g)
+{
+       u32 mixer_target, i;
+
+       /* SMEM bytes address */
+       mixer_target = abe->fw_info->map[OMAP_AESS_SMEM_GTARGET1_ID].offset;
+       mixer_target += (id<<2);
+       if (!abe->muted_gains_indicator[id]) {
+               /* load the S_G_Target SMEM table */
+               omap_abe_mem_read(abe, OMAP_ABE_SMEM, mixer_target,
+                                 (u32 *)f_g, sizeof(*f_g));
+               for (i = 0; i < sizeof_db2lin_table; i++) {
+                               if (abe_db2lin_table[i] == *f_g)
+                                       goto found;
+               }
+               *f_g = 0;
+               return -1;
+found:
+               *f_g = (i * 100) + min_mdb;
+       } else {
+               /* update muted gain with new value */
+               *f_g = abe->muted_gains_decibel[id];
+       }
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_read_gain);
+
+/**
+ * abe_read_mixer
+ * @abe: Pointer on aess handle
+ * @id: name of the mixer
+ * @f_g: pointer on the gain for the mixer
+ *
+ * Read the gain coefficients in FW memory. This API can be called when
+ * the corresponding MIXER is not activated. After reloading the firmware
+ * the default coefficients corresponds to "all input and output mixer's
+ * gain in mute state". A mixer is disabled with a network reconfiguration
+ * corresponding to an OPP value.
+ */
+int omap_aess_read_mixer(struct omap_aess *abe, u32 id, u32 *f_g)
+{
+       omap_aess_read_gain(abe, id, f_g);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_read_mixer);
+
+/**
+ * abe_reset_gain_mixer
+ * @abe: Pointer on aess handle
+ * @id: name of the mixer
+ *
+ * restart the working gain value of the mixers when a port is enabled
+ */
+void omap_aess_reset_gain_mixer(struct omap_aess *abe, u32 id)
+{
+       u32 lin_g, mixer_target;
+
+       /* SMEM bytes address for the CURRENT gain values */
+       mixer_target = abe->fw_info->map[OMAP_AESS_SMEM_GCURRENT_ID].offset;
+       mixer_target += (id<<2);
+       lin_g = 0;
+       /* load the S_G_Target SMEM table */
+       omap_abe_mem_write(abe, OMAP_ABE_SMEM, mixer_target,
+                          (u32 *)&lin_g, sizeof(lin_g));
+}
diff --git a/sound/soc/omap/aess/abe_gain.h b/sound/soc/omap/aess/abe_gain.h
new file mode 100644 (file)
index 0000000..aedef8c
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _ABE_GAIN_H_
+#define _ABE_GAIN_H_
+
+#include "abe_typ.h"
+
+#define OMAP_ABE_GAIN_MUTED     (0x0001<<0)
+#define OMAP_ABE_GAIN_DISABLED  (0x0001<<1)
+
+#define OMAP_AESS_GAIN_DMIC1_LEFT    0
+#define OMAP_AESS_GAIN_DMIC1_RIGHT   1
+#define OMAP_AESS_GAIN_DMIC2_LEFT    2
+#define OMAP_AESS_GAIN_DMIC2_RIGHT   3
+#define OMAP_AESS_GAIN_DMIC3_LEFT    4
+#define OMAP_AESS_GAIN_DMIC3_RIGHT   5
+#define OMAP_AESS_GAIN_AMIC_LEFT     6
+#define OMAP_AESS_GAIN_AMIC_RIGHT    7
+#define OMAP_AESS_GAIN_DL1_LEFT      8
+#define OMAP_AESS_GAIN_DL1_RIGHT     9
+#define OMAP_AESS_GAIN_DL2_LEFT     10
+#define OMAP_AESS_GAIN_DL2_RIGHT    11
+#define OMAP_AESS_GAIN_SPLIT_LEFT   12
+#define OMAP_AESS_GAIN_SPLIT_RIGHT  13
+#define OMAP_AESS_MIXDL1_MM_DL      14
+#define OMAP_AESS_MIXDL1_MM_UL2     15
+#define OMAP_AESS_MIXDL1_VX_DL      16
+#define OMAP_AESS_MIXDL1_TONES      17
+#define OMAP_AESS_MIXDL2_MM_DL      18
+#define OMAP_AESS_MIXDL2_MM_UL2     19
+#define OMAP_AESS_MIXDL2_VX_DL      20
+#define OMAP_AESS_MIXDL2_TONES      21
+#define OMAP_AESS_MIXECHO_DL1       22
+#define OMAP_AESS_MIXECHO_DL2       23
+#define OMAP_AESS_MIXSDT_UL         24
+#define OMAP_AESS_MIXSDT_DL         25
+#define OMAP_AESS_MIXVXREC_MM_DL    26
+#define OMAP_AESS_MIXVXREC_TONES    27
+#define OMAP_AESS_MIXVXREC_VX_UL    28
+#define OMAP_AESS_MIXVXREC_VX_DL    29
+#define OMAP_AESS_MIXAUDUL_MM_DL    30
+#define OMAP_AESS_MIXAUDUL_TONES    31
+#define OMAP_AESS_MIXAUDUL_UPLINK   32
+#define OMAP_AESS_MIXAUDUL_VX_DL    33
+#define OMAP_AESS_GAIN_BTUL_LEFT    34
+#define OMAP_AESS_GAIN_BTUL_RIGHT   35
+
+void omap_aess_reset_gain_mixer(struct omap_aess *abe, u32 id);
+int omap_aess_write_gain_ramp(struct omap_aess *abe, u32 id, u32 ramp);
+
+#endif /* _ABE_GAIN_H_ */
diff --git a/sound/soc/omap/aess/abe_ini.c b/sound/soc/omap/aess/abe_ini.c
new file mode 100644 (file)
index 0000000..edcf861
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#define DEBUG
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#include "abe.h"
+#include "abe_aess.h"
+#include "abe_gain.h"
+#include "abe_mem.h"
+#include "abe_port.h"
+#include "abe_seq.h"
+
+/* FW version that this HAL supports.
+ * We cheat and since we include the FW in the driver atm we can get the
+ * FW version based on aess_firmware_array[5]. This should be updated when the
+ * FW is removed from HAL code for modular builds.
+ */
+#define OMAP_ABE_SUPPORTED_FW_VERSION 0x09590
+
+/**
+ * abe_reset_all_ports
+ * @abe: Pointer on aess handle
+ *
+ * load default configuration for all features
+ */
+static void omap_aess_reset_all_ports(struct omap_aess *abe)
+{
+       u16 i;
+
+       for (i = 0; i < LAST_PORT_ID; i++)
+               omap_aess_reset_port(abe, i);
+
+       /* mixers' configuration */
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXDL1_MM_DL, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXDL1_MM_UL2, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXDL1_VX_DL, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXDL1_TONES, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXDL2_TONES, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXDL2_VX_DL, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXDL2_MM_DL, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXDL2_MM_UL2, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXSDT_UL, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXSDT_DL, GAIN_0dB);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXECHO_DL1, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXECHO_DL2, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXAUDUL_MM_DL, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXAUDUL_TONES, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXAUDUL_UPLINK, GAIN_0dB);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXAUDUL_VX_DL, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXVXREC_TONES, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXVXREC_VX_DL, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXVXREC_MM_DL, MUTE_GAIN);
+       omap_aess_write_mixer(abe, OMAP_AESS_MIXVXREC_VX_UL, MUTE_GAIN);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DMIC1_LEFT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DMIC1_RIGHT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DMIC2_LEFT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DMIC2_RIGHT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DMIC3_LEFT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DMIC3_RIGHT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_AMIC_LEFT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_AMIC_RIGHT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_SPLIT_LEFT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_SPLIT_RIGHT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DL1_LEFT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DL1_RIGHT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DL2_LEFT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DL2_RIGHT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_BTUL_LEFT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_BTUL_RIGHT, GAIN_0dB);
+}
+
+/**
+ * omap_aess_init_gain_ramp
+ * @abe: Pointer on aess handle
+ *
+ * load default gain ramp configuration.
+ */
+static void omap_aess_init_gain_ramp(struct omap_aess *abe)
+{
+       /* Ramps configuration */
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXDL1_MM_DL, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXDL1_MM_UL2, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXDL1_VX_DL, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXDL1_TONES, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXDL2_TONES, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXDL2_VX_DL, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXDL2_MM_DL, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXDL2_MM_UL2, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXSDT_UL, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXSDT_DL, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXECHO_DL1, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXECHO_DL2, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXAUDUL_MM_DL, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXAUDUL_TONES, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXAUDUL_UPLINK, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXAUDUL_VX_DL, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXVXREC_TONES, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXVXREC_VX_DL, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXVXREC_MM_DL, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_MIXVXREC_VX_UL, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_DMIC1_LEFT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_DMIC1_RIGHT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_DMIC2_LEFT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_DMIC2_RIGHT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_DMIC3_LEFT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_DMIC3_RIGHT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_AMIC_LEFT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_AMIC_RIGHT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_SPLIT_LEFT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_SPLIT_RIGHT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_DL1_LEFT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_DL1_RIGHT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_DL2_LEFT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_DL2_RIGHT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_BTUL_LEFT, RAMP_2MS);
+       omap_aess_write_gain_ramp(abe, OMAP_AESS_GAIN_BTUL_RIGHT, RAMP_2MS);
+}
+
+/**
+ * omap_aess_init_mem - Allocate Kernel space memory map for ABE
+ * @abe: Pointer on abe handle
+ * @dev: Pointer on device handle
+ * @_io_base: Pointer on the different AESS memory banks (DT or HW data)
+ * @fw_header: Pointer on the firmware header commit from the FS.
+ *
+ * Memory map of ABE memory space for PMEM/DMEM/SMEM/DMEM
+ */
+int omap_aess_init_mem(struct omap_aess *abe, struct device *dev,
+       void __iomem **_io_base, u32 *fw_header)
+{
+       int i, offset = 0;
+       u32 count;
+
+       abe->dev = dev;
+
+       for (i = 0; i < 5; i++)
+               abe->io_base[i] = _io_base[i];
+
+       dev_dbg(abe->dev, "DMEM bank at 0x%p\n", abe->io_base[OMAP_ABE_DMEM]);
+       dev_dbg(abe->dev, "CMEM bank at 0x%p\n", abe->io_base[OMAP_ABE_CMEM]);
+       dev_dbg(abe->dev, "SMEM bank at 0x%p\n", abe->io_base[OMAP_ABE_SMEM]);
+       dev_dbg(abe->dev, "PMEM bank at 0x%p\n", abe->io_base[OMAP_ABE_PMEM]);
+       dev_dbg(abe->dev, "AESS bank at 0x%p\n", abe->io_base[OMAP_ABE_AESS]);
+
+       abe->fw_info = kzalloc(sizeof(struct omap_aess_mapping), GFP_KERNEL);
+       if (abe->fw_info == NULL)
+               return -ENOMEM;
+
+       abe->fw_info->init_table = kzalloc(sizeof(struct omap_aess_init_task), GFP_KERNEL);
+       if (abe->fw_info->init_table == NULL) {
+               kfree(abe->fw_info);
+               return -ENOMEM;
+       }
+
+       /* get mapping */
+       count = fw_header[offset];
+       dev_dbg(abe->dev, "Map %d items of size 0x%x at offset 0x%x\n", count,
+               sizeof(struct omap_aess_addr), offset << 2);
+       abe->fw_info->map = (struct omap_aess_addr *)&fw_header[++offset];
+       offset += (sizeof(struct omap_aess_addr) * count) / 4;
+
+       /* get label IDs */
+       count = fw_header[offset];
+       dev_dbg(abe->dev, "Labels %d at offset 0x%x\n", count, offset << 2);
+       abe->fw_info->label_id = &fw_header[++offset];
+       offset += count;
+
+       /* get function IDs */
+       count = fw_header[offset];
+       dev_dbg(abe->dev, "Functions %d at offset 0x%x\n", count,
+               offset << 2);
+       abe->fw_info->fct_id = &fw_header[++offset];
+       offset += count;
+
+       /* get tasks */
+       count = fw_header[offset];
+       dev_dbg(abe->dev, "Tasks %d of size 0x%x at offset 0x%x\n", count,
+               sizeof(struct omap_aess_task), offset << 2);
+       abe->fw_info->init_table->nb_task = count;
+       abe->fw_info->init_table->task = (struct omap_aess_task *)&fw_header[++offset];
+       offset += (sizeof(struct omap_aess_task) * count) / 4;
+
+       /* get ports */
+       count = fw_header[offset];
+       dev_dbg(abe->dev, "Ports %d of size 0x%x at offset 0x%x\n", count,
+               sizeof(struct omap_aess_port), offset << 2);
+       abe->fw_info->port = (struct omap_aess_port *)&fw_header[++offset];
+       offset += (sizeof(struct omap_aess_port) * count) / 4;
+
+       /* get ping pong port */
+       dev_dbg(abe->dev, "Ping pong port at offset 0x%x\n", offset << 2);
+       abe->fw_info->ping_pong = (struct omap_aess_port *)&fw_header[offset];
+       offset += sizeof(struct omap_aess_port) / 4;
+
+       /* get DL1 mono mixer */
+       dev_dbg(abe->dev, "DL1 mono mixer at offset 0x%x\n", offset << 2);
+       abe->fw_info->dl1_mono_mixer = (struct omap_aess_task *)&fw_header[offset];
+       offset += (sizeof(struct omap_aess_task) / 4) * 2;
+
+       /* get DL2 mono mixer */
+       dev_dbg(abe->dev, "DL2 mono mixer at offset 0x%x\n", offset << 2);
+       abe->fw_info->dl2_mono_mixer = (struct omap_aess_task *)&fw_header[offset];
+       offset += (sizeof(struct omap_aess_task) / 4) * 2;
+
+       /* get AUDUL mono mixer */
+       dev_dbg(abe->dev, "AUDUL mixer at offset 0x%x\n", offset << 2);
+       abe->fw_info->audul_mono_mixer = (struct omap_aess_task *)&fw_header[offset];
+       offset += (sizeof(struct omap_aess_task) / 4) * 2;
+
+       /* ASRC */
+       dev_dbg(abe->dev, "ASRC at offset 0x%x\n", offset << 2);
+       abe->fw_info->asrc = &fw_header[offset];
+
+       mutex_init(&abe->mutex);
+       return 0;
+
+}
+EXPORT_SYMBOL(omap_aess_init_mem);
+
+/**
+ * omap_aess_load_fw_param - Load the ABE FW inside AESS memories
+ * @abe: Pointer on abe handle
+ * @data: Pointer on the ABE firmware (after the header)
+ *
+ * Load the different AESS memories PMEM/DMEM/SMEM/DMEM
+ */
+static int omap_aess_load_fw_param(struct omap_aess *abe, u32 *data)
+{
+       u32 pmem_size, dmem_size, smem_size, cmem_size;
+       u32 *pmem_ptr, *dmem_ptr, *smem_ptr, *cmem_ptr, *fw_ptr;
+
+       /* Analyze FW memories banks sizes */
+       fw_ptr = data;
+       abe->firmware_version_number = *fw_ptr++;
+       pmem_size = *fw_ptr++;
+       cmem_size = *fw_ptr++;
+       dmem_size = *fw_ptr++;
+       smem_size = *fw_ptr++;
+       pmem_ptr = fw_ptr;
+       cmem_ptr = pmem_ptr + (pmem_size >> 2);
+       dmem_ptr = cmem_ptr + (cmem_size >> 2);
+       smem_ptr = dmem_ptr + (dmem_size >> 2);
+
+       omap_abe_mem_write(abe, OMAP_ABE_PMEM, 0, pmem_ptr, pmem_size);
+       omap_abe_mem_write(abe, OMAP_ABE_CMEM, 0, cmem_ptr, cmem_size);
+       omap_abe_mem_write(abe, OMAP_ABE_SMEM, 0, smem_ptr, smem_size);
+       omap_abe_mem_write(abe, OMAP_ABE_DMEM, 0, dmem_ptr, dmem_size);
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_load_fw_param);
+
+/**
+ * omap_aess_load_fw - Load ABE Firmware and initialize memories
+ * @abe: Pointer on aess handle
+ * @firmware: Pointer on the ABE firmware (after the header)
+ *
+ */
+int omap_aess_load_fw(struct omap_aess *abe, u32 *firmware)
+{
+       omap_aess_load_fw_param(abe, firmware);
+       omap_aess_reset_all_ports(abe);
+       omap_aess_init_gain_ramp(abe);
+       omap_aess_build_scheduler_table(abe);
+       omap_aess_reset_all_sequence(abe);
+       omap_aess_select_main_port(abe, OMAP_ABE_PDM_DL_PORT);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_load_fw);
+
+/**
+ * abe_reload_fw - Reload ABE Firmware after OFF mode
+ * @abe: Pointer on aess handle
+ * @firmware: Pointer on the ABE firmware (after the header)
+ */
+int omap_aess_reload_fw(struct omap_aess *abe, u32 *firmware)
+{
+       omap_aess_load_fw_param(abe, firmware);
+       omap_aess_init_gain_ramp(abe);
+       omap_aess_build_scheduler_table(abe);
+       /* IRQ circular read pointer in DMEM */
+       abe->irq_dbg_read_ptr = 0;
+       /* Restore Gains not managed by the drivers */
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_SPLIT_LEFT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_SPLIT_RIGHT, GAIN_0dB);
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_reload_fw);
+
+/*
+ * omap_abe_get_supported_fw_version - return supported FW version number.
+ */
+u32 omap_abe_get_supported_fw_version(void)
+{
+       return OMAP_ABE_SUPPORTED_FW_VERSION;
+}
+EXPORT_SYMBOL(omap_abe_get_supported_fw_version);
diff --git a/sound/soc/omap/aess/abe_mem.h b/sound/soc/omap/aess/abe_mem.h
new file mode 100644 (file)
index 0000000..3b90ee2
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _ABE_MEM_H_
+#define _ABE_MEM_H_
+
+#ifdef __KERNEL__
+#include <asm/io.h>
+#endif
+
+#define OMAP_ABE_DMEM 0
+#define OMAP_ABE_CMEM 1
+#define OMAP_ABE_SMEM 2
+#define OMAP_ABE_PMEM 3
+#define OMAP_ABE_AESS 4
+
+struct omap_aess_addr {
+       int bank;
+       unsigned int offset;
+       unsigned int bytes;
+};
+
+#define OMAP_AESS_DMEM_MULTIFRAME_ID   0
+#define OMAP_AESS_DMEM_DMIC_UL_FIFO_ID 1
+#define OMAP_AESS_DMEM_MCPDM_UL_FIFO_ID        2
+#define OMAP_AESS_DMEM_BT_UL_FIFO_ID   3
+#define OMAP_AESS_DMEM_MM_UL_FIFO_ID   4
+#define OMAP_AESS_DMEM_MM_UL2_FIFO_ID  5
+#define OMAP_AESS_DMEM_VX_UL_FIFO_ID   6
+#define OMAP_AESS_DMEM_MM_DL_FIFO_ID   7
+#define OMAP_AESS_DMEM_VX_DL_FIFO_ID   8
+#define OMAP_AESS_DMEM_TONES_DL_FIFO_ID        9
+#define OMAP_AESS_DMEM_MCASP_DL_FIFO_ID        10
+#define OMAP_AESS_DMEM_BT_DL_FIFO_ID   11
+#define OMAP_AESS_DMEM_MCPDM_DL_FIFO_ID        12
+#define OMAP_AESS_DMEM_MM_EXT_OUT_FIFO_ID      13
+#define OMAP_AESS_DMEM_MM_EXT_IN_FIFO_ID       14
+#define OMAP_AESS_SMEM_DMIC0_96_48_DATA_ID     15
+#define OMAP_AESS_SMEM_DMIC1_96_48_DATA_ID     16
+#define OMAP_AESS_SMEM_DMIC2_96_48_DATA_ID     17
+#define OMAP_AESS_SMEM_AMIC_96_48_DATA_ID      18
+#define OMAP_AESS_SMEM_BT_UL_ID        19
+#define OMAP_AESS_SMEM_BT_UL_8_48_HP_DATA_ID   20
+#define OMAP_AESS_SMEM_BT_UL_8_48_LP_DATA_ID   21
+#define OMAP_AESS_SMEM_BT_UL_16_48_HP_DATA_ID  22
+#define OMAP_AESS_SMEM_BT_UL_16_48_LP_DATA_ID  23
+#define OMAP_AESS_SMEM_MM_UL2_ID       24
+#define OMAP_AESS_SMEM_MM_UL_ID        25
+#define OMAP_AESS_SMEM_VX_UL_ID        26
+#define OMAP_AESS_SMEM_VX_UL_48_8_HP_DATA_ID   27
+#define OMAP_AESS_SMEM_VX_UL_48_8_LP_DATA_ID   28
+#define OMAP_AESS_SMEM_VX_UL_48_16_HP_DATA_ID  29
+#define OMAP_AESS_SMEM_VX_UL_48_16_LP_DATA_ID  30
+#define OMAP_AESS_SMEM_MM_DL_ID        31
+#define OMAP_AESS_SMEM_MM_DL_44P1_ID   32
+#define OMAP_AESS_SMEM_MM_DL_44P1_XK_ID        33
+#define OMAP_AESS_SMEM_VX_DL_ID        34
+#define OMAP_AESS_SMEM_VX_DL_8_48_HP_DATA_ID   35
+#define OMAP_AESS_SMEM_VX_DL_8_48_LP_DATA_ID   36
+#define OMAP_AESS_SMEM_VX_DL_8_48_OSR_LP_DATA_ID       37
+#define OMAP_AESS_SMEM_VX_DL_16_48_HP_DATA_ID  38
+#define OMAP_AESS_SMEM_VX_DL_16_48_LP_DATA_ID  39
+#define OMAP_AESS_SMEM_TONES_ID        40
+#define OMAP_AESS_SMEM_TONES_44P1_ID   41
+#define OMAP_AESS_SMEM_TONES_44P1_XK_ID        42
+#define OMAP_AESS_SMEM_MCASP1_ID       43
+#define OMAP_AESS_SMEM_BT_DL_ID        44
+#define OMAP_AESS_SMEM_BT_DL_8_48_OSR_LP_DATA_ID       45
+#define OMAP_AESS_SMEM_BT_DL_48_8_HP_DATA_ID   46
+#define OMAP_AESS_SMEM_BT_DL_48_8_LP_DATA_ID   47
+#define OMAP_AESS_SMEM_BT_DL_48_16_HP_DATA_ID  48
+#define OMAP_AESS_SMEM_BT_DL_48_16_LP_DATA_ID  49
+#define OMAP_AESS_SMEM_DL2_M_LR_EQ_DATA_ID     50
+#define OMAP_AESS_SMEM_DL1_M_EQ_DATA_ID        51
+#define OMAP_AESS_SMEM_EARP_48_96_LP_DATA_ID   52
+#define OMAP_AESS_SMEM_IHF_48_96_LP_DATA_ID    53
+#define OMAP_AESS_SMEM_DC_HS_ID        54
+#define OMAP_AESS_SMEM_DC_HF_ID        55
+#define OMAP_AESS_SMEM_SDT_F_DATA_ID   56
+#define OMAP_AESS_SMEM_GTARGET1_ID     57
+#define OMAP_AESS_SMEM_GCURRENT_ID     58
+#define OMAP_AESS_CMEM_DL1_COEFS_ID    59
+#define OMAP_AESS_CMEM_DL2_L_COEFS_ID  60
+#define OMAP_AESS_CMEM_DL2_R_COEFS_ID  61
+#define OMAP_AESS_CMEM_SDT_COEFS_ID    62
+#define OMAP_AESS_CMEM_96_48_AMIC_COEFS_ID     63
+#define OMAP_AESS_CMEM_96_48_DMIC_COEFS_ID     64
+#define OMAP_AESS_CMEM_1_ALPHA_ID      65
+#define OMAP_AESS_CMEM_ALPHA_ID        66
+#define OMAP_AESS_DMEM_SLOT23_CTRL_ID  67
+#define OMAP_AESS_DMEM_AUPLINKROUTING_ID       68
+#define OMAP_AESS_DMEM_MAXTASKBYTESINSLOT_ID   69
+#define OMAP_AESS_DMEM_PINGPONGDESC_ID 70
+#define OMAP_AESS_DMEM_IODESCR_ID      71
+#define OMAP_AESS_DMEM_MCUIRQFIFO_ID   72
+#define OMAP_AESS_DMEM_PING_ID 73
+#define OMAP_AESS_DMEM_DEBUG_FIFO_ID   74
+#define OMAP_AESS_DMEM_DEBUG_FIFO_HAL_ID       75
+#define OMAP_AESS_DMEM_DEBUG_HAL_TASK_ID       76
+#define OMAP_AESS_DMEM_LOOPCOUNTER_ID  77
+#define OMAP_AESS_DMEM_FWMEMINITDESCR_ID       78
+
+#ifdef __KERNEL__
+
+/* Distinction between Read and Write from/to ABE memory
+ * is useful for simulation tool */
+static inline void omap_abe_mem_write(struct omap_aess *abe, int bank,
+                               u32 offset, u32 *src, size_t bytes)
+{
+       memcpy((void __force *)(abe->io_base[bank] + offset), src, bytes);
+}
+
+static inline void omap_abe_mem_read(struct omap_aess *abe, int bank,
+                               u32 offset, u32 *dest, size_t bytes)
+{
+       memcpy(dest, (void __force *)(abe->io_base[bank] + offset), bytes);
+}
+
+static inline u32 omap_aess_reg_readl(struct omap_aess *abe, u32 offset)
+{
+       return __raw_readl(abe->io_base[OMAP_ABE_AESS] + offset);
+}
+
+static inline void omap_aess_reg_writel(struct omap_aess *abe,
+                               u32 offset, u32 val)
+{
+       __raw_writel(val, (abe->io_base[OMAP_ABE_AESS] + offset));
+}
+
+static inline void *omap_abe_reset_mem(struct omap_aess *abe, int bank,
+                       u32 offset, size_t bytes)
+{
+       return memset((u32 *)(abe->io_base[bank] + offset), 0, bytes);
+}
+
+static inline void omap_aess_mem_write(struct omap_aess *abe,
+                       struct omap_aess_addr addr, u32 *src)
+{
+       memcpy((void __force *)(abe->io_base[addr.bank] + addr.offset), src, addr.bytes);
+}
+
+static inline void omap_aess_mem_read(struct omap_aess *abe,
+                               struct omap_aess_addr addr, u32 *dest)
+{
+       memcpy(dest, (void __force *)(abe->io_base[addr.bank] + addr.offset), addr.bytes);
+}
+
+static inline void *omap_aess_reset_mem(struct omap_aess *abe,
+                       struct omap_aess_addr addr)
+{
+       return memset((void __force *)(abe->io_base[addr.bank] + addr.offset), 0, addr.bytes);
+}
+
+#endif /* __KERNEL__ */
+#endif /*_ABE_MEM_H_*/
diff --git a/sound/soc/omap/aess/abe_port.c b/sound/soc/omap/aess/abe_port.c
new file mode 100644 (file)
index 0000000..2fcc382
--- /dev/null
@@ -0,0 +1,1927 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include "abe_typ.h"
+#include "abe.h"
+#include "abe_port.h"
+#include "abe_dbg.h"
+#include "abe_mem.h"
+#include "abe_gain.h"
+#include "abe_seq.h"
+
+#include "abe_def.h"
+
+/*
+ * GLOBAL DEFINITION
+ */
+#define ATC_SIZE 8             /* 8 bytes per descriptors */
+
+struct omap_abe_atc_desc {
+       unsigned rdpt:7;        /* first 32bits word of the descriptor */
+       unsigned reserved0:1;
+       unsigned cbsize:7;
+       unsigned irqdest:1;
+       unsigned cberr:1;
+       unsigned reserved1:5;
+       unsigned cbdir:1;
+       unsigned nw:1;
+       unsigned wrpt:7;
+       unsigned reserved2:1;
+       unsigned badd:12;       /* second 32bits word of the descriptor */
+       unsigned iter:7;        /* iteration field overlaps 16-bit boundary */
+       unsigned srcid:6;
+       unsigned destid:6;
+       unsigned desen:1;
+};
+
+struct ABE_SPingPongDescriptor {
+       /* 0: [W] asrc output used for the next ASRC call (+/- 1 / 0) */
+       u16 drift_ASRC;
+       /* 2: [W] asrc output used for controlling the number of
+          samples to be exchanged (+/- 1 / 0) */
+       u16 drift_io;
+       /* 4: DMAReq address or HOST IRQ buffer address (ATC ADDRESS) */
+       u16 hw_ctrl_addr;
+       /* 6: index of the copy subroutine */
+       u8 copy_func_index;
+       /* 7: X number of SMEM samples to move */
+       u8 x_io;
+       /* 8: 0 for mono data, 1 for stereo data */
+       u8 data_size;
+       /* 9: internal SMEM buffer INITPTR pointer index */
+       u8 smem_addr;
+       /* 10: data content to be loaded to "hw_ctrl_addr" */
+       u8 atc_irq_data;
+       /* 11: ping/pong buffer flag */
+       u8 counter;
+       /* 12: reseved */
+       u16 dummy1;
+       /* 14: reseved */
+       u16 dummy2;
+       /* 16 For 12/11 in case of 44.1 mode (same address as SIO desc)*/
+       u16 split_addr1;
+       /* 18: reseved */
+       u16 dummy3;
+       /* 20: current Base address of the working buffer */
+       u16 workbuff_BaseAddr;
+       /* 14: samples left in the working buffer */
+       u16 workbuff_Samples;
+       /* 16: Base address of the ping/pong buffer 0 */
+       u16 nextbuff0_BaseAddr;
+       /* 18: samples available in the ping/pong buffer 0 */
+       u16 nextbuff0_Samples;
+       /* 20: Base address of the ping/pong buffer 1 */
+       u16 nextbuff1_BaseAddr;
+       /* 22: samples available in the ping/pong buffer 1 */
+       u16 nextbuff1_Samples;
+};
+
+/*
+ * MAIN PORT SELECTION
+ */
+static const u32 abe_port_priority[LAST_PORT_ID - 1] = {
+       OMAP_ABE_PDM_DL_PORT,
+       OMAP_ABE_PDM_UL_PORT,
+       OMAP_ABE_MM_EXT_OUT_PORT,
+       OMAP_ABE_MM_EXT_IN_PORT,
+       OMAP_ABE_DMIC_PORT,
+       OMAP_ABE_MM_UL_PORT,
+       OMAP_ABE_MM_UL2_PORT,
+       OMAP_ABE_MM_DL_PORT,
+       OMAP_ABE_TONES_DL_PORT,
+       OMAP_ABE_BT_VX_DL_PORT,
+       OMAP_ABE_BT_VX_UL_PORT,
+       OMAP_ABE_VX_UL_PORT,
+       OMAP_ABE_VX_DL_PORT,
+       OMAP_ABE_MCASP_DL_PORT,
+};
+
+/*
+ * AESS/ATC destination and source address translation (except McASPs)
+ * from the original 64bits words address
+ */
+static const u32 abe_atc_dstid[ABE_ATC_DESC_SIZE >> 3] = {
+       /* DMA_0 DMIC PDM_DL PDM_UL McB1TX McB1RX McB2TX McB2RX 0 .. 7 */
+       0, 0, 12, 0, 1, 0, 2, 0,
+       /* McB3TX McB3RX SLIMT0 SLIMT1 SLIMT2 SLIMT3 SLIMT4 SLIMT5 8 .. 15 */
+       3, 0, 4, 5, 6, 7, 8, 9,
+       /* SLIMT6 SLIMT7 SLIMR0 SLIMR1 SLIMR2 SLIMR3 SLIMR4 SLIMR5 16 .. 23 */
+       10, 11, 0, 0, 0, 0, 0, 0,
+       /* SLIMR6 SLIMR7 McASP1X ----- ----- McASP1R ----- ----- 24 .. 31 */
+       0, 0, 14, 0, 0, 0, 0, 0,
+       /* CBPrT0 CBPrT1 CBPrT2 CBPrT3 CBPrT4 CBPrT5 CBPrT6 CBPrT7 32 .. 39 */
+       63, 63, 63, 63, 63, 63, 63, 63,
+       /* CBP_T0 CBP_T1 CBP_T2 CBP_T3 CBP_T4 CBP_T5 CBP_T6 CBP_T7 40 .. 47 */
+       0, 0, 0, 0, 0, 0, 0, 0,
+       /* CBP_T8 CBP_T9 CBP_T10 CBP_T11 CBP_T12 CBP_T13 CBP_T14
+          CBP_T15 48 .. 63 */
+       0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static const u32 abe_atc_srcid[ABE_ATC_DESC_SIZE >> 3] = {
+       /* DMA_0 DMIC PDM_DL PDM_UL McB1TX McB1RX McB2TX McB2RX 0 .. 7 */
+       0, 12, 0, 13, 0, 1, 0, 2,
+       /* McB3TX McB3RX SLIMT0 SLIMT1 SLIMT2 SLIMT3 SLIMT4 SLIMT5 8 .. 15 */
+       0, 3, 0, 0, 0, 0, 0, 0,
+       /* SLIMT6 SLIMT7 SLIMR0 SLIMR1 SLIMR2 SLIMR3 SLIMR4 SLIMR5 16 .. 23 */
+       0, 0, 4, 5, 6, 7, 8, 9,
+       /* SLIMR6 SLIMR7 McASP1X ----- ----- McASP1R ----- ----- 24 .. 31 */
+       10, 11, 0, 0, 0, 14, 0, 0,
+       /* CBPrT0 CBPrT1 CBPrT2 CBPrT3 CBPrT4 CBPrT5 CBPrT6 CBPrT7 32 .. 39 */
+       63, 63, 63, 63, 63, 63, 63, 63,
+       /* CBP_T0 CBP_T1 CBP_T2 CBP_T3 CBP_T4 CBP_T5 CBP_T6 CBP_T7 40 .. 47 */
+       0, 0, 0, 0, 0, 0, 0, 0,
+       /* CBP_T8 CBP_T9 CBP_T10 CBP_T11 CBP_T12 CBP_T13 CBP_T14
+          CBP_T15 48 .. 63 */
+       0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static struct omap_aess_port abe_port[LAST_PORT_ID];   /* list of ABE ports */
+
+static u32 abe_dma_port_iter_factor(struct omap_aess_data_format *f);
+static u32 abe_dma_port_iteration(struct omap_aess_data_format *f);
+void omap_aess_decide_main_port(struct omap_aess *abe);
+int omap_aess_init_io_tasks(struct omap_aess *abe, u32 id,
+                            struct omap_aess_data_format *format,
+                            struct omap_aess_port_protocol *prot);
+void abe_init_dma_t(u32 id, struct omap_aess_port_protocol *prot);
+
+extern void omap_aess_init_asrc_vx_dl(struct omap_aess *abe, s32 dppm);
+extern void omap_aess_init_asrc_vx_ul(struct omap_aess *abe, s32 dppm);
+
+
+/**
+ * omap_aess_reset_port
+ * @abe: Pointer on aess handle
+ * @id: ABE port ID
+ *
+ * Stop the port activity and reload default parameters on the associated
+ * processing features.
+ * Clears the internal AE buffers.
+ */
+int omap_aess_reset_port(struct omap_aess *abe, u32 id)
+{
+       struct omap_aess_port *port = abe->fw_info->port;
+
+       abe_port[id] = port[id];
+
+       return 0;
+}
+
+
+static void omap_aess_update_scheduling_table(struct omap_aess *abe,
+                                             struct omap_aess_init_task *init_task,
+                                             int enable)
+{
+       int i;
+       struct omap_aess_task *task;
+
+       for (i = 0; i < init_task->nb_task; i++) {
+               task = &init_task->task[i];
+               if (enable)
+                       abe->MultiFrame[task->frame][task->slot] = task->task;
+               else
+                       abe->MultiFrame[task->frame][task->slot] = 0;
+       }
+}
+
+static void omap_aess_update_scheduling_table1(struct omap_aess *abe,
+                                              struct omap_aess_init_task1 *init_task,
+                                              int enable)
+{
+       int i;
+       struct omap_aess_task *task;
+
+       for (i = 0; i < init_task->nb_task; i++) {
+               task = &init_task->task[i];
+               if (enable)
+                       abe->MultiFrame[task->frame][task->slot] = task->task;
+               else
+                       abe->MultiFrame[task->frame][task->slot] = 0;
+       }
+}
+
+static u32 omap_aess_update_io_task(struct omap_aess *abe,
+                                   struct omap_aess_io_task *io_task,
+                                   int enable)
+{
+       int i;
+       struct omap_aess_task *task;
+
+       for (i = 0; i < io_task->nb_task; i++) {
+               task = &io_task->task[i];
+               if (enable)
+                       abe->MultiFrame[task->frame][task->slot] = task->task;
+               else
+                       abe->MultiFrame[task->frame][task->slot] = 0;
+       }
+
+       return io_task->smem;
+}
+
+static u32 omap_aess_update_io_task1(struct omap_aess *abe,
+                                    struct omap_aess_io_task1 *io_task,
+                                    int enable)
+{
+       int i;
+       struct omap_aess_task *task;
+
+       for (i = 0; i < io_task->nb_task; i++) {
+               task = &io_task->task[i];
+               if (enable)
+                       abe->MultiFrame[task->frame][task->slot] = task->task;
+               else
+                       abe->MultiFrame[task->frame][task->slot] = 0;
+       }
+
+       return io_task->smem;
+}
+
+/**
+ * abe_build_scheduler_table
+ * @abe: Pointer on aess handle
+ *
+ * Initialize Audio Engine scheduling table for ABE internal
+ * processing. The content of the scheduling table is provided
+ * by the firmware header. It can be changed according to the
+ * ABE graph.
+ */
+void omap_aess_build_scheduler_table(struct omap_aess *abe)
+{
+       u16 i, n;
+       u16 aUplinkMuxing[NBROUTE_UL];
+
+       /* Initialize default scheduling table */
+       memset(abe->MultiFrame, 0, sizeof(abe->MultiFrame));
+       omap_aess_update_scheduling_table(abe, abe->fw_info->init_table, 1);
+
+       omap_aess_mem_write(abe, abe->fw_info->map[OMAP_AESS_DMEM_MULTIFRAME_ID],
+                           (u32 *)abe->MultiFrame);
+
+       /* reset the uplink router */
+       n = abe->fw_info->map[OMAP_AESS_DMEM_AUPLINKROUTING_ID].bytes >> 1;
+       for (i = 0; i < n; i++)
+               aUplinkMuxing[i] = abe->fw_info->label_id[OMAP_AESS_BUFFER_ZERO_ID];
+
+       omap_aess_mem_write(abe,
+                           abe->fw_info->map[OMAP_AESS_DMEM_AUPLINKROUTING_ID],
+                           (u32 *)aUplinkMuxing);
+}
+
+/**
+ * abe_dma_port_copy_subroutine_id
+ * @abe: Pointer on aess handle
+ * @port_id: ABE port ID
+ *
+ * returns the index of the function doing the copy in I/O tasks
+ */
+static u32 abe_dma_port_copy_subroutine_id(struct omap_aess *abe, u32 port_id)
+{
+       u32 sub_id;
+       if (abe_port[port_id].protocol.direction == ABE_ATC_DIRECTION_IN) {
+               switch (abe_port[port_id].format.samp_format) {
+               case MONO_MSB:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_D2S_MONO_MSB_ID];
+                       break;
+               case MONO_RSHIFTED_16:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_D2S_MONO_RSHIFTED_16_ID];
+                       break;
+               case STEREO_RSHIFTED_16:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_D2S_STEREO_RSHIFTED_16_ID];
+                       break;
+               case STEREO_16_16:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_D2S_STEREO_16_16_ID];
+                       break;
+               case MONO_16_16:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_D2S_MONO_16_16_ID];
+                       break;
+               case STEREO_MSB:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_D2S_STEREO_MSB_ID];
+                       break;
+               case SIX_MSB:
+                       if (port_id == OMAP_ABE_DMIC_PORT) {
+                               sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_DMIC_ID];
+                               break;
+                       }
+               default:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_NULL_ID];
+                       break;
+               }
+       } else {
+               switch (abe_port[port_id].format.samp_format) {
+               case MONO_MSB:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_S2D_MONO_MSB_ID];
+                       break;
+               case MONO_RSHIFTED_16:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_S2D_MONO_RSHIFTED_16_ID];
+                       break;
+               case STEREO_RSHIFTED_16:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_S2D_STEREO_RSHIFTED_16_ID];
+                       break;
+               case STEREO_16_16:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_S2D_STEREO_16_16_ID];
+                       break;
+               case MONO_16_16:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_S2D_MONO_16_16_ID];
+                       break;
+               case STEREO_MSB:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_S2D_STEREO_MSB_ID];
+                       break;
+               case SIX_MSB:
+                       if (port_id == OMAP_ABE_PDM_DL_PORT) {
+                               sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_MCPDM_DL_ID];
+                               break;
+                       }
+                       if (port_id == OMAP_ABE_MM_UL_PORT) {
+                               sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_MM_UL_ID];
+                               break;
+                       }
+               case THREE_MSB:
+               case FOUR_MSB:
+               case FIVE_MSB:
+               case SEVEN_MSB:
+               case EIGHT_MSB:
+               case NINE_MSB:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_MM_UL_ID];
+                       break;
+               default:
+                       sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_NULL_ID];
+                       break;
+               }
+       }
+       return sub_id;
+}
+
+/**
+ * abe_clean_temporay buffers
+ * @abe: Pointer on aess handle
+ * @id: ABE port ID
+ *
+ * clear temporary buffers according to the port ID.
+ */
+static void omap_aess_clean_temporary_buffers(struct omap_aess *abe, u32 id)
+{
+       switch (id) {
+       case OMAP_ABE_DMIC_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_DMIC_UL_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_DMIC0_96_48_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_DMIC1_96_48_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_DMIC2_96_48_DATA_ID]);
+               /* reset working values of the gain, target gain is preserved */
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_DMIC1_LEFT);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_DMIC1_RIGHT);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_DMIC2_LEFT);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_DMIC2_RIGHT);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_DMIC3_LEFT);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_DMIC3_RIGHT);
+               break;
+       case OMAP_ABE_PDM_UL_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_MCPDM_UL_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_AMIC_96_48_DATA_ID]);
+               /* reset working values of the gain, target gain is preserved */
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_AMIC_LEFT);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_AMIC_RIGHT);
+               break;
+       case OMAP_ABE_BT_VX_UL_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_BT_UL_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_BT_UL_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_BT_UL_8_48_HP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_BT_UL_8_48_LP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_BT_UL_16_48_HP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_BT_UL_16_48_LP_DATA_ID]);
+               /* reset working values of the gain, target gain is preserved */
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_BTUL_LEFT);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_BTUL_RIGHT);
+               break;
+       case OMAP_ABE_MM_UL_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_MM_UL_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_MM_UL_ID]);
+               break;
+       case OMAP_ABE_MM_UL2_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_MM_UL2_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_MM_UL2_ID]);
+               break;
+       case OMAP_ABE_VX_UL_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_VX_UL_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_VX_UL_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_VX_UL_48_8_HP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_VX_UL_48_8_LP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_VX_UL_48_16_HP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_VX_UL_48_16_LP_DATA_ID]);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_MIXAUDUL_UPLINK);
+               break;
+       case OMAP_ABE_MM_DL_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_MM_DL_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_MM_DL_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_MM_DL_44P1_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_MM_DL_44P1_XK_ID]);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_MIXDL1_MM_DL);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_MIXDL2_MM_DL);
+               break;
+       case OMAP_ABE_VX_DL_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_VX_DL_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_VX_DL_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_VX_DL_8_48_HP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_VX_DL_8_48_LP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_VX_DL_8_48_OSR_LP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_VX_DL_16_48_HP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_VX_DL_16_48_LP_DATA_ID]);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_MIXDL1_VX_DL);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_MIXDL2_VX_DL);
+               break;
+       case OMAP_ABE_TONES_DL_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_TONES_DL_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_TONES_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_TONES_44P1_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_TONES_44P1_XK_ID]);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_MIXDL1_TONES);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_MIXDL2_TONES);
+               break;
+       case OMAP_ABE_MCASP_DL_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_MCASP_DL_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_MCASP1_ID]);
+               break;
+       case OMAP_ABE_BT_VX_DL_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_BT_DL_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_BT_DL_ID]);
+#if !defined(CONFIG_SND_OMAP4_ABE_USE_ALT_FW)
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_BT_DL_8_48_OSR_LP_DATA_ID]);
+#endif
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_BT_DL_48_8_HP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_BT_DL_48_8_LP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_BT_DL_48_16_HP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_BT_DL_48_16_LP_DATA_ID]);
+               break;
+       case OMAP_ABE_PDM_DL_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_MCPDM_DL_FIFO_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_DL2_M_LR_EQ_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_DL1_M_EQ_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_EARP_48_96_LP_DATA_ID]);
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_SMEM_IHF_48_96_LP_DATA_ID]);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_DL1_LEFT);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_DL1_RIGHT);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_DL2_LEFT);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_GAIN_DL2_RIGHT);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_MIXSDT_UL);
+               omap_aess_reset_gain_mixer(abe, OMAP_AESS_MIXSDT_DL);
+               break;
+       case OMAP_ABE_MM_EXT_OUT_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_MM_EXT_OUT_FIFO_ID]);
+               break;
+       case OMAP_ABE_MM_EXT_IN_PORT:
+               omap_aess_reset_mem(abe, abe->fw_info->map[OMAP_AESS_DMEM_MM_EXT_IN_FIFO_ID]);
+               break;
+       }
+}
+
+/**
+ * omap_aess_disable_enable_dma_request
+ * @abe: Pointer on aess handle
+ * @id: ABE port ID
+ * @on_off: Enable/Disable
+ *
+ * Enable/Disable DMA request associated to a port.
+ */
+static void omap_aess_disable_enable_dma_request(struct omap_aess *abe, u32 id,
+                                                u32 on_off)
+{
+       u8 desc_third_word[4], irq_dmareq_field;
+       struct ABE_SIODescriptor sio_desc;
+       struct ABE_SPingPongDescriptor desc_pp;
+       struct omap_aess_addr addr;
+
+       if (abe_port[id].protocol.protocol_switch == PINGPONG_PORT_PROT) {
+               irq_dmareq_field = (u8) (on_off *
+                             abe_port[id].protocol.p.prot_pingpong.irq_data);
+               memcpy(&addr, &abe->fw_info->map[OMAP_AESS_DMEM_PINGPONGDESC_ID],
+                      sizeof(struct omap_aess_addr));
+               addr.offset += (u32)&(desc_pp.data_size) - (u32)&(desc_pp);
+               addr.bytes = 4;
+               omap_aess_mem_read(abe, addr, (u32 *)desc_third_word);
+               desc_third_word[2] = irq_dmareq_field;
+               omap_aess_mem_write(abe, addr, (u32 *)desc_third_word);
+       } else {
+               /* serial interface: sync ATC with Firmware activity */
+               memcpy(&addr, &abe->fw_info->map[OMAP_AESS_DMEM_IODESCR_ID],
+                      sizeof(struct omap_aess_addr));
+               addr.offset += id * sizeof(struct ABE_SIODescriptor);
+               addr.bytes = sizeof(struct ABE_SIODescriptor);
+               omap_aess_mem_read(abe, addr, (u32 *)&sio_desc);
+               if (on_off) {
+                       if (abe_port[id].protocol.protocol_switch != SERIAL_PORT_PROT)
+                               sio_desc.atc_irq_data =
+                                       (u8) abe_port[id].protocol.p.prot_dmareq.
+                                       dma_data;
+                       sio_desc.on_off = 0x80;
+               } else {
+                       sio_desc.atc_irq_data = 0;
+                       sio_desc.on_off = 0;
+               }
+               omap_aess_mem_write(abe, addr, (u32 *)&sio_desc);
+       }
+
+}
+
+/**
+ * omap_aess_enable_dma_request
+ * @abe: Pointer on aess handle
+ * @id: ABE port ID
+ *
+ * Enable DMA request associated to the port ID
+ */
+static void omap_aess_enable_dma_request(struct omap_aess *abe, u32 id)
+{
+       omap_aess_disable_enable_dma_request(abe, id, 1);
+}
+
+/**
+ * omap_aess_disable_dma_request
+ * @abe: Pointer on aess handle
+ * @id: ABE port ID
+ *
+ * Disable DMA request associated to the port ID
+ */
+static void omap_aess_disable_dma_request(struct omap_aess *abe, u32 id)
+{
+       omap_aess_disable_enable_dma_request(abe, id, 0);
+}
+
+/**
+ * omap_aess_init_atc
+ * @abe: Pointer on aess handle
+ * @id: ABE port ID
+ *
+ * load the DMEM ATC/AESS descriptor associated to the port ID.
+ * ATC is describing the internal flexible FIFO inside the DMEM
+ * connected to HW IP (eg McBSP/DMIC/...)
+ */
+static void omap_aess_init_atc(struct omap_aess *abe, u32 id)
+{
+       u8 iter;
+       s32 datasize;
+       struct omap_abe_atc_desc atc_desc;
+
+#define JITTER_MARGIN 4
+       /* load default values of the descriptor */
+       memset(&atc_desc, 0, sizeof(struct omap_abe_atc_desc));
+
+       datasize = abe_dma_port_iter_factor(&((abe_port[id]).format));
+       iter = (u8) abe_dma_port_iteration(&((abe_port[id]).format));
+       /* if the ATC FIFO is too small there will be two ABE firmware
+          utasks to do the copy this happems on DMIC and MCPDMDL */
+       /* VXDL_8kMono = 4 = 2 + 2x1 */
+       /* VXDL_16kstereo = 12 = 8 + 2x2 */
+       /* MM_DL_1616 = 14 = 12 + 2x1 */
+       /* DMIC = 84 = 72 + 2x6 */
+       /* VXUL_8kMono = 2 */
+       /* VXUL_16kstereo = 4 */
+       /* MM_UL2_Stereo = 4 */
+       /* PDMDL = 12 */
+       /* IN from AESS point of view */
+       if (abe_port[id].protocol.direction == ABE_ATC_DIRECTION_IN)
+               if (iter + 2 * datasize > 126)
+                       atc_desc.wrpt = (iter >> 1) +
+                               ((JITTER_MARGIN-1) * datasize);
+               else
+                       atc_desc.wrpt = iter + ((JITTER_MARGIN-1) * datasize);
+       else
+               atc_desc.wrpt = 0 + ((JITTER_MARGIN+1) * datasize);
+
+       switch ((abe_port[id]).protocol.protocol_switch) {
+       case SERIAL_PORT_PROT:
+               atc_desc.cbdir = (abe_port[id]).protocol.direction;
+               atc_desc.cbsize =
+                       (abe_port[id]).protocol.p.prot_serial.buf_size;
+               atc_desc.badd =
+                       ((abe_port[id]).protocol.p.prot_serial.buf_addr) >> 4;
+               atc_desc.iter = (abe_port[id]).protocol.p.prot_serial.iter;
+               atc_desc.srcid =
+                       abe_atc_srcid[(abe_port[id]).protocol.p.prot_serial.
+                                     desc_addr >> 3];
+               atc_desc.destid =
+                       abe_atc_dstid[(abe_port[id]).protocol.p.prot_serial.
+                                     desc_addr >> 3];
+               omap_abe_mem_write(abe, OMAP_ABE_DMEM,
+                                  (abe_port[id]).protocol.p.prot_serial.desc_addr,
+                                  (u32 *)&atc_desc, sizeof(atc_desc));
+               break;
+       case DMIC_PORT_PROT:
+               atc_desc.cbdir = ABE_ATC_DIRECTION_IN;
+               atc_desc.cbsize = (abe_port[id]).protocol.p.prot_dmic.buf_size;
+               atc_desc.badd =
+                       ((abe_port[id]).protocol.p.prot_dmic.buf_addr) >> 4;
+               atc_desc.iter = DMIC_ITER;
+               atc_desc.srcid = abe_atc_srcid[ABE_ATC_DMIC_DMA_REQ];
+               omap_abe_mem_write(abe, OMAP_ABE_DMEM,
+                                  (ABE_ATC_DMIC_DMA_REQ*ATC_SIZE),
+                                  (u32 *)&atc_desc, sizeof(atc_desc));
+               break;
+       case MCPDMDL_PORT_PROT:
+               atc_desc.cbdir = ABE_ATC_DIRECTION_OUT;
+               atc_desc.cbsize =
+                       (abe_port[id]).protocol.p.prot_mcpdmdl.buf_size;
+               atc_desc.badd =
+                       ((abe_port[id]).protocol.p.prot_mcpdmdl.buf_addr) >> 4;
+               atc_desc.iter = MCPDM_DL_ITER;
+               atc_desc.destid = abe_atc_dstid[ABE_ATC_MCPDMDL_DMA_REQ];
+               omap_abe_mem_write(abe, OMAP_ABE_DMEM,
+                                  (ABE_ATC_MCPDMDL_DMA_REQ*ATC_SIZE),
+                                  (u32 *)&atc_desc, sizeof(atc_desc));
+               break;
+       case MCPDMUL_PORT_PROT:
+               atc_desc.cbdir = ABE_ATC_DIRECTION_IN;
+               atc_desc.cbsize =
+                       (abe_port[id]).protocol.p.prot_mcpdmul.buf_size;
+               atc_desc.badd =
+                       ((abe_port[id]).protocol.p.prot_mcpdmul.buf_addr) >> 4;
+               atc_desc.iter = MCPDM_UL_ITER;
+               atc_desc.srcid = abe_atc_srcid[ABE_ATC_MCPDMUL_DMA_REQ];
+               omap_abe_mem_write(abe, OMAP_ABE_DMEM,
+                                  (ABE_ATC_MCPDMUL_DMA_REQ*ATC_SIZE),
+                                  (u32 *)&atc_desc, sizeof(atc_desc));
+               break;
+       case PINGPONG_PORT_PROT:
+               /* software protocol, nothing to do on ATC */
+               break;
+       case DMAREQ_PORT_PROT:
+               atc_desc.cbdir = (abe_port[id]).protocol.direction;
+               atc_desc.cbsize =
+                       (abe_port[id]).protocol.p.prot_dmareq.buf_size;
+               atc_desc.badd =
+                       ((abe_port[id]).protocol.p.prot_dmareq.buf_addr) >> 4;
+               /* CBPr needs ITER=1.
+               It is the job of eDMA to do the iterations */
+               atc_desc.iter = 1;
+               /* input from ABE point of view */
+               if (abe_port[id].protocol.direction == ABE_ATC_DIRECTION_IN) {
+                       /* atc_atc_desc.rdpt = 127; */
+                       /* atc_atc_desc.wrpt = 0; */
+                       atc_desc.srcid = abe_atc_srcid
+                               [(abe_port[id]).protocol.p.prot_dmareq.
+                                desc_addr >> 3];
+               } else {
+                       /* atc_atc_desc.rdpt = 0; */
+                       /* atc_atc_desc.wrpt = 127; */
+                       atc_desc.destid = abe_atc_dstid
+                               [(abe_port[id]).protocol.p.prot_dmareq.
+                                desc_addr >> 3];
+               }
+               omap_abe_mem_write(abe, OMAP_ABE_DMEM,
+                                  (abe_port[id]).protocol.p.prot_dmareq.desc_addr,
+                                  (u32 *)&atc_desc, sizeof(atc_desc));
+               break;
+       }
+}
+
+/**
+ * omap_aess_disable_data_transfer
+ * @abe: Pointer on aess handle
+ * @id: ABE port id
+ *
+ * disables the ATC descriptor and stop IO/port activities
+ * disable the IO task (@f = 0)
+ * clear ATC DMEM buffer, ATC enabled
+ */
+int omap_aess_disable_data_transfer(struct omap_aess *abe, u32 id)
+{
+
+       switch (id) {
+       case OMAP_ABE_MM_DL_PORT:
+               abe->MultiFrame[18][1] = 0;
+               break;
+       default:
+               break;
+       }
+
+       /* local host variable status= "port is running" */
+       abe_port[id].status = OMAP_ABE_PORT_ACTIVITY_IDLE;
+       /* disable DMA requests */
+       omap_aess_disable_dma_request(abe, id);
+       /* disable ATC transfers */
+       omap_aess_init_atc(abe, id);
+       omap_aess_clean_temporary_buffers(abe, id);
+       /* select the main port based on the desactivation of this port */
+       omap_aess_decide_main_port(abe);
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_disable_data_transfer);
+
+/**
+ * omap_aess_enable_data_transfer
+ * @abe: Pointer on aess handle
+ * @id: ABE port id
+ *
+ * enables the ATC descriptor
+ * reset ATC pointers
+ * enable the IO task (@f <> 0)
+ */
+int omap_aess_enable_data_transfer(struct omap_aess *abe, u32 id)
+{
+       struct omap_aess_port_protocol *protocol;
+       struct omap_aess_data_format format;
+
+       omap_aess_clean_temporary_buffers(abe, id);
+
+       omap_aess_update_scheduling_table1(abe, &(abe->fw_info->port[id].task), 1);
+
+       switch (id) {
+       case OMAP_ABE_PDM_UL_PORT:
+       case OMAP_ABE_PDM_DL_PORT:
+       case OMAP_ABE_DMIC_PORT:
+               /* initializes the ABE ATC descriptors in DMEM for BE ports */
+               protocol = &(abe_port[id].protocol);
+               format = abe_port[id].format;
+               omap_aess_init_atc(abe, id);
+               omap_aess_init_io_tasks(abe, id, &format, protocol);
+               break;
+
+       case OMAP_ABE_MM_DL_PORT:
+               protocol = &(abe_port[OMAP_ABE_MM_DL_PORT].protocol);
+               if (protocol->protocol_switch == PINGPONG_PORT_PROT)
+                       omap_aess_update_scheduling_table1(abe, &abe->fw_info->ping_pong->task, 1);
+               break;
+       default:
+               break;
+       }
+
+       omap_aess_mem_write(abe, abe->fw_info->map[OMAP_AESS_DMEM_MULTIFRAME_ID],
+                           (u32 *)abe->MultiFrame);
+
+       /* local host variable status= "port is running" */
+       abe_port[id].status = OMAP_ABE_PORT_ACTIVITY_RUNNING;
+       /* enable DMA requests */
+       omap_aess_enable_dma_request(abe, id);
+       /* select the main port based on the activation of this new port */
+       omap_aess_decide_main_port(abe);
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_enable_data_transfer);
+
+/**
+ * omap_aess_connect_cbpr_dmareq_port
+ * @abe: Pointer on aess handle
+ * @id: port name
+ * @f: desired data format
+ * @d: desired dma_request line (0..7)
+ * @returned_dma_t: returned pointer to the base address of the CBPr register and number of
+ *     samples to exchange during a DMA_request.
+ *
+ * enables the data echange between a DMA and the ABE through the
+ *     CBPr registers of AESS.
+ */
+int omap_aess_connect_cbpr_dmareq_port(struct omap_aess *abe,
+                                      u32 id, struct omap_aess_data_format *f,
+                                      u32 d,
+                                      struct omap_aess_dma *returned_dma_t)
+{
+       abe_port[id] = ((struct omap_aess_port *)abe->fw_info->port)[id];
+       (abe_port[id]).format = (*f);
+       abe_port[id].protocol.protocol_switch = DMAREQ_PORT_PROT;
+       abe_port[id].protocol.p.prot_dmareq.iter = abe_dma_port_iteration(f);
+       abe_port[id].protocol.p.prot_dmareq.dma_addr = ABE_DMASTATUS_RAW;
+       abe_port[id].protocol.p.prot_dmareq.dma_data = (1 << d);
+       /* load the dma_t with physical information from AE memory mapping */
+       abe_init_dma_t(id, &((abe_port[id]).protocol));
+
+       /* load the ATC descriptors - disabled */
+       omap_aess_init_atc(abe, id);
+
+       /* load the micro-task parameters */
+       omap_aess_init_io_tasks(abe,  id, &((abe_port[id]).format),
+                               &((abe_port[id]).protocol));
+       abe_port[id].status = OMAP_ABE_PORT_INITIALIZED;
+
+       /* return the dma pointer address */
+       omap_aess_read_port_address(abe, id, returned_dma_t);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_connect_cbpr_dmareq_port);
+
+/**
+ * omap_aess_connect_serial_port()
+ * @abe: Pointer on aess handle
+ * @id: port name
+ * @f: data format
+ * @mcbsp_id: peripheral ID (McBSP #1, #2, #3)
+ *
+ * Operations : enables the data echanges between a McBSP and an ATC buffer in
+ * DMEM. This API is used connect 48kHz McBSP streams to MM_DL and 8/16kHz
+ * voice streams to VX_UL, VX_DL, BT_VX_UL, BT_VX_DL. It abstracts the
+ * abe_write_port API.
+ */
+int omap_aess_connect_serial_port(struct omap_aess *abe,
+                                 u32 id, struct omap_aess_data_format *f,
+                                 u32 mcbsp_id)
+{
+       abe_port[id] = ((struct omap_aess_port *)abe->fw_info->port)[id];
+       (abe_port[id]).format = (*f);
+       (abe_port[id]).protocol.protocol_switch = SERIAL_PORT_PROT;
+       /* McBSP peripheral connected to ATC */
+       (abe_port[id]).protocol.p.prot_serial.desc_addr = mcbsp_id*ATC_SIZE;
+       /* check the iteration of ATC */
+       (abe_port[id]).protocol.p.prot_serial.iter =
+               abe_dma_port_iter_factor(f);
+
+       /* load the ATC descriptors - disabled */
+       omap_aess_init_atc(abe, id);
+       /* load the micro-task parameters */
+       omap_aess_init_io_tasks(abe,  id, &((abe_port[id]).format),
+                               &((abe_port[id]).protocol));
+       abe_port[id].status = OMAP_ABE_PORT_INITIALIZED;
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_connect_serial_port);
+
+/**
+ * omap_aess_read_port_address
+ * @abe: Pointer on aess handle
+ * @port: port name
+ * @dma2: output pointer to the DMA iteration and data destination pointer
+ *
+ * This API returns the address of the DMA register used on this audio port.
+ * Depending on the protocol being used, adds the base address offset L3
+ * (DMA) or MPU (ARM)
+ */
+int omap_aess_read_port_address(struct omap_aess *abe,
+                               u32 port, struct omap_aess_dma *dma2)
+{
+       struct omap_aess_dma_offset dma1;
+       u32 protocol_switch;
+
+       dma1 = (abe_port[port]).dma;
+       protocol_switch = abe_port[port].protocol.protocol_switch;
+       switch (protocol_switch) {
+       case PINGPONG_PORT_PROT:
+               /* return the base address of the buffer in L3 and L4 spaces */
+               (*dma2).data = (void *)(dma1.data +
+                       ABE_DEFAULT_BASE_ADDRESS_L3 + ABE_DMEM_BASE_OFFSET_MPU);
+               (*dma2).l3_dmem = (void *)(dma1.data +
+                       ABE_DEFAULT_BASE_ADDRESS_L3 + ABE_DMEM_BASE_OFFSET_MPU);
+               (*dma2).l4_dmem = (void *)(dma1.data +
+                       ABE_DEFAULT_BASE_ADDRESS_L4 + ABE_DMEM_BASE_OFFSET_MPU);
+               break;
+       case DMAREQ_PORT_PROT:
+               /* return the CBPr(L3), DMEM(L3), DMEM(L4) address */
+               (*dma2).data = (void *)(dma1.data +
+                       ABE_DEFAULT_BASE_ADDRESS_L3 + ABE_ATC_BASE_OFFSET_MPU);
+               (*dma2).l3_dmem =
+                       (void *)((abe_port[port]).protocol.p.prot_dmareq.buf_addr +
+                       ABE_DEFAULT_BASE_ADDRESS_L3 + ABE_DMEM_BASE_OFFSET_MPU);
+               (*dma2).l4_dmem =
+                       (void *)((abe_port[port]).protocol.p.prot_dmareq.buf_addr +
+                       ABE_DEFAULT_BASE_ADDRESS_L4 + ABE_DMEM_BASE_OFFSET_MPU);
+               break;
+       default:
+               break;
+       }
+       (*dma2).iter = (dma1.iter);
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_read_port_address);
+
+/**
+ * abe_init_dma_t
+ * @id: ABE port ID
+ * @prot: protocol being used
+ *
+ * load the dma_t with physical information from AE memory mapping
+ */
+void abe_init_dma_t(u32 id, struct omap_aess_port_protocol *prot)
+{
+       struct omap_aess_dma_offset dma;
+       u32 idx;
+       /* default dma_t points to address 0000... */
+       dma.data = 0;
+       dma.iter = 0;
+       switch (prot->protocol_switch) {
+       case PINGPONG_PORT_PROT:
+               for (idx = 0; idx < 32; idx++) {
+                       if (((prot->p).prot_pingpong.irq_data) ==
+                           (u32) (1 << idx))
+                               break;
+               }
+               (prot->p).prot_dmareq.desc_addr =
+                       ((CBPr_DMA_RTX0 + idx)*ATC_SIZE);
+               /* translate byte address/size in DMEM words */
+               dma.data = (prot->p).prot_pingpong.buf_addr >> 2;
+               dma.iter = (prot->p).prot_pingpong.buf_size >> 2;
+               break;
+       case DMAREQ_PORT_PROT:
+               for (idx = 0; idx < 32; idx++) {
+                       if (((prot->p).prot_dmareq.dma_data) ==
+                           (u32) (1 << idx))
+                               break;
+               }
+               dma.data = (CIRCULAR_BUFFER_PERIPHERAL_R__0 + (idx << 2));
+               dma.iter = (prot->p).prot_dmareq.iter;
+               (prot->p).prot_dmareq.desc_addr =
+                       ((CBPr_DMA_RTX0 + idx)*ATC_SIZE);
+               break;
+       case SLIMBUS_PORT_PROT:
+       case SERIAL_PORT_PROT:
+       case DMIC_PORT_PROT:
+       case MCPDMDL_PORT_PROT:
+       case MCPDMUL_PORT_PROT:
+       default:
+               break;
+       }
+       /* upload the dma type */
+       abe_port[id].dma = dma;
+}
+
+/**
+ * omap_aess_enable_atc
+ * @abe: Pointer on aess handle
+ * @id: port name
+ *
+ * Enable ATC associated to the port ID
+ */
+static void omap_aess_enable_atc(struct omap_aess *abe, u32 id)
+{
+       struct omap_abe_atc_desc atc_desc;
+
+       omap_abe_mem_read(abe, OMAP_ABE_DMEM,
+                         (abe_port[id]).protocol.p.prot_dmareq.desc_addr,
+                         (u32 *)&atc_desc, sizeof(atc_desc));
+       atc_desc.desen = 1;
+       omap_abe_mem_write(abe, OMAP_ABE_DMEM,
+                          (abe_port[id]).protocol.p.prot_dmareq.desc_addr,
+                          (u32 *)&atc_desc, sizeof(atc_desc));
+
+}
+
+/**
+ * omap_aess_disable_atc
+ * @abe: Pointer on aess handle
+ * @id: port name
+ *
+ * Enable ATC associated to the port ID
+ */
+static void omap_aess_disable_atc(struct omap_aess *abe, u32 id)
+{
+       struct omap_abe_atc_desc atc_desc;
+
+       omap_abe_mem_read(abe, OMAP_ABE_DMEM,
+                         (abe_port[id]).protocol.p.prot_dmareq.desc_addr,
+                         (u32 *)&atc_desc, sizeof(atc_desc));
+       atc_desc.desen = 0;
+       omap_abe_mem_write(abe, OMAP_ABE_DMEM,
+                          (abe_port[id]).protocol.p.prot_dmareq.desc_addr,
+                          (u32 *)&atc_desc, sizeof(atc_desc));
+
+}
+
+/**
+ * omap_aess_init_io_tasks
+ * @abe: Pointer on aess handle
+ * @id: port name
+ * @format: data format being used
+ * @prot: protocol being used
+ *
+ * load the micro-task parameters doing to DMEM <==> SMEM data moves
+ *
+ * I/O descriptors input parameters :
+ * For Read from DMEM usually THR1/THR2 = X+1/X-1
+ * For Write to DMEM usually THR1/THR2 = 2/0
+ * UP_1/2 =X+1/X-1
+ */
+int omap_aess_init_io_tasks(struct omap_aess *abe, u32 id,
+                           struct omap_aess_data_format *format,
+                           struct omap_aess_port_protocol *prot)
+{
+       u32 x_io, direction, iter_samples, smem1, smem2, smem3, io_sub_id,
+               io_flag;
+       u32 copy_func_index, before_func_index, after_func_index;
+       u32 dmareq_addr, dmareq_field;
+       u32 datasize, iter, nsamp, datasize2;
+       u32 atc_ptr_saved, atc_ptr_saved2, copy_func_index1;
+       u32 copy_func_index2, atc_desc_address1, atc_desc_address2;
+       struct omap_aess_addr addr;
+
+       if (prot->protocol_switch == PINGPONG_PORT_PROT) {
+               struct ABE_SPingPongDescriptor desc_pp;
+
+               memset(&desc_pp, 0, sizeof(desc_pp));
+
+               /* ping_pong is only supported on MM_DL */
+               if (OMAP_ABE_MM_DL_PORT != id) {
+                       aess_err("Only Ping-pong port supported");
+                       return -AESS_EINVAL;
+               }
+               if (abe_port[id].format.f == 44100)
+                       smem1 = omap_aess_update_io_task1(abe, &(abe->fw_info->ping_pong->tsk_freq[2].task), 1);
+               else
+                       smem1 = omap_aess_update_io_task1(abe, &(abe->fw_info->ping_pong->tsk_freq[3].task), 1);
+
+               /* able  interrupt to be generated at the first frame */
+               desc_pp.split_addr1 = 1;
+
+               copy_func_index = (u8) abe_dma_port_copy_subroutine_id(abe, id);
+               dmareq_addr = abe_port[id].protocol.p.prot_pingpong.irq_addr;
+               dmareq_field = abe_port[id].protocol.p.prot_pingpong.irq_data;
+               datasize = abe_dma_port_iter_factor(format);
+               /* number of "samples" either mono or stereo */
+               iter = abe_dma_port_iteration(format);
+               iter_samples = (iter / datasize);
+
+               /* load the IO descriptor */
+               desc_pp.hw_ctrl_addr = (u16) dmareq_addr;
+               desc_pp.copy_func_index = (u8) copy_func_index;
+               desc_pp.smem_addr = (u8) smem1;
+               /* DMA req 0 is used for CBPr0 */
+               desc_pp.atc_irq_data = (u8) dmareq_field;
+               /* size of block transfer */
+               desc_pp.x_io = (u8) iter_samples;
+               desc_pp.data_size = (u8) datasize;
+               /* address comunicated in Bytes */
+               desc_pp.workbuff_BaseAddr =
+                       (u16) (abe->base_address_pingpong[1]);
+
+               /* size comunicated in XIO sample */
+               desc_pp.workbuff_Samples = 0;
+               desc_pp.nextbuff0_BaseAddr =
+                       (u16) (abe->base_address_pingpong[0]);
+               desc_pp.nextbuff1_BaseAddr =
+                       (u16) (abe->base_address_pingpong[1]);
+               if (dmareq_addr == ABE_DMASTATUS_RAW) {
+                       desc_pp.nextbuff0_Samples =
+                               (u16) ((abe->size_pingpong >> 2) / datasize);
+                       desc_pp.nextbuff1_Samples =
+                               (u16) ((abe->size_pingpong >> 2) / datasize);
+               } else {
+                       desc_pp.nextbuff0_Samples = 0;
+                       desc_pp.nextbuff1_Samples = 0;
+               }
+               /* next buffer to send is B1, first IRQ fills B0 */
+               desc_pp.counter = 0;
+               /* send a DMA req to fill B0 with N samples
+                  abe_block_copy (COPY_FROM_HOST_TO_ABE,
+                       ABE_ATC,
+                       ABE_DMASTATUS_RAW,
+                       &(abe_port[id].protocol.p.prot_pingpong.irq_data),
+                       4); */
+               memcpy(&addr, &abe->fw_info->map[OMAP_AESS_DMEM_PINGPONGDESC_ID],
+                      sizeof(struct omap_aess_addr));
+               addr.bytes = sizeof(desc_pp);
+               omap_aess_mem_write(abe, addr, (u32 *)&desc_pp);
+       } else {
+               struct ABE_SIODescriptor sio_desc;
+               int idx;
+
+               switch (abe_port[id].format.f) {
+               case 8000:
+               default:
+                       idx = 0;
+                       break;
+               case 16000:
+                       idx = 1;
+                       break;
+               case 44100:
+                       idx = 2;
+                       break;
+               case 48000:
+                       idx = 3;
+                       break;
+               }
+
+               memset(&sio_desc, 0, sizeof(sio_desc));
+
+               io_sub_id = ABE_DMASTATUS_RAW;
+               dmareq_addr = ABE_DMASTATUS_RAW;
+               dmareq_field = 0;
+               atc_desc_address1 = 0;
+               atc_desc_address2 = 0;
+               /* default: repeat of the last downlink samples in case of
+                  DMA errors, (disable=0x00) */
+               io_flag = 0xFF;
+               datasize2 = abe_dma_port_iter_factor(format);
+               datasize = abe_dma_port_iter_factor(format);
+               x_io = (u8) abe_dma_port_iteration(format);
+               nsamp = (x_io / datasize);
+               atc_ptr_saved2 = abe->fw_info->label_id[OMAP_AESS_BUFFER_DMIC_ATC_PTR_ID] + id;
+               atc_ptr_saved = abe->fw_info->label_id[OMAP_AESS_BUFFER_DMIC_ATC_PTR_ID] + id;
+
+               smem1 = abe_port[id].smem_buffer1;
+               smem2 = abe_port[id].smem_buffer2;
+               smem3 = abe_port[id].smem_buffer2;
+               copy_func_index1 = (u8) abe_dma_port_copy_subroutine_id(abe, id);
+
+               before_func_index = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_NULL_ID];
+               after_func_index = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_NULL_ID];
+               copy_func_index2 = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_NULL_ID];
+
+               switch (prot->protocol_switch) {
+               case DMIC_PORT_PROT:
+                       /* DMIC port is read in two steps */
+                       x_io = x_io >> 1;
+                       nsamp = nsamp >> 1;
+                       atc_desc_address1 = (ABE_ATC_DMIC_DMA_REQ*ATC_SIZE);
+                       io_sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_IO_IP_ID];
+                       break;
+               case MCPDMDL_PORT_PROT:
+                       /* PDMDL port is written to in two steps */
+                       x_io = x_io >> 1;
+                       atc_desc_address1 = (ABE_ATC_MCPDMDL_DMA_REQ*ATC_SIZE);
+                       io_sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_IO_IP_ID];
+                       break;
+               case MCPDMUL_PORT_PROT:
+                       atc_desc_address1 = (ABE_ATC_MCPDMUL_DMA_REQ*ATC_SIZE);
+                       io_sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_IO_IP_ID];
+                       break;
+               case SLIMBUS_PORT_PROT:
+                       atc_desc_address1 = abe_port[id].protocol.p.prot_slimbus.desc_addr1;
+                       atc_desc_address2 = abe_port[id].protocol.p.prot_slimbus.desc_addr2;
+                       copy_func_index2 = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_NULL_ID];
+                       /* @@@@@@
+                          #define SPLIT_SMEM_CFPID 9
+                          #define MERGE_SMEM_CFPID 10
+                          #define SPLIT_TDM_12_CFPID 11
+                          #define MERGE_TDM_12_CFPID 12
+                        */
+                       io_sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_IO_IP_ID];
+                       break;
+               case SERIAL_PORT_PROT:  /* McBSP/McASP */
+                       atc_desc_address1 = (s16) abe_port[id].protocol.p.prot_serial.desc_addr;
+                       io_sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_IO_IP_ID];
+                       break;
+               case DMAREQ_PORT_PROT:  /* DMA w/wo CBPr */
+                       dmareq_addr = abe_port[id].protocol.p.prot_dmareq.dma_addr;
+                       dmareq_field = 0;
+                       atc_desc_address1 = abe_port[id].protocol.p.prot_dmareq.desc_addr;
+                       io_sub_id = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_IO_IP_ID];
+                       break;
+               }
+               /* special situation of the PING_PONG protocol which
+               has its own SIO descriptor format */
+               /*
+                  Sequence of operations on ping-pong buffers B0/B1
+                  -------------- time ----------------------------
+                  Host Application is ready to send data from DDR to B0
+                  SDMA is initialized from "abe_connect_irq_ping_pong_port" to B0
+                  FIRMWARE starts with #12 B1 data,
+                  sends IRQ/DMAreq, sends #pong B1 data,
+                  sends IRQ/DMAreq, sends #ping B0,
+                  sends B1 samples
+                  ARM / SDMA | fills B0 | fills B1 ... | fills B0 ...
+                  Counter 0 1 2 3
+                */
+               switch (id) {
+               case OMAP_ABE_VX_DL_PORT:
+                       omap_aess_update_scheduling_table1(abe, &(abe->fw_info->port[id].task), 1);
+
+                       smem1 = omap_aess_update_io_task1(abe, &(abe->fw_info->port[id].tsk_freq[idx].task), 1);
+                       /* check for 8kHz/16kHz */
+                       if (idx < 2) {
+                               /* ASRC set only for McBSP */
+                               if ((prot->protocol_switch == SERIAL_PORT_PROT)) {
+                                       if ((abe_port[OMAP_ABE_VX_DL_PORT].status ==
+                                               OMAP_ABE_PORT_ACTIVITY_IDLE) &&
+                                           (abe_port[OMAP_ABE_VX_UL_PORT].status ==
+                                               OMAP_ABE_PORT_ACTIVITY_IDLE)) {
+                                               /* the 1st opened port is VX_DL_PORT
+                                                * both VX_UL ASRC and VX_DL ASRC will add/remove sample
+                                                * referring to VX_DL flow_counter */
+                                               omap_aess_update_scheduling_table1(abe, &(abe->fw_info->port[id].tsk_freq[idx].asrc.serial), 1);
+
+                                               /* Init VX_UL ASRC & VX_DL ASRC and enable its adaptation */
+                                               omap_aess_init_asrc_vx_ul(abe, -250);
+                                               omap_aess_init_asrc_vx_dl(abe, 250);
+                                       } else {
+                                               /* Do nothing, Scheduling Table has already been patched */
+                                       }
+                               } else {
+                                       /* Enable only ASRC on VXDL port*/
+                                       omap_aess_update_scheduling_table1(abe, &(abe->fw_info->port[id].tsk_freq[idx].asrc.cbpr), 1);
+                                       omap_aess_init_asrc_vx_dl(abe, 0);
+                               }
+                       }
+                       break;
+               case OMAP_ABE_VX_UL_PORT:
+                       omap_aess_update_scheduling_table1(abe, &(abe->fw_info->port[id].task), 1);
+
+                       smem1 = omap_aess_update_io_task1(abe, &(abe->fw_info->port[id].tsk_freq[idx].task), 1);
+                       /* check for 8kHz/16kHz */
+                       if (idx < 2) {
+                               /* ASRC set only for McBSP */
+                               if ((prot->protocol_switch == SERIAL_PORT_PROT)) {
+                                       if ((abe_port[OMAP_ABE_VX_DL_PORT].status ==
+                                               OMAP_ABE_PORT_ACTIVITY_IDLE) &&
+                                           (abe_port[OMAP_ABE_VX_UL_PORT].status ==
+                                               OMAP_ABE_PORT_ACTIVITY_IDLE)) {
+                                               /* the 1st opened port is VX_UL_PORT
+                                                * both VX_UL ASRC and VX_DL ASRC will add/remove sample
+                                                * referring to VX_UL flow_counter */
+                                               omap_aess_update_scheduling_table1(abe, &(abe->fw_info->port[id].tsk_freq[idx].asrc.serial), 1);
+                                               /* Init VX_UL ASRC & VX_DL ASRC and enable its adaptation */
+                                               omap_aess_init_asrc_vx_ul(abe, -250);
+                                               omap_aess_init_asrc_vx_dl(abe, 250);
+                                       } else {
+                                               /* Do nothing, Scheduling Table has already been patched */
+                                       }
+                               } else {
+                                       /* Enable only ASRC on VXUL port*/
+                                       omap_aess_update_scheduling_table1(abe, &(abe->fw_info->port[id].tsk_freq[idx].asrc.cbpr), 1);
+                                       omap_aess_init_asrc_vx_ul(abe, 0);
+                               }
+                       }
+                       break;
+               case OMAP_ABE_BT_VX_DL_PORT:
+               case OMAP_ABE_BT_VX_UL_PORT:
+               case OMAP_ABE_MM_DL_PORT:
+               case OMAP_ABE_TONES_DL_PORT:
+                       smem1 = omap_aess_update_io_task1(abe, &(abe->fw_info->port[id].tsk_freq[idx].task), 1);
+                       break;
+               case OMAP_ABE_MM_UL_PORT:
+                       copy_func_index1 = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_MM_UL_ID];
+                       before_func_index = abe->fw_info->fct_id[OMAP_AESS_COPY_FCT_ROUTE_MM_UL_ID];
+                       break;
+               case OMAP_ABE_MM_EXT_IN_PORT:
+                       /* set the SMEM buffer -- programming sequence */
+                       smem1 = abe->fw_info->label_id[OMAP_AESS_BUFFER_MM_EXT_IN_ID];
+                       break;
+               case OMAP_ABE_PDM_DL_PORT:
+               case OMAP_ABE_PDM_UL_PORT:
+               case OMAP_ABE_DMIC_PORT:
+               case OMAP_ABE_MM_UL2_PORT:
+               case OMAP_ABE_MM_EXT_OUT_PORT:
+               default:
+                       break;
+               }
+
+               if (abe_port[id].protocol.direction == ABE_ATC_DIRECTION_IN)
+                       direction = 0;
+               else
+                       /* offset of the write pointer in the ATC descriptor */
+                       direction = 3;
+
+               sio_desc.io_type_idx = (u8) io_sub_id;
+               sio_desc.samp_size = (u8) datasize;
+               sio_desc.hw_ctrl_addr = (u16) (dmareq_addr << 2);
+               sio_desc.atc_irq_data = (u8) dmareq_field;
+               sio_desc.flow_counter = (u16) 0;
+               sio_desc.direction_rw = (u8) direction;
+               sio_desc.repeat_last_samp = (u8) io_flag;
+               sio_desc.nsamp = (u8) nsamp;
+               sio_desc.x_io = (u8) x_io;
+               /* set ATC ON */
+               sio_desc.on_off = 0x80;
+               sio_desc.split_addr1 = (u16) smem1;
+               sio_desc.split_addr2 = (u16) smem2;
+               sio_desc.split_addr3 = (u16) smem3;
+               sio_desc.before_f_index = (u8) before_func_index;
+               sio_desc.after_f_index = (u8) after_func_index;
+               sio_desc.smem_addr1 = (u16) smem1;
+               sio_desc.atc_address1 = (u16) atc_desc_address1;
+               sio_desc.atc_pointer_saved1 = (u16) atc_ptr_saved;
+               sio_desc.data_size1 = (u8) datasize;
+               sio_desc.copy_f_index1 = (u8) copy_func_index1;
+               sio_desc.smem_addr2 = (u16) smem2;
+               sio_desc.atc_address2 = (u16) atc_desc_address2;
+               sio_desc.atc_pointer_saved2 = (u16) atc_ptr_saved2;
+               sio_desc.data_size2 = (u8) datasize2;
+               sio_desc.copy_f_index2 = (u8) copy_func_index2;
+
+               memcpy(&addr, &abe->fw_info->map[OMAP_AESS_DMEM_IODESCR_ID],
+                      sizeof(struct omap_aess_addr));
+               addr.bytes = sizeof(struct ABE_SIODescriptor);
+               addr.offset += (id * sizeof(struct ABE_SIODescriptor));
+
+               omap_aess_mem_write(abe, addr, (u32 *)&sio_desc);
+
+       }
+       omap_aess_mem_write(abe, abe->fw_info->map[OMAP_AESS_DMEM_MULTIFRAME_ID],
+                           (u32 *)abe->MultiFrame);
+
+       return 0;
+}
+
+/**
+ * omap_aess_select_main_port - Select stynchronization port for Event generator.
+ * @abe: Pointer on aess handle
+ * @id: port name
+ *
+ * tells the FW which is the reference stream for adjusting
+ * the processing on 23/24/25 slots
+ */
+int omap_aess_select_main_port(struct omap_aess *abe, u32 id)
+{
+       u32 selection;
+
+       /* flow control */
+       selection = abe->fw_info->map[OMAP_AESS_DMEM_IODESCR_ID].offset + id * sizeof(struct ABE_SIODescriptor) +
+               offsetof(struct ABE_SIODescriptor, flow_counter);
+       /* when the main port is a sink port from AESS point of view
+          the sign the firmware task analysis must be changed  */
+       selection &= 0xFFFFL;
+       if (abe_port[id].protocol.direction == ABE_ATC_DIRECTION_IN)
+               selection |= 0x80000;
+
+       omap_aess_mem_write(abe, abe->fw_info->map[OMAP_AESS_DMEM_SLOT23_CTRL_ID],
+                           &selection);
+       return 0;
+}
+
+/**
+ * omap_aess_decide_main_port()  - Select stynchronization port for Event generator.
+ * @id: audio port name
+ *
+ * tells the FW which is the reference stream for adjusting
+ * the processing on 23/24/25 slots
+ *
+ * takes the first port in a list which is slave on the data interface
+ */
+static u32 abe_valid_port_for_synchro(u32 id)
+{
+       if ((abe_port[id].protocol.protocol_switch == DMAREQ_PORT_PROT) ||
+           (abe_port[id].protocol.protocol_switch == PINGPONG_PORT_PROT) ||
+           (abe_port[id].status != OMAP_ABE_PORT_ACTIVITY_RUNNING))
+               return 0;
+       else
+               return 1;
+}
+
+/**
+ * omap_aess_decide_main_port()  - Decide main port selection for synchronization.
+ * @abe: Pointer on aess handle
+ *
+ * Lock up on all ABE port in order to find out the correct port for the
+ * Audio Engine synchronization.
+ */
+void omap_aess_decide_main_port(struct omap_aess *abe)
+{
+       u32 id, id_not_found;
+
+       id_not_found = 1;
+       for (id = 0; id < LAST_PORT_ID - 1; id++) {
+               if (abe_valid_port_for_synchro(abe_port_priority[id])) {
+                       id_not_found = 0;
+                       break;
+               }
+       }
+
+       /* if no port is currently activated, the default one is PDM_DL */
+       if (id_not_found)
+               omap_aess_select_main_port(abe, OMAP_ABE_PDM_DL_PORT);
+       else
+               omap_aess_select_main_port(abe, abe_port_priority[id]);
+}
+
+/**
+ * abe_format_switch
+ * @f: port format
+ * @iter: port iteration
+ * @mulfac: multiplication factor
+ *
+ * translates the sampling and data length to ITER number for the DMA
+ * and the multiplier factor to apply during data move with DMEM
+ *
+ */
+static void abe_format_switch(struct omap_aess_data_format *f, u32 *iter, u32 *mulfac)
+{
+       u32 n_freq;
+       switch (f->f) {
+               /* nb of samples processed by scheduling loop */
+       case 8000:
+               n_freq = 2;
+               break;
+       case 16000:
+               n_freq = 4;
+               break;
+       case 24000:
+               n_freq = 6;
+               break;
+       case 44100:
+               n_freq = 12;
+               break;
+       case 96000:
+               n_freq = 24;
+               break;
+       default:
+               /*case 48000 */
+               n_freq = 12;
+               break;
+       }
+
+       switch (f->samp_format) {
+       case MONO_MSB:
+       case MONO_RSHIFTED_16:
+       case STEREO_16_16:
+               *mulfac = 1;
+               break;
+       case STEREO_MSB:
+       case STEREO_RSHIFTED_16:
+               *mulfac = 2;
+               break;
+       case THREE_MSB:
+               *mulfac = 3;
+               break;
+       case FOUR_MSB:
+               *mulfac = 4;
+               break;
+       case FIVE_MSB:
+               *mulfac = 5;
+               break;
+       case SIX_MSB:
+               *mulfac = 6;
+               break;
+       case SEVEN_MSB:
+               *mulfac = 7;
+               break;
+       case EIGHT_MSB:
+               *mulfac = 8;
+               break;
+       case NINE_MSB:
+               *mulfac = 9;
+               break;
+       default:
+               *mulfac = 1;
+               break;
+       }
+       *iter = (n_freq * (*mulfac));
+       if (f->samp_format == MONO_16_16)
+               *iter /= 2;
+}
+
+/**
+ * abe_dma_port_iteration
+ * @f: port format
+ *
+ * translates the sampling and data length to ITER number for the DMA
+ */
+static u32 abe_dma_port_iteration(struct omap_aess_data_format *f)
+{
+       u32 iter, mulfac;
+
+       abe_format_switch(f, &iter, &mulfac);
+       return iter;
+}
+
+/**
+ * abe_dma_port_iter_factor
+ * @f: port format
+ *
+ * returns the multiplier factor to apply during data move with DMEM
+ */
+static u32 abe_dma_port_iter_factor(struct omap_aess_data_format *f)
+{
+       u32 iter, mulfac;
+
+       abe_format_switch(f, &iter, &mulfac);
+       return mulfac;
+}
+
+/**
+ * omap_aess_dma_port_iter_factor
+ * @abe: Pointer on aess handle
+ * @f: port format
+ *
+ * returns the multiplier factor to apply during data move with DMEM
+ */
+static u32 omap_aess_dma_port_iter_factor(struct omap_aess *abe, struct omap_aess_data_format *f)
+{
+       u32 iter, mulfac;
+
+       abe_format_switch(f, &iter, &mulfac);
+       return mulfac;
+}
+
+/**
+ * omap_aess_mono_mixer
+ * @abe: Pointer on aess handle
+ * @id: name of the mixer (MIXDL1, MIXDL2 or MIXAUDUL)
+ * @on_off: enable/disable flag
+ *
+ * This API Programs DL1Mixer or DL2Mixer to output mono data
+ * on both left and right data paths.
+ */
+int omap_aess_mono_mixer(struct omap_aess *abe, u32 id, u32 on_off)
+{
+       struct omap_aess_task *task;
+
+       switch (id) {
+       case MIXDL1:
+               task = &abe->fw_info->dl1_mono_mixer[on_off];
+               break;
+       case MIXDL2:
+               task = &abe->fw_info->dl2_mono_mixer[on_off];
+               break;
+       case MIXAUDUL:
+               task = &abe->fw_info->audul_mono_mixer[on_off];
+               break;
+       default:
+               return 0;
+               break;
+       }
+
+       abe->MultiFrame[task->frame][task->slot] = task->task;
+
+       omap_aess_mem_write(abe, abe->fw_info->map[OMAP_AESS_DMEM_MULTIFRAME_ID],
+                           (u32 *)abe->MultiFrame);
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_mono_mixer);
+
+/**
+ * omap_aess_check_activity - Check if some ABE activity.
+ * @abe: Pointer on aess handle
+ *
+ * Check if any ABE ports are running.
+ * return 1: still activity on ABE
+ * return 0: no more activity on ABE. Event generator can be stopped
+ *
+ */
+int omap_aess_check_activity(struct omap_aess *abe)
+{
+       int i, ret = 0;
+
+       for (i = 0; i < (LAST_PORT_ID - 1); i++) {
+               if (abe_port[abe_port_priority[i]].status ==
+                               OMAP_ABE_PORT_ACTIVITY_RUNNING)
+                       break;
+       }
+       if (i < (LAST_PORT_ID - 1))
+               ret = 1;
+       return ret;
+}
+EXPORT_SYMBOL(omap_aess_check_activity);
+
+/**
+ * abe_write_pdmdl_offset - write the desired offset on the DL1/DL2 paths
+ * @abe: Pointer on aess handle
+ * @path: DL1 or DL2 port
+ * @offset_left: integer value that will be added on all PDM left samples
+ * @offset_right: integer value that will be added on all PDM right samples
+ *
+ * Set ABE internal DC offset cancellation parameter for McPDM IP. Value
+ * depends on TWL604x triming parameters.
+ */
+void omap_aess_write_pdmdl_offset(struct omap_aess *abe, u32 path,
+                                 u32 offset_left, u32 offset_right)
+{
+       u32 offset[2];
+
+       offset[0] = offset_right;
+       offset[1] = offset_left;
+
+       switch (path) {
+       case 1:
+               omap_aess_mem_write(abe, abe->fw_info->map[OMAP_AESS_SMEM_DC_HS_ID], offset);
+               break;
+       case 2:
+               omap_aess_mem_write(abe, abe->fw_info->map[OMAP_AESS_SMEM_DC_HF_ID], offset);
+               break;
+       default:
+               break;
+       }
+}
+EXPORT_SYMBOL(omap_aess_write_pdmdl_offset);
+
+/**
+ * oamp_abe_set_ping_pong_buffer
+ * @abe: Pointer on aess handle
+ * @port: ABE port ID
+ * @n_bytes: Size of Ping/Pong buffer
+ *
+ * Updates the next ping-pong buffer with "size" bytes copied from the
+ * host processor. This API notifies the FW that the data transfer is done.
+ */
+int omap_aess_set_ping_pong_buffer(struct omap_aess *abe, u32 port, u32 n_bytes)
+{
+       u32 struct_offset, n_samples, datasize, base_and_size;
+       struct ABE_SPingPongDescriptor desc_pp;
+       struct omap_aess_addr addr;
+
+       /* ping_pong is only supported on MM_DL */
+       if (port != OMAP_ABE_MM_DL_PORT) {
+               aess_err("Only Ping-pong port supported");
+               return -AESS_EINVAL;
+       }
+       /* translates the number of bytes in samples */
+       /* data size in DMEM words */
+       datasize = omap_aess_dma_port_iter_factor(abe, (struct omap_aess_data_format *)&((abe_port[port]).format));
+       /* data size in bytes */
+       datasize = datasize << 2;
+       n_samples = n_bytes / datasize;
+       memcpy(&addr, &abe->fw_info->map[OMAP_AESS_DMEM_PINGPONGDESC_ID],
+              sizeof(struct omap_aess_addr));
+       addr.bytes = sizeof(struct ABE_SPingPongDescriptor);
+       omap_aess_mem_read(abe, addr, (u32 *)&desc_pp);
+       /*
+        * read the port SIO descriptor and extract the current pointer
+        * address after reading the counter
+        */
+       if ((desc_pp.counter & 0x1) == 0) {
+               struct_offset = (u32)&(desc_pp.nextbuff0_BaseAddr) -
+                       (u32)&(desc_pp);
+               base_and_size = desc_pp.nextbuff0_BaseAddr;
+       } else {
+               struct_offset = (u32)&(desc_pp.nextbuff1_BaseAddr) -
+                       (u32)&(desc_pp);
+               base_and_size = desc_pp.nextbuff1_BaseAddr;
+       }
+
+       base_and_size = abe->pp_buf_addr[abe->pp_buf_id_next];
+       abe->pp_buf_id_next = (abe->pp_buf_id_next + 1) & 0x03;
+
+       base_and_size = (base_and_size & 0xFFFFL) + (n_samples << 16);
+
+       addr.offset += struct_offset;
+       addr.bytes = 4;
+       omap_aess_mem_write(abe, addr, (u32 *)&base_and_size);
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_set_ping_pong_buffer);
+
+/**
+ * omap_aess_read_offset_from_ping_buffer
+ * @abe: Pointer on aess handle
+ * @id: ABE port ID
+ * @n:  returned address of the offset
+ *     from the ping buffer start address (in samples)
+ *
+ * Computes the current firmware ping pong read pointer location,
+ * expressed in samples, as the offset from the start address of ping buffer.
+ */
+int omap_aess_read_offset_from_ping_buffer(struct omap_aess *abe,
+                                         u32 id, u32 *n)
+{
+       struct ABE_SPingPongDescriptor desc_pp;
+       struct omap_aess_addr addr;
+
+       /* ping_pong is only supported on MM_DL */
+       if (OMAP_ABE_MM_DL_PORT != id) {
+               aess_err("Only Ping-pong port supported");
+               return -AESS_EINVAL;
+       } else {
+               /* read the port SIO ping pong descriptor */
+               memcpy(&addr, &abe->fw_info->map[OMAP_AESS_DMEM_PINGPONGDESC_ID],
+                      sizeof(struct omap_aess_addr));
+               addr.bytes = sizeof(struct ABE_SPingPongDescriptor);
+               omap_aess_mem_read(abe, addr, (u32 *)&desc_pp);
+               /* extract the current ping pong buffer read pointer based on
+                  the value of the counter */
+               if ((desc_pp.counter & 0x1) == 0) {
+                       /* the next is buffer0, hence the current is buffer1 */
+                       *n = desc_pp.nextbuff1_Samples -
+                               desc_pp.workbuff_Samples;
+               } else {
+                       /* the next is buffer1, hence the current is buffer0 */
+                       *n = desc_pp.nextbuff0_Samples -
+                               desc_pp.workbuff_Samples;
+               }
+               switch (abe_port[OMAP_ABE_MM_DL_PORT].format.samp_format) {
+               case MONO_MSB:
+               case MONO_RSHIFTED_16:
+               case STEREO_16_16:
+                       *n +=  abe->pp_buf_id * abe->size_pingpong / 4;
+                       break;
+               case STEREO_MSB:
+               case STEREO_RSHIFTED_16:
+                       *n += abe->pp_buf_id * abe->size_pingpong / 8;
+                       break;
+               default:
+                       aess_err("Bad data format for Ping-pong buffer");
+                       return -AESS_EINVAL;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_read_offset_from_ping_buffer);
+
+/**
+ * omap_aess_irq_ping_pong
+ * @abe: Pointer on aess handle
+ *
+ * Call the respective subroutine depending on the IRQ FIFO content:
+ * APS interrupts : IRQ_FIFO[31:28] = IRQtag_APS,
+ *     IRQ_FIFO[27:16] = APS_IRQs, IRQ_FIFO[15:0] = loopCounter
+ * SEQ interrupts : IRQ_FIFO[31:28] = IRQtag_COUNT,
+ *     IRQ_FIFO[27:16] = Count_IRQs, IRQ_FIFO[15:0] = loopCounter
+ * Ping-Pong Interrupts : IRQ_FIFO[31:28] = IRQtag_PP,
+ *     IRQ_FIFO[27:16] = PP_MCU_IRQ, IRQ_FIFO[15:0] = loopCounter
+ */
+void omap_aess_irq_ping_pong(struct omap_aess *abe)
+{
+       /* first IRQ doesn't represent a buffer transference completion */
+       if (abe->pp_first_irq)
+               abe->pp_first_irq = 0;
+       else
+               abe->pp_buf_id = (abe->pp_buf_id + 1) & 0x03;
+
+       omap_aess_call_subroutine(abe, abe->seq.irq_pingpong_player_id,
+                                 NOPARAMETER, NOPARAMETER,
+                                 NOPARAMETER, NOPARAMETER);
+}
+
+/**
+ * omap_aess_read_next_ping_pong_buffer
+ * @abe: Pointer on aess handle
+ * @port: ABE portID
+ * @p: Next buffer address (pointer)
+ * @n: Next buffer size (pointer)
+ *
+ * Tell the next base address of the next ping_pong Buffer and its size
+ */
+int omap_aess_read_next_ping_pong_buffer(struct omap_aess *abe, u32 port,
+                                        u32 *p, u32 *n)
+{
+       struct ABE_SPingPongDescriptor desc_pp;
+       struct omap_aess_addr addr;
+
+       /* ping_pong is only supported on MM_DL */
+       if (port != OMAP_ABE_MM_DL_PORT) {
+               aess_err("Only Ping-pong port supported");
+               return -AESS_EINVAL;
+       }
+       /* read the port SIO descriptor and extract the current pointer
+          address after reading the counter */
+       memcpy(&addr, &abe->fw_info->map[OMAP_AESS_DMEM_PINGPONGDESC_ID],
+              sizeof(struct omap_aess_addr));
+       addr.bytes = sizeof(struct ABE_SPingPongDescriptor);
+       omap_aess_mem_read(abe, addr, (u32 *)&desc_pp);
+
+       if ((desc_pp.counter & 0x1) == 0)
+               *p = desc_pp.nextbuff0_BaseAddr;
+       else
+               *p = desc_pp.nextbuff1_BaseAddr;
+
+       /* translates the number of samples in bytes */
+       *n = abe->size_pingpong;
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_read_next_ping_pong_buffer);
+
+/**
+ * omap_aess_init_ping_pong_buffer
+ * @abe: Pointer on aess handle
+ * @id: ABE port ID
+ * @size_bytes:size of the ping pong
+ * @n_buffers:number of buffers (2 = ping/pong)
+ * @p:returned address of the ping-pong list of base addresses
+ *     (byte offset from DMEM start)
+ *
+ * Computes the base address of the ping_pong buffers
+ */
+static int omap_aess_init_ping_pong_buffer(struct omap_aess *abe,
+                                          u32 id, u32 size_bytes,
+                                          u32 n_buffers, u32 *p)
+{
+       u32 i, dmem_addr;
+       struct omap_aess_addr addr;
+
+       /* ping_pong is supported in 2 buffers configuration right now but FW
+          is ready for ping/pong/pung/pang... */
+       if (id != OMAP_ABE_MM_DL_PORT || n_buffers > MAX_PINGPONG_BUFFERS) {
+               aess_err("Too Many Ping-pong buffers requested");
+               return -AESS_EINVAL;
+       }
+
+       memcpy(&addr, &abe->fw_info->map[OMAP_AESS_DMEM_PING_ID],
+              sizeof(struct omap_aess_addr));
+
+       for (i = 0; i < n_buffers; i++) {
+               dmem_addr = addr.offset + (i * size_bytes);
+               /* base addresses of the ping pong buffers in U8 unit */
+               abe->base_address_pingpong[i] = dmem_addr;
+       }
+
+       for (i = 0; i < 4; i++)
+               abe->pp_buf_addr[i] = addr.offset + (i * size_bytes);
+       abe->pp_buf_id = 0;
+       abe->pp_buf_id_next = 0;
+       abe->pp_first_irq = 1;
+
+       /* global data */
+       abe->size_pingpong = size_bytes;
+       *p = (u32)addr.offset;
+       return 0;
+}
+
+/**
+ * omap_aess_connect_irq_ping_pong_port
+ * @abe: Pointer on aess handle
+ * @id: port name
+ * @f: desired data format
+ * @subroutine_id: index of the call-back subroutine to call
+ * @size: half-buffer (ping) size
+ * @sink: returned base address of the first (ping) buffer)
+ * @dsp_mcu_flag: Ping/pong interrupt direction (MPU or DSP)
+ *
+ * enables the data echanges between a direct access to the DMEM
+ * memory of ABE using cache flush. On each IRQ activation a subroutine
+ * registered with "abe_plug_subroutine" will be called. This subroutine
+ * will generate an amount of samples, send them to DMEM memory and call
+ * "abe_set_ping_pong_buffer" to notify the new amount of samples in the
+ * pong buffer.
+ */
+int omap_aess_connect_irq_ping_pong_port(struct omap_aess *abe,
+                                        u32 id, struct omap_aess_data_format *f,
+                                        u32 subroutine_id, u32 size,
+                                        u32 *sink, u32 dsp_mcu_flag)
+{
+       struct omap_aess_addr addr;
+
+       /* ping_pong is only supported on MM_DL */
+       if (id != OMAP_ABE_MM_DL_PORT) {
+               aess_err("Only Ping-pong port supported");
+               return -AESS_EINVAL;
+       }
+
+       memcpy(&addr, &abe->fw_info->map[OMAP_AESS_DMEM_PING_ID],
+              sizeof(struct omap_aess_addr));
+
+       abe_port[id] = ((struct omap_aess_port *)abe->fw_info->port)[id];
+       (abe_port[id]).format = (*f);
+       (abe_port[id]).protocol.protocol_switch = PINGPONG_PORT_PROT;
+       (abe_port[id]).protocol.p.prot_pingpong.buf_addr = addr.offset;
+       (abe_port[id]).protocol.p.prot_pingpong.buf_size = size;
+       (abe_port[id]).protocol.p.prot_pingpong.irq_data = (1);
+       omap_aess_init_ping_pong_buffer(abe, OMAP_ABE_MM_DL_PORT, size, 2, sink);
+       if (dsp_mcu_flag == PING_PONG_WITH_MCU_IRQ)
+               (abe_port[id]).protocol.p.prot_pingpong.irq_addr =
+                       ABE_MCU_IRQSTATUS_RAW;
+       if (dsp_mcu_flag == PING_PONG_WITH_DSP_IRQ)
+               (abe_port[id]).protocol.p.prot_pingpong.irq_addr =
+                       ABE_DSP_IRQSTATUS_RAW;
+       abe_port[id].status = OMAP_ABE_PORT_INITIALIZED;
+
+       /* load the ATC descriptors - disabled */
+       omap_aess_init_atc(abe, id);
+       /* load the micro-task parameters */
+       omap_aess_init_io_tasks(abe,  id, &((abe_port[id]).format),
+                               &((abe_port[id]).protocol));
+
+       *sink = (abe_port[id]).protocol.p.prot_pingpong.buf_addr;
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_connect_irq_ping_pong_port);
+
diff --git a/sound/soc/omap/aess/abe_port.h b/sound/soc/omap/aess/abe_port.h
new file mode 100644 (file)
index 0000000..42d31f7
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _ABE_PORT_H_
+#define _ABE_PORT_H_
+
+struct ABE_STask {
+       /* 0 ... Index of called function */
+       u16 iF;
+       /* 2 ... for INITPTR of A0 */
+       u16 A0;
+       /* 4 ... for INITPTR of A1 */
+       u16 A1;
+       /* 6 ... for INITPTR of A2 & A3 */
+       u16 A2_3;
+       /* 8 ... for INITPTR of A4 & A5 */
+       u16 A4_5;
+       /* 10 ... for INITREG of R0, R1, R2, R3 */
+       u16 R;
+       /* 12 */
+       u16 misc0;
+       /* 14 */
+       u16 misc1;
+};
+
+#define ABE_TASK_ID(ID) (OMAP_ABE_D_TASKSLIST_ADDR + sizeof(struct ABE_STask)*(ID))
+
+struct ABE_SIODescriptor {
+       /* 0 */
+       u16 drift_asrc;
+       /* 2 */
+       u16 drift_io;
+       /* 4 "Function index" of XLS sheet "Functions" */
+       u8 io_type_idx;
+       /* 5 1 = MONO or Stereo1616, 2= STEREO, ... */
+       u8 samp_size;
+       /* 6 drift "issues" for ASRC */
+       s16 flow_counter;
+       /* 8 address for IRQ or DMArequests */
+       u16 hw_ctrl_addr;
+       /* 10 DMA request bit-field or IRQ (DSP/MCU) */
+       u8 atc_irq_data;
+       /* 11 0 = Read, 3 = Write */
+       u8 direction_rw;
+       /* 12 */
+       u8 repeat_last_samp;
+       /* 13 12 at 48kHz, ... */
+       u8 nsamp;
+       /* 14 nsamp x samp_size */
+       u8 x_io;
+       /* 15 ON = 0x80, OFF = 0x00 */
+       u8 on_off;
+       /* 16 For Slimbus and TDM purpose */
+       u16 split_addr1;
+       /* 18 */
+       u16 split_addr2;
+       /* 20 */
+       u16 split_addr3;
+       /* 22 */
+       u8 before_f_index;
+       /* 23 */
+       u8 after_f_index;
+       /* 24 SM/CM INITPTR field */
+       u16 smem_addr1;
+       /* 26 in bytes */
+       u16 atc_address1;
+       /* 28 DMIC_ATC_PTR, MCPDM_UL_ATC_PTR, ... */
+       u16 atc_pointer_saved1;
+       /* 30 samp_size (except in TDM or Slimbus) */
+       u8 data_size1;
+       /* 31 "Function index" of XLS sheet "Functions" */
+       u8 copy_f_index1;
+       /* 32 For Slimbus and TDM purpose */
+       u16 smem_addr2;
+       /* 34 */
+       u16 atc_address2;
+       /* 36 */
+       u16 atc_pointer_saved2;
+       /* 38 */
+       u8 data_size2;
+       /* 39 */
+       u8 copy_f_index2;
+};
+
+#ifdef __KERNEL__
+int omap_aess_select_main_port(struct omap_aess *abe, u32 id);
+void omap_aess_build_scheduler_table(struct omap_aess *abe);
+int omap_aess_reset_port(struct omap_aess *abe, u32 id);
+void omap_aess_irq_ping_pong(struct omap_aess *abe);
+#endif
+
+#endif /* _ABE_PORT_H_ */
diff --git a/sound/soc/omap/aess/abe_seq.c b/sound/soc/omap/aess/abe_seq.c
new file mode 100644 (file)
index 0000000..310c42d
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "abe.h"
+#include "abe_dbg.h"
+
+/* Maximun subroutines for ABE sequences */
+#define OMAP_ABE_MAX_SUB_ROUTINE 10
+
+struct omap_aess_subroutine {
+       u32 sub_id;
+       s32 param[4];
+};
+
+
+/* table of new subroutines called in the sequence */
+static abe_subroutine2 abe_all_subsubroutine[OMAP_ABE_MAX_SUB_ROUTINE];
+/* number of parameters per calls */
+static u32 abe_all_subsubroutine_nparam[OMAP_ABE_MAX_SUB_ROUTINE];
+/* paramters of the subroutine (if any) */
+static u32 abe_all_subroutine_params[OMAP_ABE_MAX_SUB_ROUTINE][4];
+
+/**
+ * omap_aess_dummy_subroutine
+ *
+ */
+void omap_aess_dummy_subroutine(void)
+{
+}
+
+/**
+ * omap_aess_add_subroutine
+ * @abe: Pointer on aess handle
+ * @id: ABE port id
+ * @f: pointer to the subroutines
+ * @nparam: number of parameters
+ * @params: pointer to the parameters
+ *
+ * add one function pointer more and returns the index to it
+ */
+int omap_aess_add_subroutine(struct omap_aess *abe, u32 *id, abe_subroutine2 f, u32 nparam, u32 *params)
+{
+       u32 i, i_found;
+
+       if ((abe->seq.write_pointer >= OMAP_ABE_MAX_SUB_ROUTINE) ||
+           ((u32) f == 0)) {
+               aess_err("Too many subroutine Plugged");
+               return -AESS_EINVAL;
+       } else {
+               /* search if this subroutine address was not already
+                * declared, then return the previous index
+                */
+               for (i_found = abe->seq.write_pointer, i = 0;
+                    i < abe->seq.write_pointer; i++) {
+                       if (f == abe_all_subsubroutine[i])
+                               i_found = i;
+               }
+
+               if (i_found == abe->seq.write_pointer) {
+                       /* Sub routine not listed - Add it */
+                       *id = abe->seq.write_pointer;
+                       abe_all_subsubroutine[i_found] = (f);
+                       for (i = 0; i < nparam; i++)
+                               abe_all_subroutine_params[i_found][i] = params[i];
+                       abe_all_subsubroutine_nparam[i_found] = nparam;
+                       abe->seq.write_pointer++;
+               } else {
+                       /* Sub routine listed - Update parameters */
+                       for (i = 0; i < nparam; i++)
+                               abe_all_subroutine_params[i_found][i] = params[i];
+                       abe_all_subsubroutine_nparam[i_found] = nparam;
+                       *id = i_found;
+               }
+       }
+       return 0;
+}
+
+/**
+ * omap_aess_init_subroutine_table
+ * @abe: Pointer on aess handle
+ *
+ * initializes the default table of pointers to subroutines
+ *
+ */
+void omap_aess_init_subroutine_table(struct omap_aess *abe)
+{
+       u32 id;
+
+       /* reset the table's pointers */
+       abe->seq.write_pointer = 0;
+
+       /* the first index is the NULL task */
+       omap_aess_add_subroutine(abe, &id,
+                                (abe_subroutine2)omap_aess_dummy_subroutine,
+                                0, (u32 *)0);
+
+       omap_aess_add_subroutine(abe, &abe->seq.irq_pingpong_player_id,
+                                (abe_subroutine2)omap_aess_dummy_subroutine,
+                                0, (u32 *)0);
+}
+
+/**
+ * omap_aess_reset_all_sequence
+ * @abe: Pointer on aess handle
+ *
+ * load default configuration for all sequences
+ * kill any running activities
+ */
+void omap_aess_reset_all_sequence(struct omap_aess *abe)
+{
+       omap_aess_init_subroutine_table(abe);
+}
+
+/**
+ * omap_aess_call_subroutine
+ * @abe: Pointer on aess handle
+ * @idx: index to the table of all registered Call-backs and subroutines
+ * @p1: first parameter
+ * @p2: second parameter
+ * @p3: 3rd parameter
+ * @p4: 4th parameter
+ *
+ * run and log a subroutine
+ */
+void omap_aess_call_subroutine(struct omap_aess *abe, u32 idx, u32 p1, u32 p2, u32 p3, u32 p4)
+{
+       abe_subroutine0 f0;
+       abe_subroutine1 f1;
+       abe_subroutine2 f2;
+       abe_subroutine3 f3;
+       abe_subroutine4 f4;
+       u32 *params;
+
+       if (idx > OMAP_ABE_MAX_SUB_ROUTINE)
+               return;
+
+       switch (idx) {
+       default:
+               switch (abe_all_subsubroutine_nparam[idx]) {
+               case 0:
+                       f0 = (abe_subroutine0)abe_all_subsubroutine[idx];
+                       (*f0)();
+                       break;
+               case 1:
+                       f1 = (abe_subroutine1)abe_all_subsubroutine[idx];
+                       params = abe_all_subroutine_params[idx];
+                       if (params != (u32 *)0)
+                               p1 = params[0];
+                       (*f1)(p1);
+                       break;
+               case 2:
+                       f2 = abe_all_subsubroutine[idx];
+                       params = abe_all_subroutine_params[idx];
+                       if (params != (u32 *)0) {
+                               p1 = params[0];
+                               p2 = params[1];
+                       }
+                       (*f2)(p1, p2);
+                       break;
+               case 3:
+                       f3 = (abe_subroutine3) abe_all_subsubroutine[idx];
+                       params = abe_all_subroutine_params[idx];
+                       if (params != (u32 *)0) {
+                               p1 = params[0];
+                               p2 = params[1];
+                               p3 = params[2];
+                       }
+                       (*f3)(p1, p2, p3);
+                       break;
+               case 4:
+                       f4 = (abe_subroutine4) abe_all_subsubroutine[idx];
+                       params = abe_all_subroutine_params[idx];
+                       if (params != (u32 *)0) {
+                               p1 = params[0];
+                               p2 = params[1];
+                               p3 = params[2];
+                               p4 = params[3];
+                       }
+                       (*f4)(p1, p2, p3, p4);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+/**
+ * omap_aess_plug_subroutine
+ * @abe: Pointer on aess handle
+ * @id: returned sequence index after plugging a new subroutine
+ * @f: subroutine address to be inserted
+ * @n: number of parameters of this subroutine
+ * @params: pointer on parameters
+ *
+ * register a list of subroutines for call-back purpose
+ */
+int omap_aess_plug_subroutine(struct omap_aess *abe, u32 *id,
+                             abe_subroutine2 f, u32 n, u32 *params)
+{
+       omap_aess_add_subroutine(abe, id, (abe_subroutine2)f, n,
+                                (u32 *)params);
+       return 0;
+}
+EXPORT_SYMBOL(omap_aess_plug_subroutine);
diff --git a/sound/soc/omap/aess/abe_seq.h b/sound/soc/omap/aess/abe_seq.h
new file mode 100644 (file)
index 0000000..fe17e62
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _ABE_SEQ_H_
+#define _ABE_SEQ_H_
+
+void omap_aess_reset_all_sequence(struct omap_aess *abe);
+void omap_aess_call_subroutine(struct omap_aess *abe, u32 idx, u32 p1, u32 p2, u32 p3, u32 p4);
+
+#endif /* _ABE_SEQ_H_ */
diff --git a/sound/soc/omap/aess/abe_typ.h b/sound/soc/omap/aess/abe_typ.h
new file mode 100644 (file)
index 0000000..831bf68
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2012 Texas Instruments Incorporated,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Texas Instruments Incorporated nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "abe_def.h"
+
+#ifndef _ABE_TYP_H_
+#define _ABE_TYP_H_
+
+/*
+ *     BASIC TYPES
+ */
+#define MAX_UINT8      ((((1L <<  7) - 1) << 1) + 1)
+#define MAX_UINT16     ((((1L << 15) - 1) << 1) + 1)
+#define MAX_UINT32     ((((1L << 31) - 1) << 1) + 1)
+#include <linux/types.h>
+
+/* subroutine types */
+typedef void (*abe_subroutine0) (void);
+typedef void (*abe_subroutine1) (u32);
+typedef void (*abe_subroutine2) (u32, u32);
+typedef void (*abe_subroutine3) (u32, u32, u32);
+typedef void (*abe_subroutine4) (u32, u32, u32, u32);
+/*
+ *     OPP TYPE
+ *
+ *             0: Ultra Lowest power consumption audio player
+ *             1: OPP 25% (simple multimedia features)
+ *             2: OPP 50% (multimedia and voice calls)
+ *             3: OPP100% (multimedia complex use-cases)
+ */
+#define ABE_OPP0 0
+#define ABE_OPP25 1
+#define ABE_OPP50 2
+#define ABE_OPP100 3
+
+/*
+ *     SAMPLES TYPE
+ *
+ *     mono 16 bit sample LSB aligned, 16 MSB bits are unused;
+ *     mono right shifted to 16bits LSBs on a 32bits DMEM FIFO for McBSP
+ *     TX purpose;
+ *     mono sample MSB aligned (16/24/32bits);
+ *     two successive mono samples in one 32bits container;
+ *     Two L/R 16bits samples in a 32bits container;
+ *     Two channels defined with two MSB aligned samples;
+ *     Three channels defined with three MSB aligned samples (MIC);
+ *     Four channels defined with four MSB aligned samples (MIC);
+ *     . . .
+ *     Eight channels defined with eight MSB aligned samples (MIC);
+ */
+#define MONO_MSB 1
+#define MONO_RSHIFTED_16 2
+#define STEREO_RSHIFTED_16 3
+#define STEREO_16_16 4
+#define STEREO_MSB 5
+#define THREE_MSB 6
+#define FOUR_MSB 7
+#define FIVE_MSB 8
+#define SIX_MSB 9
+#define SEVEN_MSB 10
+#define EIGHT_MSB 11
+#define NINE_MSB 12
+#define TEN_MSB 13
+#define MONO_16_16 14
+
+/*
+ *     PORT PROTOCOL TYPE - abe_port_protocol_switch_id
+ */
+#define SLIMBUS_PORT_PROT 1
+#define SERIAL_PORT_PROT 2
+#define TDM_SERIAL_PORT_PROT 3
+#define DMIC_PORT_PROT 4
+#define MCPDMDL_PORT_PROT 5
+#define MCPDMUL_PORT_PROT 6
+#define PINGPONG_PORT_PROT 7
+#define DMAREQ_PORT_PROT 8
+
+/*
+ *     PORT IDs, this list is aligned with the FW data mapping
+ */
+#define OMAP_ABE_DMIC_PORT 0
+#define OMAP_ABE_PDM_UL_PORT 1
+#define OMAP_ABE_BT_VX_UL_PORT 2
+#define OMAP_ABE_MM_UL_PORT 3
+#define OMAP_ABE_MM_UL2_PORT 4
+#define OMAP_ABE_VX_UL_PORT 5
+#define OMAP_ABE_MM_DL_PORT 6
+#define OMAP_ABE_VX_DL_PORT 7
+#define OMAP_ABE_TONES_DL_PORT 8
+#define OMAP_ABE_MCASP_DL_PORT 9
+#define OMAP_ABE_BT_VX_DL_PORT 10
+#define OMAP_ABE_PDM_DL_PORT 11
+#define OMAP_ABE_MM_EXT_OUT_PORT 12
+#define OMAP_ABE_MM_EXT_IN_PORT 13
+#define TDM_DL_PORT 14
+#define TDM_UL_PORT 15
+#define DEBUG_PORT 16
+#define LAST_PORT_ID 17
+
+#define FEAT_MIXDL1         14
+#define FEAT_MIXDL2         15
+#define FEAT_MIXAUDUL       16
+#define FEAT_GAINS          21
+#define FEAT_GAINS_DMIC1    22
+#define FEAT_GAINS_DMIC2    23
+#define FEAT_GAINS_DMIC3    24
+#define FEAT_GAINS_AMIC     25
+#define FEAT_GAIN_BTUL      29
+
+/* abe_mixer_id */
+#define MIXDL1 FEAT_MIXDL1
+#define MIXDL2 FEAT_MIXDL2
+#define MIXAUDUL FEAT_MIXAUDUL
+/*
+ *     GAIN IDs
+ */
+#define GAINS_DMIC1     FEAT_GAINS_DMIC1
+#define GAINS_DMIC2     FEAT_GAINS_DMIC2
+#define GAINS_DMIC3     FEAT_GAINS_DMIC3
+#define GAINS_AMIC      FEAT_GAINS_AMIC
+#define GAINS_BTUL      FEAT_GAIN_BTUL
+
+/*
+ *     EVENT GENERATORS - abe_event_id
+ */
+#define EVENT_TIMER 0
+#define EVENT_44100 1
+/*
+ *     SERIAL PORTS IDs - abe_mcbsp_id
+ */
+#define MCBSP1_TX MCBSP1_DMA_TX
+#define MCBSP1_RX MCBSP1_DMA_RX
+#define MCBSP2_TX MCBSP2_DMA_TX
+#define MCBSP2_RX MCBSP2_DMA_RX
+#define MCBSP3_TX MCBSP3_DMA_TX
+#define MCBSP3_RX MCBSP3_DMA_RX
+
+/*
+ *     SERIAL PORTS IDs - abe_mcasp_id
+ */
+#define MCASP1_TX      McASP1_AXEVT
+#define MCASP1_RX      McASP1_AREVT
+
+/*
+ *     DATA_FORMAT_T
+ *
+ *     used in port declaration
+ */
+struct omap_aess_data_format {
+       /* Sampling frequency of the stream */
+       u32 f;
+       /* Sample format type  */
+       u32 samp_format;
+};
+
+/*
+ *     PORT_PROTOCOL_T
+ *
+ *     port declaration
+ */
+struct omap_aess_port_protocol {
+       /* Direction=0 means input from AESS point of view */
+       u32 direction;
+       /* Protocol type (switch) during the data transfers */
+       u32 protocol_switch;
+       union {
+               /* Slimbus peripheral connected to ATC */
+               struct {
+                       /* Address of ATC Slimbus descriptor's index */
+                       u32 desc_addr1;
+                       /* DMEM address 1 in bytes */
+                       u32 buf_addr1;
+                       /* DMEM buffer size size in bytes */
+                       u32 buf_size;
+                       /* ITERation on each DMAreq signals */
+                       u32 iter;
+                       /* Second ATC index for SlimBus reception (or NULL) */
+                       u32 desc_addr2;
+                       /* DMEM address 2 in bytes */
+                       u32 buf_addr2;
+               } prot_slimbus;
+               /* McBSP/McASP peripheral connected to ATC */
+               struct {
+                       u32 desc_addr;
+                       /* Address of ATC McBSP/McASP descriptor's in bytes */
+                       u32 buf_addr;
+                       /* DMEM address in bytes */
+                       u32 buf_size;
+                       /* ITERation on each DMAreq signals */
+                       u32 iter;
+               } prot_serial;
+               /* DMIC peripheral connected to ATC */
+               struct {
+                       /* DMEM address in bytes */
+                       u32 buf_addr;
+                       /* DMEM buffer size in bytes */
+                       u32 buf_size;
+                       /* Number of activated DMIC */
+                       u32 nbchan;
+               } prot_dmic;
+               /* McPDMDL peripheral connected to ATC */
+               struct {
+                       /* DMEM address in bytes */
+                       u32 buf_addr;
+                       /* DMEM size in bytes */
+                       u32 buf_size;
+                       /* Control allowed on McPDM DL */
+                       u32 control;
+               } prot_mcpdmdl;
+               /* McPDMUL peripheral connected to ATC */
+               struct {
+                       /* DMEM address size in bytes */
+                       u32 buf_addr;
+                       /* DMEM buffer size size in bytes */
+                       u32 buf_size;
+               } prot_mcpdmul;
+               /* Ping-Pong interface to the Host using cache-flush */
+               struct {
+                       /* Address of ATC descriptor's */
+                       u32 desc_addr;
+                       /* DMEM buffer base address in bytes */
+                       u32 buf_addr;
+                       /* DMEM size in bytes for each ping and pong buffers */
+                       u32 buf_size;
+                       /* IRQ address (either DMA (0) MCU (1) or DSP(2)) */
+                       u32 irq_addr;
+                       /* IRQ data content loaded in the AESS IRQ register */
+                       u32 irq_data;
+                       /* Call-back function upon IRQ reception */
+                       u32 callback;
+               } prot_pingpong;
+               /* DMAreq line to CBPr */
+               struct {
+                       /* Address of ATC descriptor's */
+                       u32 desc_addr;
+                       /* DMEM buffer address in bytes */
+                       u32 buf_addr;
+                       /* DMEM buffer size size in bytes */
+                       u32 buf_size;
+                       /* ITERation on each DMAreq signals */
+                       u32 iter;
+                       /* DMAreq address */
+                       u32 dma_addr;
+                       /* DMA/AESS = 1 << #DMA */
+                       u32 dma_data;
+               } prot_dmareq;
+               /* Circular buffer - direct addressing to DMEM */
+               struct {
+                       /* DMEM buffer base address in bytes */
+                       u32 buf_addr;
+                       /* DMEM buffer size in bytes */
+                       u32 buf_size;
+                       /* DMAreq address */
+                       u32 dma_addr;
+                       /* DMA/AESS = 1 << #DMA */
+                       u32 dma_data;
+               } prot_circular_buffer;
+       } p;
+};
+
+struct omap_aess_dma_offset {
+       /* Offset to the first address of the */
+       u32 data;
+       /* number of iterations for the DMA data moves. */
+       u32 iter;
+};
+
+/*
+ *     ABE_PORT_T status / format / sampling / protocol(call_back) /
+ *     features / gain / name ..
+ *
+ */
+
+struct omap_aess_task {
+       u8 frame;
+       u8 slot;
+       u16 task;
+};
+
+struct omap_aess_init_task1 {
+       u32 nb_task;
+       struct omap_aess_task task[2];
+};
+
+struct omap_aess_init_task {
+       u32 nb_task;
+       struct omap_aess_task *task;
+};
+
+struct omap_aess_io_task {
+       u32 nb_task;
+       u32 smem;
+       struct omap_aess_task *task;
+};
+
+struct omap_aess_io_task1 {
+       u32 nb_task;
+       u32 smem;
+       struct omap_aess_task task[2];
+};
+
+struct omap_aess_port_type {
+       struct omap_aess_init_task1 serial;
+       struct omap_aess_init_task1 cbpr;
+};
+
+struct omap_aess_asrc_port {
+       struct omap_aess_io_task1 task;
+       struct omap_aess_port_type asrc;
+};
+
+struct omap_aess_port {
+       /* running / idled */
+       u16 status;
+       /* Sample format type  */
+       struct omap_aess_data_format format;
+       /* API : for ASRC */
+       s32 drift;
+       /* optionnal call-back index for errors and ack */
+       u16 callback;
+       /* IO tasks buffers */
+       u16 smem_buffer1;
+       u16 smem_buffer2;
+       struct omap_aess_port_protocol protocol;
+       /* pointer and iteration counter of the xDMA */
+       struct omap_aess_dma_offset dma;
+       struct omap_aess_init_task1 task;
+       struct omap_aess_asrc_port tsk_freq[4];
+};
+
+/*
+ *     ROUTER_T
+ *
+ *     table of indexes in unsigned bytes
+ */
+typedef u16 abe_router_t;
+/*
+ *     DRIFT_T abe_drift_t = s32
+ *
+ *     ASRC drift parameter in [ppm] value
+ */
+/*
+ *  --------------------   INTERNAL DATA TYPES  ---------------------
+ */
+/*
+ *     ABE_IRQ_DATA_T
+ *
+ *     IRQ FIFO content declaration
+ *     APS interrupts : IRQ_FIFO[31:28] = IRQtag_APS,
+ *             IRQ_FIFO[27:16] = APS_IRQs, IRQ_FIFO[15:0] = loopCounter
+ *     SEQ interrupts : IRQ_FIFO[31:28] IRQtag_COUNT,
+ *             IRQ_FIFO[27:16] = Count_IRQs, IRQ_FIFO[15:0] = loopCounter
+ *     Ping-Pong Interrupts : IRQ_FIFO[31:28] = IRQtag_PP,
+ *             IRQ_FIFO[27:16] = PP_MCU_IRQ, IRQ_FIFO[15:0] = loopCounter
+ */
+struct omap_aess_irq_data {
+       unsigned int counter:16;
+       unsigned int data:12;
+       unsigned int tag:4;
+};
+
+#endif /* ifndef _ABE_TYP_H_ */
diff --git a/sound/soc/omap/aess/port_mgr.c b/sound/soc/omap/aess/port_mgr.c
new file mode 100644 (file)
index 0000000..89d1d1e
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * ALSA SoC OMAP ABE port manager
+ *
+ * Author: Liam Girdwood <lrg@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+
+#include "abe.h"
+#include "abe_port.h"
+
+/* this must match logical ID numbers in port_mgr.h */
+static const char *lport_name[] = {
+               "dmic0", "dmic1", "dmic2", "pdmdl1", "pdmdl2", "mcasp",
+               "pdmul1", "bt_vx_dl", "bt_vx_ul", "mm_ext_ul", "mm_ext_dl",
+               "mm_dl1", "mm_ul1", "mm_ul2", "vx_dl", "vx_ul", "tones",
+               "mm_dl_lp"
+};
+
+static DEFINE_MUTEX(port_mgr_mutex);
+static struct omap_aess *the_abe = NULL;
+static int users = 0;
+
+/*
+ * Get the Physical port ID based on the logical port ID
+ *
+ * FE and BE ports have unique ID's within the driver but share
+ * ID's within the ABE. This maps a driver port ID to an ABE port ID.
+ */
+static int get_physical_id(int logical_id)
+{
+       switch (logical_id) {
+       /* backend ports */
+       case OMAP_ABE_BE_PORT_DMIC0:
+       case OMAP_ABE_BE_PORT_DMIC1:
+       case OMAP_ABE_BE_PORT_DMIC2:
+               return OMAP_ABE_DMIC_PORT;
+       case OMAP_ABE_BE_PORT_PDM_DL1:
+       case OMAP_ABE_BE_PORT_PDM_DL2:
+               return OMAP_ABE_PDM_DL_PORT;
+       case OMAP_ABE_BE_PORT_MCASP:
+               return OMAP_ABE_MCASP_DL_PORT;
+       case OMAP_ABE_BE_PORT_PDM_UL1:
+               return OMAP_ABE_PDM_UL_PORT;
+       case OMAP_ABE_BE_PORT_BT_VX_DL:
+               return OMAP_ABE_BT_VX_DL_PORT;
+       case OMAP_ABE_BE_PORT_BT_VX_UL:
+               return OMAP_ABE_BT_VX_UL_PORT;
+       case OMAP_ABE_BE_PORT_MM_EXT_DL:
+               return OMAP_ABE_MM_EXT_OUT_PORT;
+       case OMAP_ABE_BE_PORT_MM_EXT_UL:
+               return OMAP_ABE_MM_EXT_IN_PORT;
+       /* front end ports */
+       case OMAP_ABE_FE_PORT_MM_DL1:
+       case OMAP_ABE_FE_PORT_MM_DL_LP:
+               return OMAP_ABE_MM_DL_PORT;
+       case OMAP_ABE_FE_PORT_MM_UL1:
+               return OMAP_ABE_MM_UL_PORT;
+       case OMAP_ABE_FE_PORT_MM_UL2:
+               return OMAP_ABE_MM_UL2_PORT;
+       case OMAP_ABE_FE_PORT_VX_DL:
+               return OMAP_ABE_VX_DL_PORT;
+       case OMAP_ABE_FE_PORT_VX_UL:
+               return OMAP_ABE_VX_UL_PORT;
+       case OMAP_ABE_FE_PORT_TONES:
+               return OMAP_ABE_TONES_DL_PORT;
+       }
+       return -EINVAL;
+}
+
+/*
+ * Get the number of enabled users of the physical port shared by this client.
+ * Locks held by callers.
+ */
+static int port_get_num_users(struct omap_aess *abe, struct omap_abe_port *port)
+{
+       struct omap_abe_port *p;
+       int users = 0;
+
+       list_for_each_entry(p, &abe->ports, list) {
+               if (p->physical_id == port->physical_id && p->state == PORT_ENABLED)
+                       users++;
+       }
+       return users;
+}
+
+static int port_is_open(struct omap_aess *abe, int phy_port)
+{
+       struct omap_abe_port *p;
+
+       list_for_each_entry(p, &abe->ports, list) {
+               if (p->physical_id == phy_port && p->state == PORT_ENABLED)
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * Check whether the physical port is enabled for this PHY port ID.
+ * Locks held by callers.
+ */
+int omap_abe_port_is_enabled(struct omap_aess *abe, struct omap_abe_port *port)
+{
+       struct omap_abe_port *p;
+       unsigned long flags;
+
+       spin_lock_irqsave(&abe->lock, flags);
+
+       list_for_each_entry(p, &abe->ports, list) {
+               if (p->physical_id == port->physical_id && p->state == PORT_ENABLED) {
+                       spin_unlock_irqrestore(&abe->lock, flags);
+                       return 1;
+               }
+       }
+
+       spin_unlock_irqrestore(&abe->lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(omap_abe_port_is_enabled);
+
+/*
+ * omap_abe_port_enable - enable ABE logical port
+ *
+ * @abe -  ABE.
+ * @port - logical ABE port ID to be enabled.
+ */
+int omap_abe_port_enable(struct omap_aess *abe, struct omap_abe_port *port)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       /* only enable the physical port iff it is disabled */
+       pr_debug("port %s increment count %d\n",
+                lport_name[port->logical_id], port->users);
+
+       spin_lock_irqsave(&abe->lock, flags);
+       if (port->users == 0 && port_get_num_users(abe, port) == 0) {
+
+               /* enable the physical port */
+               pr_debug("port %s phy port %d enabled\n",
+                        lport_name[port->logical_id], port->physical_id);
+               omap_aess_enable_data_transfer(abe, port->physical_id);
+       }
+
+       port->state = PORT_ENABLED;
+       port->users++;
+       spin_unlock_irqrestore(&abe->lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL(omap_abe_port_enable);
+
+/*
+ * omap_abe_port_disable - disable ABE logical port
+ *
+ * @abe -  ABE.
+ * @port - logical ABE port ID to be disabled.
+ */
+int omap_abe_port_disable(struct omap_aess *abe, struct omap_abe_port *port)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       /* only disable the port iff no other users are using it */
+       pr_debug("port %s decrement count %d\n",
+                lport_name[port->logical_id], port->users);
+
+       spin_lock_irqsave(&abe->lock, flags);
+
+       if (port->users == 1 && port_get_num_users(abe, port) == 1) {
+               /* disable the physical port */
+               pr_debug("port %s phy port %d disabled\n",
+                        lport_name[port->logical_id], port->physical_id);
+
+               omap_aess_disable_data_transfer(abe, port->physical_id);
+       }
+
+       port->state = PORT_DISABLED;
+       port->users--;
+       spin_unlock_irqrestore(&abe->lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL(omap_abe_port_disable);
+
+/*
+ * omap_abe_port_open - open ABE logical port
+ *
+ * @abe -  ABE.
+ * @logical_id - logical ABE port ID to be opened.
+ */
+struct omap_abe_port *omap_abe_port_open(struct omap_aess *abe, int logical_id)
+{
+       struct omap_abe_port *port;
+       unsigned long flags;
+
+#ifdef CONFIG_DEBUG_FS
+       char debug_fs_name[32];
+#endif
+
+       if (logical_id < 0 || logical_id > OMAP_ABE_MAX_PORT_ID) {
+               pr_err("invalid logical port %d\n", logical_id);
+               return NULL;
+       }
+
+       if (port_is_open(abe, logical_id)) {
+               pr_err("logical port %d already open\n", logical_id);
+               return NULL;
+       }
+
+       port = kzalloc(sizeof(struct omap_abe_port), GFP_KERNEL);
+       if (port == NULL)
+               return NULL;
+
+       port->logical_id = logical_id;
+       port->physical_id = get_physical_id(logical_id);
+       port->state = PORT_DISABLED;
+       port->abe = abe;
+
+       spin_lock_irqsave(&abe->lock, flags);
+       list_add(&port->list, &abe->ports);
+       spin_unlock_irqrestore(&abe->lock, flags);
+       port->physical_users = port_get_num_users(abe, port);
+
+#ifdef CONFIG_DEBUG_FS
+       sprintf(debug_fs_name, "%s_state", lport_name[logical_id]);
+       port->debugfs_lstate = debugfs_create_u32(debug_fs_name, 0644,
+                       abe->debugfs_root, &port->state);
+       sprintf(debug_fs_name, "%s_phy", lport_name[logical_id]);
+       port->debugfs_lphy = debugfs_create_u32(debug_fs_name, 0644,
+                       abe->debugfs_root, &port->physical_id);
+       sprintf(debug_fs_name, "%s_users", lport_name[logical_id]);
+       port->debugfs_lusers = debugfs_create_u32(debug_fs_name, 0644,
+                       abe->debugfs_root, &port->users);
+#endif
+
+       pr_debug("opened port %s\n", lport_name[logical_id]);
+       return port;
+}
+EXPORT_SYMBOL(omap_abe_port_open);
+
+/*
+ * omap_abe_port_close - close ABE logical port
+ *
+ * @port - logical ABE port to be closed (and disabled).
+ */
+void omap_abe_port_close(struct omap_aess *abe, struct omap_abe_port *port)
+{
+       unsigned long flags;
+
+       /* disable the port */
+       omap_abe_port_disable(abe, port);
+
+       spin_lock_irqsave(&abe->lock, flags);
+       list_del(&port->list);
+       spin_unlock_irqrestore(&abe->lock, flags);
+
+       pr_debug("closed port %s\n", lport_name[port->logical_id]);
+       kfree(port);
+}
+EXPORT_SYMBOL(omap_abe_port_close);
+
+static struct omap_aess *omap_abe_port_mgr_init(void)
+{
+       struct omap_aess *abe;
+
+       abe = kzalloc(sizeof(struct omap_aess), GFP_KERNEL);
+       if (abe == NULL)
+               return NULL;
+
+       spin_lock_init(&abe->lock);
+
+       INIT_LIST_HEAD(&abe->ports);
+       the_abe = abe;
+
+#ifdef CONFIG_DEBUG_FS
+       abe->debugfs_root = debugfs_create_dir("abe_port", NULL);
+       if (!abe->debugfs_root)
+               pr_debug("Failed to create port manager debugfs directory\n");
+#endif
+       return abe;
+}
+
+static void omap_abe_port_mgr_free(struct omap_aess *abe)
+{
+#ifdef CONFIG_DEBUG_FS
+       debugfs_remove_recursive(abe->debugfs_root);
+#endif
+       kfree(abe);
+       the_abe = NULL;
+}
+
+struct omap_aess *omap_abe_port_mgr_get(void)
+{
+       struct omap_aess *abe;
+
+       mutex_lock(&port_mgr_mutex);
+
+       if (the_abe)
+               abe = the_abe;
+       else
+               abe = omap_abe_port_mgr_init();
+
+       users++;
+       mutex_unlock(&port_mgr_mutex);
+       return abe;
+}
+EXPORT_SYMBOL(omap_abe_port_mgr_get);
+
+void omap_abe_port_mgr_put(struct omap_aess *abe)
+{
+       mutex_lock(&port_mgr_mutex);
+
+       if (users == 0)
+               goto out;
+
+       if (--users == 0)
+               omap_abe_port_mgr_free(abe);
+
+out:
+       mutex_unlock(&port_mgr_mutex);
+}
+EXPORT_SYMBOL(omap_abe_port_mgr_put);
+
+MODULE_LICENSE("GPL");
index 285c8368cb47bcaf3461b4470f09fb9d7159c0fc..71105d8e0b8843e8435eec52d358846a59ed33bd 100644 (file)
@@ -448,6 +448,7 @@ void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
        if (threshold && threshold <= mcbsp->max_tx_thres)
                MCBSP_WRITE(mcbsp, THRSH2, threshold - 1);
 }
+EXPORT_SYMBOL_GPL(omap_mcbsp_set_tx_threshold);
 
 /*
  * omap_mcbsp_set_rx_threshold configures the receive threshold in words.
@@ -462,6 +463,7 @@ void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
        if (threshold && threshold <= mcbsp->max_rx_thres)
                MCBSP_WRITE(mcbsp, THRSH1, threshold - 1);
 }
+EXPORT_SYMBOL_GPL(omap_mcbsp_set_rx_threshold);
 
 /*
  * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
diff --git a/sound/soc/omap/omap-abe-core.c b/sound/soc/omap/omap-abe-core.c
new file mode 100644 (file)
index 0000000..91b6e84
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * omap-abe.c  --  OMAP ALSA SoC DAI driver using Audio Backend
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Contact: Liam Girdwood <lrg@ti.com>
+ *          Misael Lopez Cruz <misael.lopez@ti.com>
+ *          Sebastien Guiriec <s-guiriec@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/firmware.h>
+#include <linux/debugfs.h>
+#include <linux/opp.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_device.h>
+
+#include <sound/soc.h>
+#include <sound/soc-fw.h>
+#include <plat/cpu.h>
+#include "../../../arch/arm/mach-omap2/omap-pm.h"
+
+#include "omap-abe-priv.h"
+
+int abe_opp_stream_event(struct snd_soc_dapm_context *dapm, int event);
+int abe_pm_suspend(struct snd_soc_dai *dai);
+int abe_pm_resume(struct snd_soc_dai *dai);
+
+int abe_mixer_write(struct snd_soc_platform *platform, unsigned int reg,
+               unsigned int val);
+unsigned int abe_mixer_read(struct snd_soc_platform *platform,
+               unsigned int reg);
+irqreturn_t abe_irq_handler(int irq, void *dev_id);
+void abe_init_debugfs(struct omap_abe *abe);
+void abe_cleanup_debugfs(struct omap_abe *abe);
+int abe_opp_init_initial_opp(struct omap_abe *abe);
+extern struct snd_pcm_ops omap_aess_pcm_ops;
+extern struct snd_soc_dai_driver omap_abe_dai[6];
+
+static u64 omap_abe_dmamask = DMA_BIT_MASK(32);
+
+static const char *abe_memory_bank[5] = {
+       "dmem",
+       "cmem",
+       "smem",
+       "pmem",
+       "mpu"
+};
+
+static void abe_init_gains(struct omap_aess *abe)
+{
+       /* Uplink gains */
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXAUDUL_MM_DL);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXAUDUL_TONES);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXAUDUL_UPLINK);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXAUDUL_VX_DL);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXVXREC_TONES);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXVXREC_VX_DL);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXVXREC_MM_DL);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXVXREC_VX_UL);
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_DMIC1_LEFT);
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_DMIC1_RIGHT);
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_DMIC2_LEFT);
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_DMIC2_RIGHT);
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_DMIC3_LEFT);
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_DMIC3_RIGHT);
+
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_AMIC_LEFT);
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_AMIC_RIGHT);
+
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_BTUL_LEFT);
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_BTUL_RIGHT);
+
+       /* Downlink gains */
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DL1_LEFT, GAIN_0dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DL1_RIGHT, GAIN_0dB);
+       /*SEBG: Ramp RAMP_2MS */
+
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_DL1_LEFT);
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_DL1_RIGHT);
+
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DL2_LEFT, GAIN_M7dB);
+       omap_aess_write_gain(abe, OMAP_AESS_GAIN_DL2_RIGHT, GAIN_M7dB);
+       /*SEBG: Ramp RAMP_2MS */
+
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_DL2_LEFT);
+       omap_aess_mute_gain(abe, OMAP_AESS_GAIN_DL2_RIGHT);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXDL1_MM_DL);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXDL1_MM_UL2);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXDL1_VX_DL);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXDL1_TONES);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXDL2_TONES);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXDL2_VX_DL);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXDL2_MM_DL);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXDL2_MM_UL2);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXECHO_DL1);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXECHO_DL2);
+
+       /* Sidetone gains */
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXSDT_UL);
+       omap_aess_mute_gain(abe, OMAP_AESS_MIXSDT_DL);
+}
+
+static int abe_load_coeffs(struct snd_soc_platform *platform,
+       struct snd_soc_fw_hdr *hdr)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       const struct snd_soc_file_coeff_data *cd = snd_soc_fw_get_data(hdr);
+       const void *coeff_data = cd + 1;
+
+       dev_dbg(platform->dev,"coeff %d size 0x%x with %d elems\n",
+               cd->id, cd->size, cd->count);
+
+       switch (cd->id) {
+       case OMAP_AESS_CMEM_DL1_COEFS_ID:
+               abe->equ.dl1.profile_size = cd->size / cd->count;
+               abe->equ.dl1.num_profiles = cd->count;
+               abe->equ.dl1.coeff_data = kmalloc(cd->size, GFP_KERNEL);
+               if (abe->equ.dl1.coeff_data == NULL)
+                       return -ENOMEM;
+               memcpy(abe->equ.dl1.coeff_data, coeff_data, cd->size);
+               break;
+       case OMAP_AESS_CMEM_DL2_L_COEFS_ID:
+               abe->equ.dl2l.profile_size = cd->size / cd->count;
+               abe->equ.dl2l.num_profiles = cd->count;
+               abe->equ.dl2l.coeff_data = kmalloc(cd->size, GFP_KERNEL);
+               if (abe->equ.dl2l.coeff_data == NULL)
+                       return -ENOMEM;
+               memcpy(abe->equ.dl2l.coeff_data, coeff_data, cd->size);
+               break;
+       case OMAP_AESS_CMEM_DL2_R_COEFS_ID:
+               abe->equ.dl2r.profile_size = cd->size / cd->count;
+               abe->equ.dl2r.num_profiles = cd->count;
+               abe->equ.dl2r.coeff_data = kmalloc(cd->size, GFP_KERNEL);
+               if (abe->equ.dl2r.coeff_data == NULL)
+                       return -ENOMEM;
+               memcpy(abe->equ.dl2r.coeff_data, coeff_data, cd->size);
+               break;
+       case OMAP_AESS_CMEM_SDT_COEFS_ID:
+               abe->equ.sdt.profile_size = cd->size / cd->count;
+               abe->equ.sdt.num_profiles = cd->count;
+               abe->equ.sdt.coeff_data = kmalloc(cd->size, GFP_KERNEL);
+               if (abe->equ.sdt.coeff_data == NULL)
+                       return -ENOMEM;
+               memcpy(abe->equ.sdt.coeff_data, coeff_data, cd->size);
+               break;
+       case OMAP_AESS_CMEM_96_48_AMIC_COEFS_ID:
+               abe->equ.amic.profile_size = cd->size / cd->count;
+               abe->equ.amic.num_profiles = cd->count;
+               abe->equ.amic.coeff_data = kmalloc(cd->size, GFP_KERNEL);
+               if (abe->equ.amic.coeff_data == NULL)
+                       return -ENOMEM;
+               memcpy(abe->equ.amic.coeff_data, coeff_data, cd->size);
+               break;
+       case OMAP_AESS_CMEM_96_48_DMIC_COEFS_ID:
+               abe->equ.dmic.profile_size = cd->size / cd->count;
+               abe->equ.dmic.num_profiles = cd->count;
+               abe->equ.dmic.coeff_data = kmalloc(cd->size, GFP_KERNEL);
+               if (abe->equ.dmic.coeff_data == NULL)
+                       return -ENOMEM;
+               memcpy(abe->equ.dmic.coeff_data, coeff_data, cd->size);
+               break;
+       default:
+               dev_err(platform->dev, "invalid coefficient ID %d\n", cd->id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int abe_load_fw(struct snd_soc_platform *platform,
+       struct snd_soc_fw_hdr *hdr)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       const u8 *fw_data = snd_soc_fw_get_data(hdr);
+
+       /* get firmware and coefficients header info */
+       memcpy(&abe->hdr, fw_data, sizeof(struct fw_header));
+       if (hdr->size > OMAP_ABE_MAX_FW_SIZE) {
+               dev_err(abe->dev, "Firmware too large at %d bytes\n",
+                       hdr->size);
+               return -ENOMEM;
+       }
+       dev_info(abe->dev, "ABE firmware size %d bytes\n", hdr->size);
+       dev_info(abe->dev, "ABE mem P %d C %d D %d S %d bytes\n",
+               abe->hdr.pmem_size, abe->hdr.cmem_size,
+               abe->hdr.dmem_size, abe->hdr.smem_size);
+
+       dev_info(abe->dev, "ABE Firmware version %x\n", abe->hdr.version);
+#if 0
+       if (omap_abe_get_supported_fw_version() <= abe->hdr.firmware_version) {
+               dev_err(abe->dev, "firmware version too old. Need %x have %x\n",
+                       omap_abe_get_supported_fw_version(),
+                       abe->hdr.firmware_version);
+               return -EINVAL;
+       }
+#endif
+       /* store ABE firmware for later context restore */
+       abe->fw_text = kzalloc(hdr->size, GFP_KERNEL);
+       if (abe->fw_text == NULL)
+               return -ENOMEM;
+
+       memcpy(abe->fw_text, fw_data, hdr->size);
+
+       return 0;
+}
+
+static int abe_load_config(struct snd_soc_platform *platform,
+       struct snd_soc_fw_hdr *hdr)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       const u8 *fw_data = snd_soc_fw_get_data(hdr);
+
+       /* store ABE config for later context restore */
+       abe->fw_config = kzalloc(hdr->size, GFP_KERNEL);
+       if (abe->fw_config == NULL)
+               return -ENOMEM;
+
+       dev_info(abe->dev, "ABE Config size %d bytes\n", hdr->size);
+
+       memcpy(abe->fw_config, fw_data, hdr->size);
+
+       return 0;
+}
+
+static void abe_free_fw(struct omap_abe *abe)
+{
+       kfree(abe->fw_text);
+       kfree(abe->fw_config);
+
+       /* This below should be done in HAL  - oposite of init_mem()*/
+       if (!abe->aess)
+               return;
+
+       if (abe->aess->fw_info) {
+               kfree(abe->aess->fw_info->init_table);
+               kfree(abe->aess->fw_info);
+       }
+}
+
+/* callback to handle vendor data */
+static int abe_vendor_load(struct snd_soc_platform *platform,
+       struct snd_soc_fw_hdr *hdr)
+{
+
+       switch (hdr->type) {
+       case SND_SOC_FW_VENDOR_FW:
+               return abe_load_fw(platform, hdr);
+       case SND_SOC_FW_VENDOR_CONFIG:
+               return abe_load_config(platform, hdr);
+       case SND_SOC_FW_COEFF:
+               return abe_load_coeffs(platform, hdr);
+       case SND_SOC_FW_VENDOR_CODEC:
+       default:
+               dev_err(platform->dev, "vendor type %d:%d not supported\n",
+                       hdr->type, hdr->vendor_type);
+               return 0;
+       }
+       return 0;
+}
+
+static struct snd_soc_fw_platform_ops soc_fw_ops = {
+       .vendor_load    = abe_vendor_load,
+       .io_ops         = abe_ops,
+       .io_ops_count   = 7,
+};
+
+static int abe_probe(struct snd_soc_platform *platform)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       int ret = 0, i;
+
+       pm_runtime_enable(abe->dev);
+       pm_runtime_irq_safe(abe->dev);
+
+       ret = snd_soc_fw_load_platform(platform, &soc_fw_ops, abe->fw, 0);
+       if (ret < 0) {
+               dev_err(platform->dev, "request for ABE FW failed %d\n", ret);
+               goto err_fw;
+       }
+
+       ret = request_threaded_irq(abe->irq, NULL, abe_irq_handler,
+                               IRQF_ONESHOT, "ABE", (void *)abe);
+       if (ret) {
+               dev_err(platform->dev, "request for ABE IRQ %d failed %d\n",
+                               abe->irq, ret);
+               goto err_irq;
+       }
+
+       ret = abe_opp_init_initial_opp(abe);
+       if (ret < 0) {
+               dev_info(platform->dev, "No OPP definition\n");
+               ret = 0;
+       }
+       /* aess_clk has to be enabled to access hal register.
+        * Disable the clk after it has been used.
+        */
+       pm_runtime_get_sync(abe->dev);
+
+       /* lrg - rework for better init flow */
+       abe->aess = omap_abe_port_mgr_get();
+       omap_aess_init_mem(abe->aess, abe->dev, abe->io_base, abe->fw_config);
+
+       omap_aess_reset_hal(abe->aess);
+
+       /* ZERO_labelID should really be 0 */
+       for (i = 0; i < OMAP_ABE_ROUTES_UL + 2; i++)
+               abe->mixer.route_ul[i] = abe->aess->fw_info->label_id[OMAP_AESS_BUFFER_ZERO_ID];
+
+       omap_aess_load_fw(abe->aess, abe->fw_text);
+
+       /* "tick" of the audio engine */
+       omap_aess_write_event_generator(abe->aess, EVENT_TIMER);
+       abe_init_gains(abe->aess);
+
+       /* Stop the engine */
+       omap_aess_stop_event_generator(abe->aess);
+       omap_aess_disable_irq(abe->aess);
+
+       pm_runtime_put_sync(abe->dev);
+       abe_init_debugfs(abe);
+
+       return ret;
+
+err_opp:
+       free_irq(abe->irq, (void *)abe);
+err_irq:
+       abe_free_fw(abe);
+err_fw:
+       pm_runtime_disable(abe->dev);
+       return ret;
+}
+
+static int abe_remove(struct snd_soc_platform *platform)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+
+       abe_cleanup_debugfs(abe);
+       free_irq(abe->irq, (void *)abe);
+       abe_free_fw(abe);
+       pm_runtime_disable(abe->dev);
+
+       return 0;
+}
+
+static struct snd_soc_platform_driver omap_aess_platform = {
+       .ops            = &omap_aess_pcm_ops,
+       .probe          = abe_probe,
+       .remove         = abe_remove,
+       .suspend        = abe_pm_suspend,
+       .resume         = abe_pm_resume,
+       .read           = abe_mixer_read,
+       .write          = abe_mixer_write,
+       .stream_event   = abe_opp_stream_event,
+};
+
+void driver_deferred_probe_trigger(void);
+
+static void abe_fw_ready(const struct firmware *fw, void *context)
+{
+       struct platform_device *pdev = (struct platform_device *)context;
+       struct omap_abe *abe = dev_get_drvdata(&pdev->dev);
+       int err;
+
+       abe->fw = fw;
+
+       err = snd_soc_register_platform(&pdev->dev, &omap_aess_platform);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to register ABE platform %d\n", err);
+               release_firmware(fw);
+               return;
+       }
+
+       err = snd_soc_register_dais(&pdev->dev, omap_abe_dai,
+                       ARRAY_SIZE(omap_abe_dai));
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to register ABE DAIs %d\n", err);
+               snd_soc_unregister_platform(&pdev->dev);
+               release_firmware(fw);
+       }
+       driver_deferred_probe_trigger();
+
+}
+
+static int abe_engine_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct omap_abe *abe;
+       int ret = -EINVAL, i;
+
+       abe = devm_kzalloc(&pdev->dev, sizeof(struct omap_abe), GFP_KERNEL);
+       if (abe == NULL)
+               return -ENOMEM;
+       dev_set_drvdata(&pdev->dev, abe);
+
+       for (i = 0; i < OMAP_ABE_IO_RESOURCES; i++) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  abe_memory_bank[i]);
+               if (res == NULL) {
+                       dev_err(&pdev->dev, "no resource %s\n",
+                               abe_memory_bank[i]);
+                       goto err;
+               }
+               abe->io_base[i] = ioremap(res->start, resource_size(res));
+               if (!abe->io_base[i]) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+       }
+
+       abe->irq = platform_get_irq(pdev, 0);
+       if (abe->irq < 0) {
+               ret = abe->irq;
+               goto err;
+       }
+
+#ifdef CONFIG_PM
+       abe->get_context_lost_count = omap_pm_get_dev_context_loss_count;
+       abe->device_scale = NULL;
+#endif
+       abe->dev = &pdev->dev;
+       mutex_init(&abe->mutex);
+       mutex_init(&abe->opp.mutex);
+       mutex_init(&abe->opp.req_mutex);
+       INIT_LIST_HEAD(&abe->opp.req);
+
+       get_device(abe->dev);
+       abe->dev->dma_mask = &omap_abe_dmamask;
+       abe->dev->coherent_dma_mask = omap_abe_dmamask;
+       put_device(abe->dev);
+
+       ret = request_firmware_nowait(THIS_MODULE, 1, "omap4_abe_new", abe->dev,
+               GFP_KERNEL, pdev, abe_fw_ready);
+       if (ret != 0) {
+               dev_err(abe->dev, "Failed to load firmware %d\n", ret);
+               goto err;
+       }
+
+       return ret;
+
+err:
+       for (--i; i >= 0; i--)
+               iounmap(abe->io_base[i]);
+
+       return ret;
+}
+
+static int abe_engine_remove(struct platform_device *pdev)
+{
+       struct omap_abe *abe = dev_get_drvdata(&pdev->dev);
+       int i;
+
+       snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(omap_abe_dai));
+       snd_soc_unregister_platform(&pdev->dev);
+       release_firmware(abe->fw);
+       for (i = 0; i < OMAP_ABE_IO_RESOURCES; i++)
+               iounmap(abe->io_base[i]);
+
+       return 0;
+}
+
+static const struct of_device_id omap_aess_of_match[] = {
+       { .compatible = "ti,omap4-aess", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, omap_aess_of_match);
+
+static struct platform_driver omap_aess_driver = {
+       .driver = {
+               .name = "aess",
+               .owner = THIS_MODULE,
+               .of_match_table = omap_aess_of_match,
+       },
+       .probe = abe_engine_probe,
+       .remove = abe_engine_remove,
+};
+
+module_platform_driver(omap_aess_driver);
+
+MODULE_ALIAS("platform:omap-aess");
+MODULE_DESCRIPTION("ASoC OMAP4 ABE");
+MODULE_AUTHOR("Liam Girdwood <lrg@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-abe-dbg.c b/sound/soc/omap/omap-abe-dbg.c
new file mode 100644 (file)
index 0000000..a9b8d91
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ * omap-abe-dbg.c  --  OMAP ALSA SoC DAI driver using Audio Backend
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Contact: Liam Girdwood <lrg@ti.com>
+ *          Misael Lopez Cruz <misael.lopez@ti.com>
+ *          Sebastien Guiriec <s-guiriec@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+
+#include <linux/omap-dma.h>
+#define OMAP44XX_DMA_ABE_REQ_7                 108
+
+#include <sound/soc.h>
+
+#include "omap-abe-priv.h"
+
+/* TODO: size in bytes of debug options */
+#define OMAP_ABE_DBG_FLAG1_SIZE        0
+#define OMAP_ABE_DBG_FLAG2_SIZE        0
+#define OMAP_ABE_DBG_FLAG3_SIZE        0
+
+#ifdef CONFIG_DEBUG_FS
+
+static int abe_dbg_get_dma_pos(struct omap_abe *abe)
+{
+       return omap_get_dma_dst_pos(abe->debugfs.dma_ch) - abe->debugfs.buffer_addr;
+}
+
+static void abe_dbg_dma_irq(int ch, u16 stat, void *data)
+{
+}
+
+static int abe_dbg_start_dma(struct omap_abe *abe, int circular)
+{
+       struct omap_dma_channel_params dma_params;
+       int err;
+
+       /* start the DMA in either :-
+        *
+        * 1) circular buffer mode where the DMA will restart when it get to
+        *    the end of the buffer.
+        * 2) default mode, where DMA stops at the end of the buffer.
+        */
+
+       abe->debugfs.dma_req = OMAP44XX_DMA_ABE_REQ_7;
+       err = omap_request_dma(abe->debugfs.dma_req, "ABE debug",
+                              abe_dbg_dma_irq, abe, &abe->debugfs.dma_ch);
+       if (abe->debugfs.circular) {
+               /*
+                * Link channel with itself so DMA doesn't need any
+                * reprogramming while looping the buffer
+                */
+               omap_dma_link_lch(abe->debugfs.dma_ch, abe->debugfs.dma_ch);
+       }
+
+       memset(&dma_params, 0, sizeof(dma_params));
+       dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
+       dma_params.trigger = abe->debugfs.dma_req;
+       dma_params.sync_mode = OMAP_DMA_SYNC_FRAME;
+       dma_params.src_amode = OMAP_DMA_AMODE_DOUBLE_IDX;
+       dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
+       dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
+       dma_params.src_start = abe->aess->fw_info->map[OMAP_AESS_DMEM_DEBUG_FIFO_ID].offset + ABE_DEFAULT_BASE_ADDRESS_L3 + ABE_DMEM_BASE_OFFSET_MPU;
+       dma_params.dst_start = abe->debugfs.buffer_addr;
+       dma_params.src_port = OMAP_DMA_PORT_MPUI;
+       dma_params.src_ei = 1;
+       dma_params.src_fi = 1 - abe->debugfs.elem_bytes;
+
+        /* 128 bytes shifted into words */
+       dma_params.elem_count = abe->debugfs.elem_bytes >> 2;
+       dma_params.frame_count =
+                       abe->debugfs.buffer_bytes / abe->debugfs.elem_bytes;
+       omap_set_dma_params(abe->debugfs.dma_ch, &dma_params);
+
+       omap_enable_dma_irq(abe->debugfs.dma_ch, OMAP_DMA_FRAME_IRQ);
+       omap_set_dma_src_burst_mode(abe->debugfs.dma_ch, OMAP_DMA_DATA_BURST_16);
+       omap_set_dma_dest_burst_mode(abe->debugfs.dma_ch, OMAP_DMA_DATA_BURST_16);
+
+       abe->debugfs.reader_offset = 0;
+
+       pm_runtime_get_sync(abe->dev);
+       omap_start_dma(abe->debugfs.dma_ch);
+       return 0;
+}
+
+static void abe_dbg_stop_dma(struct omap_abe *abe)
+{
+       /* Since we are using self linking, there is a
+       chance that the DMA as re-enabled the channel just after disabling it */
+       while (omap_get_dma_active_status(abe->debugfs.dma_ch))
+               omap_stop_dma(abe->debugfs.dma_ch);
+
+       if (abe->debugfs.circular)
+               omap_dma_unlink_lch(abe->debugfs.dma_ch, abe->debugfs.dma_ch);
+
+       omap_free_dma(abe->debugfs.dma_ch);
+       pm_runtime_put_sync(abe->dev);
+}
+
+static int abe_open_data(struct inode *inode, struct file *file)
+{
+       struct omap_abe *abe = inode->i_private;
+
+       /* adjust debug word size based on any user params */
+       if (abe->debugfs.format1)
+               abe->debugfs.elem_bytes += OMAP_ABE_DBG_FLAG1_SIZE;
+       if (abe->debugfs.format2)
+               abe->debugfs.elem_bytes += OMAP_ABE_DBG_FLAG2_SIZE;
+       if (abe->debugfs.format3)
+               abe->debugfs.elem_bytes += OMAP_ABE_DBG_FLAG3_SIZE;
+
+       abe->debugfs.buffer_bytes = abe->debugfs.elem_bytes * 4 *
+                                                       abe->debugfs.buffer_msecs;
+
+       abe->debugfs.buffer = dma_alloc_writecombine(abe->dev,
+                       abe->debugfs.buffer_bytes, &abe->debugfs.buffer_addr, GFP_KERNEL);
+       if (abe->debugfs.buffer == NULL) {
+               dev_err(abe->dev, "can't alloc %d bytes for trace DMA buffer\n",
+                               abe->debugfs.buffer_bytes);
+               return -ENOMEM;
+       }
+
+       file->private_data = inode->i_private;
+       abe->debugfs.complete = 0;
+       abe_dbg_start_dma(abe, abe->debugfs.circular);
+
+       return 0;
+}
+
+static int abe_release_data(struct inode *inode, struct file *file)
+{
+       struct omap_abe *abe = inode->i_private;
+
+       abe_dbg_stop_dma(abe);
+
+       dma_free_writecombine(abe->dev, abe->debugfs.buffer_bytes,
+                                     abe->debugfs.buffer, abe->debugfs.buffer_addr);
+       return 0;
+}
+
+static ssize_t abe_copy_to_user(struct omap_abe *abe, char __user *user_buf,
+                              size_t count)
+{
+       /* check for reader buffer wrap */
+       if (abe->debugfs.reader_offset + count > abe->debugfs.buffer_bytes) {
+               size_t size = abe->debugfs.buffer_bytes - abe->debugfs.reader_offset;
+
+               /* wrap */
+               if (copy_to_user(user_buf,
+                       abe->debugfs.buffer + abe->debugfs.reader_offset, size))
+                       return -EFAULT;
+
+               /* need to just return if non circular */
+               if (!abe->debugfs.circular) {
+                       abe->debugfs.complete = 1;
+                       return count;
+               }
+
+               if (copy_to_user(user_buf + size,
+                       abe->debugfs.buffer, count - size))
+                       return -EFAULT;
+               abe->debugfs.reader_offset = count - size;
+               return count;
+       } else {
+               /* no wrap */
+               if (copy_to_user(user_buf,
+                       abe->debugfs.buffer + abe->debugfs.reader_offset, count))
+                       return -EFAULT;
+               abe->debugfs.reader_offset += count;
+
+               if (!abe->debugfs.circular &&
+                               abe->debugfs.reader_offset == abe->debugfs.buffer_bytes)
+                       abe->debugfs.complete = 1;
+
+               return count;
+       }
+}
+
+static ssize_t abe_read_data(struct file *file, char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       ssize_t ret = 0;
+       struct omap_abe *abe = file->private_data;
+       DECLARE_WAITQUEUE(wait, current);
+       int dma_offset, bytes;
+
+       add_wait_queue(&abe->debugfs.wait, &wait);
+       do {
+               set_current_state(TASK_INTERRUPTIBLE);
+               /* TODO: Check if really needed. Or adjust sleep delay
+                * If not delay trace is not working */
+               msleep_interruptible(1);
+
+               /* is DMA finished ? */
+               if (abe->debugfs.complete)
+                       break;
+
+               dma_offset = abe_dbg_get_dma_pos(abe);
+
+
+               /* get maximum amount of debug bytes we can read */
+               if (dma_offset >= abe->debugfs.reader_offset) {
+                       /* dma ptr is ahead of reader */
+                       bytes = dma_offset - abe->debugfs.reader_offset;
+               } else {
+                       /* dma ptr is behind reader */
+                       bytes = dma_offset + abe->debugfs.buffer_bytes -
+                               abe->debugfs.reader_offset;
+               }
+
+               if (count > bytes)
+                       count = bytes;
+
+               if (count > 0) {
+                       ret = abe_copy_to_user(abe, user_buf, count);
+                       break;
+               }
+
+               if (file->f_flags & O_NONBLOCK) {
+                       ret = -EAGAIN;
+                       break;
+               }
+
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+
+               schedule();
+
+       } while (1);
+
+       __set_current_state(TASK_RUNNING);
+       remove_wait_queue(&abe->debugfs.wait, &wait);
+
+       return ret;
+}
+
+static const struct file_operations omap_abe_fops = {
+       .open = abe_open_data,
+       .read = abe_read_data,
+       .release = abe_release_data,
+};
+
+
+static int abe_open_mem(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t abe_read_mem(struct file *file, char __user *user_buf,
+                              size_t count, loff_t *ppos, void *mem, int size)
+{
+       struct omap_abe *abe = file->private_data;
+       ssize_t ret = 0;
+
+       pm_runtime_get_sync(abe->dev);
+       set_current_state(TASK_INTERRUPTIBLE);
+
+       if (*ppos >= size)
+               goto out;
+
+       if (*ppos + count > size)
+               count = size - *ppos;
+
+       if (copy_to_user(user_buf, mem + *ppos, count)) {
+               ret = -EFAULT;
+               goto out;
+       }
+       *ppos += count;
+       ret = count;
+out:
+       __set_current_state(TASK_RUNNING);
+       pm_runtime_put_sync(abe->dev);
+       return ret;
+}
+
+loff_t abe_llseek(struct file *file, loff_t off, int whence, int size)
+{
+       loff_t newpos;
+
+       switch (whence) {
+       case SEEK_SET:
+               newpos = off;
+               break;
+       case SEEK_CUR:
+               newpos = file->f_pos + off;
+               break;
+       case SEEK_END:
+               newpos = size;
+               break;
+       default: /* can't happen */
+               return -EINVAL;
+       }
+
+       if (newpos < 0)
+               return -EINVAL;
+
+       if (newpos > size)
+               newpos = size;
+
+       file->f_pos = newpos;
+       return newpos;
+}
+
+loff_t abe_llseek_cmem(struct file *file, loff_t off, int whence)
+{
+       struct omap_abe *abe = file->private_data;
+
+       return abe_llseek(file, off, whence, abe->hdr.cmem_size);
+}
+
+static ssize_t abe_read_cmem(struct file *file, char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       struct omap_abe *abe = file->private_data;
+
+       return abe_read_mem(file, user_buf, count, ppos,
+               abe->io_base[OMAP_ABE_IO_CMEM], abe->hdr.cmem_size);
+}
+
+static const struct file_operations omap_abe_cmem_fops = {
+       .open = abe_open_mem,
+       .read = abe_read_cmem,
+       .llseek = abe_llseek_cmem,
+};
+
+loff_t abe_llseek_pmem(struct file *file, loff_t off, int whence)
+{
+       struct omap_abe *abe = file->private_data;
+
+       return abe_llseek(file, off, whence, abe->hdr.pmem_size);
+}
+
+static ssize_t abe_read_pmem(struct file *file, char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       struct omap_abe *abe = file->private_data;
+
+       return abe_read_mem(file, user_buf, count, ppos,
+               abe->io_base[OMAP_ABE_IO_PMEM], abe->hdr.pmem_size);
+}
+
+static const struct file_operations omap_abe_pmem_fops = {
+       .open = abe_open_mem,
+       .read = abe_read_pmem,
+       .llseek = abe_llseek_pmem,
+};
+
+loff_t abe_llseek_smem(struct file *file, loff_t off, int whence)
+{
+       struct omap_abe *abe = file->private_data;
+
+       return abe_llseek(file, off, whence, abe->hdr.smem_size);
+}
+
+static ssize_t abe_read_smem(struct file *file, char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       struct omap_abe *abe = file->private_data;
+
+       return abe_read_mem(file, user_buf, count, ppos,
+               abe->io_base[OMAP_ABE_IO_SMEM], abe->hdr.smem_size);
+}
+
+static const struct file_operations omap_abe_smem_fops = {
+       .open = abe_open_mem,
+       .read = abe_read_smem,
+       .llseek = abe_llseek_smem,
+};
+
+loff_t abe_llseek_dmem(struct file *file, loff_t off, int whence)
+{
+       struct omap_abe *abe = file->private_data;
+
+       return abe_llseek(file, off, whence, abe->hdr.dmem_size);
+}
+
+static ssize_t abe_read_dmem(struct file *file, char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       struct omap_abe *abe = file->private_data;
+
+       return abe_read_mem(file, user_buf, count, ppos,
+               abe->io_base[OMAP_ABE_IO_DMEM], abe->hdr.dmem_size);
+}
+
+static const struct file_operations omap_abe_dmem_fops = {
+       .open = abe_open_mem,
+       .read = abe_read_dmem,
+       .llseek = abe_llseek_dmem,
+};
+
+void abe_init_debugfs(struct omap_abe *abe)
+{
+       abe->debugfs.d_root = debugfs_create_dir("omap-abe", NULL);
+       if (!abe->debugfs.d_root) {
+               dev_err(abe->dev, "Failed to create debugfs directory\n");
+               return;
+       }
+
+       abe->debugfs.d_fmt1 = debugfs_create_bool("format1", 0644,
+                                                abe->debugfs.d_root,
+                                                &abe->debugfs.format1);
+       if (!abe->debugfs.d_fmt1)
+               dev_err(abe->dev, "Failed to create format1 debugfs file\n");
+
+       abe->debugfs.d_fmt2 = debugfs_create_bool("format2", 0644,
+                                                abe->debugfs.d_root,
+                                                &abe->debugfs.format2);
+       if (!abe->debugfs.d_fmt2)
+               dev_err(abe->dev, "Failed to create format2 debugfs file\n");
+
+       abe->debugfs.d_fmt3 = debugfs_create_bool("format3", 0644,
+                                                abe->debugfs.d_root,
+                                                &abe->debugfs.format3);
+       if (!abe->debugfs.d_fmt3)
+               dev_err(abe->dev, "Failed to create format3 debugfs file\n");
+
+       abe->debugfs.d_elem_bytes = debugfs_create_u32("element_bytes", 0604,
+                                                abe->debugfs.d_root,
+                                                &abe->debugfs.elem_bytes);
+       if (!abe->debugfs.d_elem_bytes)
+               dev_err(abe->dev, "Failed to create element size debugfs file\n");
+
+       abe->debugfs.d_size = debugfs_create_u32("msecs", 0644,
+                                                abe->debugfs.d_root,
+                                                &abe->debugfs.buffer_msecs);
+       if (!abe->debugfs.d_size)
+               dev_err(abe->dev, "Failed to create buffer size debugfs file\n");
+
+       abe->debugfs.d_circ = debugfs_create_bool("circular", 0644,
+                                                abe->debugfs.d_root,
+                                                &abe->debugfs.circular);
+       if (!abe->debugfs.d_size)
+               dev_err(abe->dev, "Failed to create circular mode debugfs file\n");
+
+       abe->debugfs.d_data = debugfs_create_file("debug", 0644,
+                                                abe->debugfs.d_root,
+                                                abe, &omap_abe_fops);
+       if (!abe->debugfs.d_data)
+               dev_err(abe->dev, "Failed to create data debugfs file\n");
+
+       abe->debugfs.d_opp = debugfs_create_u32("opp_level", 0604,
+                                                abe->debugfs.d_root,
+                                                &abe->opp.level);
+       if (!abe->debugfs.d_opp)
+               dev_err(abe->dev, "Failed to create OPP level debugfs file\n");
+
+       abe->debugfs.d_pmem = debugfs_create_file("pmem", 0644,
+                                                abe->debugfs.d_root,
+                                                abe, &omap_abe_pmem_fops);
+       if (!abe->debugfs.d_pmem)
+               dev_err(abe->dev, "Failed to create PMEM debugfs file\n");
+
+       abe->debugfs.d_smem = debugfs_create_file("smem", 0644,
+                                                abe->debugfs.d_root,
+                                                abe, &omap_abe_smem_fops);
+       if (!abe->debugfs.d_smem)
+               dev_err(abe->dev, "Failed to create SMEM debugfs file\n");
+
+       abe->debugfs.d_dmem = debugfs_create_file("dmem", 0644,
+                                                abe->debugfs.d_root,
+                                                abe, &omap_abe_dmem_fops);
+       if (!abe->debugfs.d_dmem)
+               dev_err(abe->dev, "Failed to create DMEM debugfs file\n");
+
+       abe->debugfs.d_cmem = debugfs_create_file("cmem", 0644,
+                                                abe->debugfs.d_root,
+                                                abe, &omap_abe_cmem_fops);
+       if (!abe->debugfs.d_cmem)
+               dev_err(abe->dev, "Failed to create CMEM debugfs file\n");
+
+       init_waitqueue_head(&abe->debugfs.wait);
+}
+
+void abe_cleanup_debugfs(struct omap_abe *abe)
+{
+       debugfs_remove_recursive(abe->debugfs.d_root);
+}
+
+#else
+
+inline void abe_init_debugfs(struct omap_abe *abe)
+{
+}
+
+inline void abe_cleanup_debugfs(struct omap_abe *abe)
+{
+}
+#endif
diff --git a/sound/soc/omap/omap-abe-mixer.c b/sound/soc/omap/omap-abe-mixer.c
new file mode 100644 (file)
index 0000000..33abb0e
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+ * omap-abe.c  --  OMAP ALSA SoC DAI driver using Audio Backend
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Contact: Liam Girdwood <lrg@ti.com>
+ *          Misael Lopez Cruz <misael.lopez@ti.com>
+ *          Sebastien Guiriec <s-guiriec@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/opp.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/soc-fw.h>
+
+#include "omap-abe-priv.h"
+
+#define OMAP_ABE_HS_DC_OFFSET_STEP     (1800 / 8)
+#define OMAP_ABE_HF_DC_OFFSET_STEP     (4600 / 8)
+
+/* Gain value conversion */
+#define OMAP_ABE_MAX_GAIN              12000   /* 120dB */
+#define OMAP_ABE_GAIN_SCALE            100             /* 1dB */
+#define abe_gain_to_val(gain) \
+       ((val + OMAP_ABE_MAX_GAIN) / OMAP_ABE_GAIN_SCALE)
+#define abe_val_to_gain(val) \
+       (-OMAP_ABE_MAX_GAIN + (val * OMAP_ABE_GAIN_SCALE))
+
+/* TODO: map IO directly into ABE memories */
+unsigned int abe_mixer_read(struct snd_soc_platform *platform,
+               unsigned int reg)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+
+       if (reg > OMAP_ABE_NUM_DAPM_REG)
+               return 0;
+
+       dev_dbg(platform->dev, "read R%d (Ox%x) = 0x%x\n",
+                       reg, reg, abe->opp.widget[reg]);
+       return abe->opp.widget[reg];
+}
+
+int abe_mixer_write(struct snd_soc_platform *platform, unsigned int reg,
+               unsigned int val)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+
+       if (reg > OMAP_ABE_NUM_DAPM_REG)
+               return 0;
+
+       abe->opp.widget[reg] = val;
+       dev_dbg(platform->dev, "write R%d (Ox%x) = 0x%x\n", reg, reg, val);
+       return 0;
+}
+
+void omap_abe_dc_set_hs_offset(struct snd_soc_platform *platform,
+       int left, int right, int step_mV)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+
+       if (left >= 8)
+               left -= 16;
+       abe->dc_offset.hsl = OMAP_ABE_HS_DC_OFFSET_STEP * left * step_mV;
+
+       if (right >= 8)
+               right -= 16;
+       abe->dc_offset.hsr = OMAP_ABE_HS_DC_OFFSET_STEP * right * step_mV;
+}
+EXPORT_SYMBOL(omap_abe_dc_set_hs_offset);
+
+void omap_abe_dc_set_hf_offset(struct snd_soc_platform *platform,
+       int left, int right)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+
+       if (left >= 8)
+               left -= 16;
+       abe->dc_offset.hfl = OMAP_ABE_HF_DC_OFFSET_STEP * left;
+
+       if (right >= 8)
+               right -= 16;
+       abe->dc_offset.hfr = OMAP_ABE_HF_DC_OFFSET_STEP * right;
+}
+EXPORT_SYMBOL(omap_abe_dc_set_hf_offset);
+
+void omap_abe_set_dl1_gains(struct snd_soc_platform *platform,
+       int left, int right)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+
+       omap_aess_write_gain(abe->aess, OMAP_AESS_GAIN_DL1_LEFT, left);
+       omap_aess_write_gain(abe->aess, OMAP_AESS_GAIN_DL1_RIGHT, right);
+}
+EXPORT_SYMBOL(omap_abe_set_dl1_gains);
+
+
+/* TODO: we have to use the shift value atm to represent register
+ * id due to current HAL ID MACROS not being unique.
+ */
+static int abe_put_mixer(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_platform *platform = widget->platform;
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+
+       if (ucontrol->value.integer.value[0]) {
+               abe->opp.widget[mc->reg] |= ucontrol->value.integer.value[0]<<mc->shift;
+               snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+               omap_aess_enable_gain(abe->aess, mc->reg);
+       } else {
+               abe->opp.widget[mc->reg] &= ~(0x1<<mc->shift);
+               snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+               omap_aess_disable_gain(abe->aess, mc->reg);
+       }
+
+       return 1;
+}
+
+static int abe_get_mixer(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_platform *platform = widget->platform;
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+
+       ucontrol->value.integer.value[0] = (abe->opp.widget[mc->reg]>>mc->shift) & 0x1;
+
+       return 0;
+}
+
+int abe_mixer_enable_mono(struct omap_abe *abe, int id, int enable)
+{
+       int mixer;
+
+       switch (id) {
+       case MIX_DL1_MONO:
+               mixer = MIXDL1;
+               break;
+       case MIX_DL2_MONO:
+               mixer = MIXDL2;
+               break;
+       case MIX_AUDUL_MONO:
+               mixer = MIXAUDUL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       omap_aess_mono_mixer(abe->aess, mixer, enable);
+
+       return 0;
+}
+
+static int abe_put_mono_mixer(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       int id = mc->shift - MIX_DL1_MONO;
+
+       abe->mixer.mono[id] = ucontrol->value.integer.value[0];
+       abe_mixer_enable_mono(abe, mc->shift, abe->mixer.mono[id]);
+
+       return 1;
+}
+
+static int abe_get_mono_mixer(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       int id = mc->shift - MIX_DL1_MONO;
+
+       ucontrol->value.integer.value[0] = abe->mixer.mono[id];
+       return 0;
+}
+
+/* router IDs that match our mixer strings */
+static const int router[] = {
+               OMAP_AESS_BUFFER_ZERO_ID, /* strangely this is not 0 */
+               OMAP_AESS_BUFFER_DMIC1_L_ID,
+               OMAP_AESS_BUFFER_DMIC1_R_ID,
+               OMAP_AESS_BUFFER_DMIC2_L_ID,
+               OMAP_AESS_BUFFER_DMIC2_R_ID,
+               OMAP_AESS_BUFFER_DMIC3_L_ID,
+               OMAP_AESS_BUFFER_DMIC3_R_ID,
+               OMAP_AESS_BUFFER_BT_UL_L_ID,
+               OMAP_AESS_BUFFER_BT_UL_R_ID,
+               OMAP_AESS_BUFFER_MM_EXT_IN_L_ID,
+               OMAP_AESS_BUFFER_MM_EXT_IN_R_ID,
+               OMAP_AESS_BUFFER_AMIC_L_ID,
+               OMAP_AESS_BUFFER_AMIC_R_ID,
+               OMAP_AESS_BUFFER_VX_REC_L_ID,
+               OMAP_AESS_BUFFER_VX_REC_R_ID,
+};
+
+static int ul_mux_put_route(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_platform *platform = widget->platform;
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       int mux = ucontrol->value.enumerated.item[0];
+       int reg = e->reg - OMAP_ABE_MUX(0);
+
+       if (mux > OMAP_ABE_ROUTES_UL) {
+               pr_err("inavlid mux %d\n", mux);
+               return 0;
+       }
+
+       /* TODO: remove the gap */
+       if (reg < 6) {
+               /* 0  .. 5   = MM_UL */
+               abe->mixer.route_ul[reg] = abe->aess->fw_info->label_id[router[mux]];
+       } else if (reg < 10) {
+               /* 10 .. 11  = MM_UL2 */
+               /* 12 .. 13  = VX_UL */
+               abe->mixer.route_ul[reg + 4] = abe->aess->fw_info->label_id[router[mux]];
+       }
+
+       omap_aess_set_router_configuration(abe->aess, (u32 *)abe->mixer.route_ul);
+
+       if (router[mux] != OMAP_AESS_BUFFER_ZERO_ID)
+               abe->opp.widget[e->reg] = e->shift_l;
+       else
+               abe->opp.widget[e->reg] = 0;
+
+       snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+
+       return 1;
+}
+
+static int ul_mux_get_route(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_platform *platform = widget->platform;
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_enum *e =
+               (struct soc_enum *)kcontrol->private_value;
+       int reg = e->reg - OMAP_ABE_MUX(0), i, rval = 0;
+
+       /* TODO: remove the gap */
+       if (reg < 6) {
+               /* 0  .. 5   = MM_UL */
+               rval = abe->mixer.route_ul[reg];
+       } else if (reg < 10) {
+               /* 10 .. 11  = MM_UL2 */
+               /* 12 .. 13  = VX_UL */
+               rval = abe->mixer.route_ul[reg + 4];
+       }
+
+       for (i = 0; i < ARRAY_SIZE(router); i++) {
+               if (abe->aess->fw_info->label_id[router[i]] == rval) {
+                       ucontrol->value.integer.value[0] = i;
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+
+static int abe_put_switch(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_platform *platform = widget->platform;
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+
+       if (ucontrol->value.integer.value[0]) {
+               abe->opp.widget[mc->reg] |= ucontrol->value.integer.value[0]<<mc->shift;
+               snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+       } else {
+               abe->opp.widget[mc->reg] &= ~(0x1<<mc->shift);
+               snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+       }
+
+       return 1;
+}
+
+
+static int volume_put_mixer(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+
+       omap_aess_write_mixer(abe->aess, mc->reg, abe_val_to_gain(ucontrol->value.integer.value[0]));
+
+       return 1;
+}
+
+static int volume_put_gain(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+
+       omap_aess_write_gain(abe->aess, mc->shift,
+                      abe_val_to_gain(ucontrol->value.integer.value[0]));
+       omap_aess_write_gain(abe->aess, mc->rshift,
+                      abe_val_to_gain(ucontrol->value.integer.value[1]));
+
+       return 1;
+}
+
+static int volume_get_mixer(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       u32 val;
+
+       omap_aess_read_mixer(abe->aess, mc->reg, &val);
+       ucontrol->value.integer.value[0] = abe_gain_to_val(val);
+
+       return 0;
+}
+
+
+static int volume_get_gain(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       u32 val;
+
+       omap_aess_read_gain(abe->aess, mc->shift, &val);
+       ucontrol->value.integer.value[0] = abe_gain_to_val(val);
+       omap_aess_read_gain(abe->aess, mc->rshift, &val);
+       ucontrol->value.integer.value[1] = abe_gain_to_val(val);
+
+       return 0;
+}
+
+int abe_mixer_set_equ_profile(struct omap_abe *abe,
+               unsigned int id, unsigned int profile)
+{
+       struct omap_aess_equ params;
+       void *src_coeff;
+
+       switch (id) {
+       case OMAP_AESS_CMEM_DL1_COEFS_ID:
+               abe->equ.dl1.profile = profile;
+               params.equ_length = abe->equ.dl1.profile_size;
+               src_coeff = abe->equ.dl1.coeff_data;
+               break;
+       case OMAP_AESS_CMEM_DL2_L_COEFS_ID:
+               abe->equ.dl2l.profile = profile;
+               params.equ_length = abe->equ.dl2l.profile_size;
+               src_coeff = abe->equ.dl2l.coeff_data;
+               break;
+       case OMAP_AESS_CMEM_DL2_R_COEFS_ID:
+               abe->equ.dl2r.profile = profile;
+               params.equ_length = abe->equ.dl2r.profile_size;
+               src_coeff = abe->equ.dl2r.coeff_data;
+               break;
+       case OMAP_AESS_CMEM_SDT_COEFS_ID:
+               abe->equ.sdt.profile = profile;
+               params.equ_length = abe->equ.sdt.profile_size;
+               src_coeff = abe->equ.sdt.coeff_data;
+               break;
+       case OMAP_AESS_CMEM_96_48_AMIC_COEFS_ID:
+               abe->equ.amic.profile = profile;
+               params.equ_length = abe->equ.amic.profile_size;
+               src_coeff = abe->equ.amic.coeff_data;
+               break;
+       case OMAP_AESS_CMEM_96_48_DMIC_COEFS_ID:
+               abe->equ.dmic.profile = profile;
+               params.equ_length = abe->equ.dmic.profile_size;
+               src_coeff = abe->equ.dmic.coeff_data;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       src_coeff += profile * params.equ_length;
+       memcpy(params.coef.type1, src_coeff, params.equ_length);
+
+       omap_aess_write_equalizer(abe->aess, id,
+               (struct omap_aess_equ *)&params); //align types on this API
+
+       return 0;
+}
+
+static int abe_get_equalizer(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_enum *eqc = (struct soc_enum *)kcontrol->private_value;
+
+       switch (eqc->reg) {
+       case OMAP_AESS_CMEM_DL1_COEFS_ID:
+               ucontrol->value.integer.value[0] = abe->equ.dl1.profile;
+               break;
+       case OMAP_AESS_CMEM_DL2_L_COEFS_ID:
+               ucontrol->value.integer.value[0] = abe->equ.dl2l.profile;
+               break;
+       case OMAP_AESS_CMEM_DL2_R_COEFS_ID:
+               ucontrol->value.integer.value[0] = abe->equ.dl2r.profile;
+               break;
+       case OMAP_AESS_CMEM_SDT_COEFS_ID:
+               ucontrol->value.integer.value[0] = abe->equ.sdt.profile;
+               break;
+       case OMAP_AESS_CMEM_96_48_AMIC_COEFS_ID:
+               ucontrol->value.integer.value[0] = abe->equ.amic.profile;
+               break;
+       case OMAP_AESS_CMEM_96_48_DMIC_COEFS_ID:
+               ucontrol->value.integer.value[0] = abe->equ.dmic.profile;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int abe_put_equalizer(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct soc_enum *eqc = (struct soc_enum *)kcontrol->private_value;
+       u16 val = ucontrol->value.enumerated.item[0];
+       int ret;
+
+       ret = abe_mixer_set_equ_profile(abe, eqc->reg, val);
+       if (ret < 0)
+               return ret;
+
+       return 1;
+}
+
+const struct snd_soc_fw_kcontrol_ops abe_ops[] = {
+       {OMAP_CONTROL_DEFAULT,  abe_get_mixer,  abe_put_mixer, NULL},
+       {OMAP_CONTROL_VOLUME,   volume_get_mixer, volume_put_mixer, NULL},
+       {OMAP_CONTROL_ROUTER,   ul_mux_get_route, ul_mux_put_route, NULL},
+       {OMAP_CONTROL_EQU,      abe_get_equalizer, abe_put_equalizer, NULL},
+       {OMAP_CONTROL_GAIN,     volume_get_gain, volume_put_gain, NULL},
+       {OMAP_CONTROL_MONO,     abe_get_mono_mixer, abe_put_mono_mixer, NULL},
+       {OMAP_CONTROL_SWITCH,   NULL, abe_put_switch, NULL},
+};
+
diff --git a/sound/soc/omap/omap-abe-mmap.c b/sound/soc/omap/omap-abe-mmap.c
new file mode 100644 (file)
index 0000000..86b98c2
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * omap-abe.c  --  OMAP ALSA SoC DAI driver using Audio Backend
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Contact: Liam Girdwood <lrg@ti.com>
+ *          Misael Lopez Cruz <misael.lopez@ti.com>
+ *          Sebastien Guiriec <s-guiriec@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/pm_runtime.h>
+
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "omap-abe-priv.h"
+#include "omap-pcm.h"
+
+int abe_pm_save_context(struct omap_abe *abe);
+int abe_pm_restore_context(struct omap_abe *abe);
+int abe_opp_recalc_level(struct omap_abe *abe);
+int abe_opp_set_level(struct omap_abe *abe, int opp);
+
+/* Ping pong buffer DMEM offset - we should read this from future FWs */
+#define OMAP_ABE_DMEM_BASE_OFFSET_PING_PONG    0x4000
+
+static const struct snd_pcm_hardware omap_abe_hardware = {
+       .info                   = SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME,
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE |
+                                 SNDRV_PCM_FMTBIT_S32_LE,
+       .period_bytes_min       = 4 * 1024,
+       .period_bytes_max       = 24 * 1024,
+       .periods_min            = 4,
+       .periods_max            = 4,
+       .buffer_bytes_max       = 24 * 1024 * 2,
+};
+
+static void abe_irq_pingpong_subroutine(u32 *sub, u32 *data)
+{
+
+       struct snd_pcm_substream *substream = (struct snd_pcm_substream *)sub;
+       struct omap_abe *abe = (struct omap_abe *)data;
+       u32 dst, n_bytes;
+
+       omap_aess_read_next_ping_pong_buffer(abe->aess, OMAP_ABE_MM_DL_PORT, &dst, &n_bytes);
+       omap_aess_set_ping_pong_buffer(abe->aess, OMAP_ABE_MM_DL_PORT, n_bytes);
+
+       /* 1st IRQ does not signal completed period */
+       if (abe->mmap.first_irq) {
+               abe->mmap.first_irq = 0;
+       } else {
+               if (substream)
+                       snd_pcm_period_elapsed(substream);
+       }
+}
+
+irqreturn_t abe_irq_handler(int irq, void *dev_id)
+{
+       struct omap_abe *abe = dev_id;
+
+       pm_runtime_get_sync(abe->dev);
+       omap_aess_clear_irq(abe->aess);
+       omap_aess_irq_processing(abe->aess);
+       pm_runtime_put_sync_suspend(abe->dev);
+       return IRQ_HANDLED;
+}
+
+static int omap_abe_hwrule_period_step(struct snd_pcm_hw_params *params,
+                                       struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *period_size = hw_param_interval(params,
+                                    SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+       unsigned int rate = params_rate(params);
+
+       /* 44.1kHz has the same iteration number as 48kHz */
+       rate = (rate == 44100) ? 48000 : rate;
+
+       /* ABE requires chunks of 250us worth of data */
+       return snd_interval_step(period_size, 0, rate / 4000);
+}
+
+static int aess_open(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       int ret = 0;
+
+       mutex_lock(&abe->mutex);
+
+       dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+       switch (dai->id) {
+       case OMAP_ABE_FRONTEND_DAI_MODEM:
+               break;
+       case OMAP_ABE_FRONTEND_DAI_LP_MEDIA:
+               snd_soc_set_runtime_hwparams(substream, &omap_abe_hardware);
+               ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 1024);
+               break;
+       default:
+               /*
+                * Period size must be aligned with the Audio Engine
+                * processing loop which is 250 us long
+                */
+               ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                       SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                       omap_abe_hwrule_period_step,
+                                       NULL,
+                                       SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+               break;
+       }
+
+       if (ret < 0) {
+               dev_err(abe->dev, "failed to set period constraints for DAI %d\n",
+                       dai->id);
+               goto out;
+       }
+
+       pm_runtime_get_sync(abe->dev);
+
+       if (!abe->active++) {
+               abe->opp.level = 0;
+               abe_pm_restore_context(abe);
+               abe_opp_set_level(abe, 100);
+               omap_aess_wakeup(abe->aess);
+       }
+
+out:
+       mutex_unlock(&abe->mutex);
+       return ret;
+}
+
+static int aess_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct omap_aess_data_format format;
+       size_t period_size;
+       u32 dst, param[2];
+       int ret = 0;
+
+       mutex_lock(&abe->mutex);
+
+       dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+       if (dai->id != OMAP_ABE_FRONTEND_DAI_LP_MEDIA)
+               goto out;
+
+       format.f = params_rate(params);
+       if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE)
+               format.samp_format = STEREO_MSB;
+       else
+               format.samp_format = STEREO_16_16;
+
+       period_size = params_period_bytes(params);
+
+       param[0] = (u32)substream;
+       param[1] = (u32)abe;
+
+       /* Adding ping pong buffer subroutine */
+       omap_aess_plug_subroutine(abe->aess, &abe->aess->seq.irq_pingpong_player_id,
+                               (abe_subroutine2) abe_irq_pingpong_subroutine,
+                               2, param);
+
+       /* Connect a Ping-Pong cache-flush protocol to MM_DL port */
+       omap_aess_connect_irq_ping_pong_port(abe->aess, OMAP_ABE_MM_DL_PORT, &format,
+                               abe->aess->seq.irq_pingpong_player_id,
+                               period_size, &dst,
+                               PING_PONG_WITH_MCU_IRQ);
+
+       /* Memory mapping for hw params */
+       runtime->dma_area  = abe->io_base[0] + dst;
+       runtime->dma_addr  = 0;
+       runtime->dma_bytes = period_size * 4;
+
+       /* Need to set the first buffer in order to get interrupt */
+       omap_aess_set_ping_pong_buffer(abe->aess, OMAP_ABE_MM_DL_PORT, period_size);
+       abe->mmap.first_irq = 1;
+
+out:
+       mutex_unlock(&abe->mutex);
+       return ret;
+}
+
+static int aess_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+
+       dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+       mutex_lock(&abe->mutex);
+       /*
+        * TODO: do we need to set OPP here ? e.g.
+        * Startup() -> OPP100
+        * aess_prepare() -> OPP0 (as widgets are not updated until after)
+        * stream_event() -> OPP25/50/100 (correct value based on widgets)
+        * abe_opp_recalc_level(abe);
+        */
+       mutex_unlock(&abe->mutex);
+       return 0;
+}
+
+static int aess_close(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+
+       dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+       mutex_lock(&abe->mutex);
+
+       if (dai->id != OMAP_ABE_FRONTEND_DAI_LP_MEDIA) {
+       }
+
+       if (!--abe->active) {
+               omap_aess_disable_irq(abe->aess);
+               abe_pm_save_context(abe);
+               omap_abe_pm_shutdown(platform);
+       } else {
+               /* Only scale OPP level
+                * if ABE is still active */
+               abe_opp_recalc_level(abe);
+       }
+       pm_runtime_put_sync(abe->dev);
+
+       mutex_unlock(&abe->mutex);
+       return 0;
+}
+
+static int aess_mmap(struct snd_pcm_substream *substream,
+       struct vm_area_struct *vma)
+{
+       struct snd_soc_pcm_runtime  *rtd = substream->private_data;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       int offset, size, err;
+
+       if (dai->id != OMAP_ABE_FRONTEND_DAI_LP_MEDIA)
+               return -EINVAL;
+
+       vma->vm_flags |= VM_IO;
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+       size = vma->vm_end - vma->vm_start;
+       offset = vma->vm_pgoff << PAGE_SHIFT;
+
+       err = io_remap_pfn_range(vma, vma->vm_start,
+                       (ABE_DEFAULT_BASE_ADDRESS_L4 + ABE_DMEM_BASE_OFFSET_MPU +
+                       OMAP_ABE_DMEM_BASE_OFFSET_PING_PONG + offset) >> PAGE_SHIFT,
+                       size, vma->vm_page_prot);
+       if (err)
+               return -EAGAIN;
+
+       return 0;
+}
+
+static snd_pcm_uframes_t aess_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       snd_pcm_uframes_t offset = 0;
+       u32 pingpong;
+
+       if (!abe->mmap.first_irq) {
+               omap_aess_read_offset_from_ping_buffer(abe->aess, OMAP_ABE_MM_DL_PORT, &pingpong);
+               offset = (snd_pcm_uframes_t)pingpong;
+       }
+
+       return offset;
+}
+
+struct snd_pcm_ops omap_aess_pcm_ops = {
+       .open           = aess_open,
+       .hw_params      = aess_hw_params,
+       .prepare        = aess_prepare,
+       .close          = aess_close,
+       .pointer        = aess_pointer,
+       .mmap           = aess_mmap,
+};
diff --git a/sound/soc/omap/omap-abe-opp.c b/sound/soc/omap/omap-abe-opp.c
new file mode 100644 (file)
index 0000000..d775410
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * omap-abe.c  --  OMAP ALSA SoC DAI driver using Audio Backend
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Contact: Liam Girdwood <lrg@ti.com>
+ *          Misael Lopez Cruz <misael.lopez@ti.com>
+ *          Sebastien Guiriec <s-guiriec@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/opp.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/delay.h>
+
+#include <sound/soc.h>
+
+#include "omap-abe-priv.h"
+
+int abe_opp_recalc_level(struct omap_abe *abe);
+
+static struct abe_opp_req *abe_opp_lookup_requested(struct omap_abe *abe,
+                                       struct device *dev)
+{
+       struct abe_opp_req *req;
+
+       list_for_each_entry(req, &abe->opp.req, node) {
+               if (req->dev == dev)
+                       return req;
+       }
+
+       return NULL;
+}
+
+static int abe_opp_get_requested(struct omap_abe *abe)
+{
+       struct abe_opp_req *req;
+       int opp = 0;
+
+       list_for_each_entry(req, &abe->opp.req, node)
+               opp |= req->opp;
+
+       opp = (1 << (fls(opp) - 1)) * 25;
+
+       return opp;
+}
+
+int abe_opp_init_initial_opp(struct omap_abe *abe)
+{
+       struct opp *opp;
+       int opp_count, ret = 0, i;
+       unsigned long freq = ULONG_MAX;
+
+       abe->opp.req_count = 0;
+
+       /* query supported opps */
+       rcu_read_lock();
+       opp_count = opp_get_opp_count(abe->dev);
+       if (opp_count <= 0) {
+               dev_err(abe->dev, "opp: no OPP data\n");
+               ret = opp_count;
+               goto out;
+       } else if (opp_count > OMAP_ABE_OPP_COUNT) {
+               dev_err(abe->dev, "opp: unsupported OPP count %d (max:%d)\n",
+                       opp_count, OMAP_ABE_OPP_COUNT);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* assume provided opps are always higher */
+       for (i = OMAP_ABE_OPP_COUNT - 1; i >= 0; i--) {
+
+               opp = opp_find_freq_floor(abe->dev, &freq);
+               if (IS_ERR_OR_NULL(opp))
+                       break;
+
+               abe->opp.freqs[i] = freq;
+
+               /* prepare to obtain next available opp */
+               freq--;
+       }
+
+       /* use lowest available opp for non-populated items */
+       for (freq++; i >= 0; i--)
+               abe->opp.freqs[i] = freq;
+
+out:
+       rcu_read_unlock();
+       return ret;
+}
+
+int omap_abe_opp_new_request(struct snd_soc_platform *platform,
+               struct device *dev, int opp)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct abe_opp_req *req;
+       int ret = 0;
+
+       mutex_lock(&abe->opp.req_mutex);
+
+       req = abe_opp_lookup_requested(abe, dev);
+       if (!req) {
+               req = kzalloc(sizeof(struct abe_opp_req), GFP_KERNEL);
+               if (!req) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               req->dev = dev;
+               req->opp = 1 << opp; /* use the same convention as ABE DSP DAPM */
+
+               list_add(&req->node, &abe->opp.req);
+               dev_dbg(abe->dev, "opp: new constraint %d from %s\n", opp,
+                       dev_name(dev));
+               abe->opp.req_count++;
+       } else
+               req->opp = opp;
+
+       abe_opp_recalc_level(abe);
+
+out:
+       mutex_unlock(&abe->opp.req_mutex);
+       return ret;
+}
+EXPORT_SYMBOL(omap_abe_opp_new_request);
+
+int omap_abe_opp_free_request(struct snd_soc_platform *platform,
+               struct device *dev)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       struct abe_opp_req *req;
+       int ret;
+
+       mutex_lock(&abe->opp.req_mutex);
+
+       req = abe_opp_lookup_requested(abe, dev);
+       if (!req) {
+               dev_err(dev, "opp: trying to remove an invalid OPP request\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       dev_dbg(abe->dev, "opp: free constraint %d from %s\n", req->opp,
+                       dev_name(dev));
+
+       list_del(&req->node);
+       abe->opp.req_count--;
+       kfree(req);
+
+       abe_opp_recalc_level(abe);
+
+out:
+       mutex_unlock(&abe->opp.req_mutex);
+       return ret;
+}
+EXPORT_SYMBOL(omap_abe_opp_free_request);
+
+int abe_opp_set_level(struct omap_abe *abe, int opp)
+{
+       int ret = 0;
+
+       if (abe->opp.level > opp) {
+               /* Decrease OPP mode - no need of OPP100% */
+               switch (opp) {
+               case 25:
+                       omap_aess_set_opp_processing(abe->aess, ABE_OPP25);
+                       udelay(250);
+                       if (abe->device_scale) {
+                               ret = abe->device_scale(abe->dev, abe->dev,
+                                       abe->opp.freqs[OMAP_ABE_OPP_25]);
+                               if (ret)
+                                       goto err_down_scale;
+                       }
+
+                       break;
+               case 50:
+               default:
+                       omap_aess_set_opp_processing(abe->aess, ABE_OPP50);
+                       udelay(250);
+                       if (abe->device_scale) {
+                               ret = abe->device_scale(abe->dev, abe->dev,
+                                       abe->opp.freqs[OMAP_ABE_OPP_50]);
+                               if (ret)
+                                       goto err_down_scale;
+                       }
+
+                       break;
+               }
+       } else if (abe->opp.level < opp) {
+               /* Increase OPP mode */
+               switch (opp) {
+               case 25:
+                       if (abe->device_scale) {
+                               abe->device_scale(abe->dev, abe->dev,
+                                       abe->opp.freqs[OMAP_ABE_OPP_25]);
+                               if (ret)
+                                       goto err_up_scale;
+                       }
+
+                       omap_aess_set_opp_processing(abe->aess, ABE_OPP25);
+                       break;
+               case 50:
+                       if (abe->device_scale) {
+                               ret = abe->device_scale(abe->dev, abe->dev,
+                                       abe->opp.freqs[OMAP_ABE_OPP_50]);
+                               if (ret)
+                                       goto err_up_scale;
+                       }
+                       omap_aess_set_opp_processing(abe->aess, ABE_OPP50);
+                       break;
+               case 100:
+               default:
+                       if (abe->device_scale) {
+                               ret = abe->device_scale(abe->dev, abe->dev,
+                                       abe->opp.freqs[OMAP_ABE_OPP_100]);
+                               if (ret)
+                                       goto err_up_scale;
+                       }
+                       omap_aess_set_opp_processing(abe->aess, ABE_OPP100);
+                       break;
+               }
+       }
+       abe->opp.level = opp;
+       dev_dbg(abe->dev, "opp: new OPP level is %d\n", opp);
+
+       return 0;
+
+err_down_scale:
+       /* revert old to OPP ABE processing to keep ABE and MPU in sync */
+       dev_err(abe->dev, "opp: failed to scale OPP - reverting to %d\n",
+                       abe->opp.level);
+       switch (abe->opp.level) {
+       case 25:
+               omap_aess_set_opp_processing(abe->aess, ABE_OPP25);
+               break;
+       case 50:
+               omap_aess_set_opp_processing(abe->aess, ABE_OPP50);
+               break;
+       case 100:
+               omap_aess_set_opp_processing(abe->aess, ABE_OPP100);
+               break;
+       }
+       udelay(250);
+
+err_up_scale:
+       dev_err(abe->dev, "opp: failed to scale to OPP%d\n", opp);
+       return ret;
+}
+
+int abe_opp_recalc_level(struct omap_abe *abe)
+{
+       int i, requested_opp, opp = 0;
+
+       mutex_lock(&abe->opp.mutex);
+
+       /* now calculate OPP level based upon DAPM widget status */
+       for (i = 0; i < OMAP_ABE_NUM_WIDGETS; i++) {
+               if (abe->opp.widget[OMAP_ABE_WIDGET(i)]) {
+                       dev_dbg(abe->dev, "opp: id %d = %d%%\n", i,
+                                       abe->opp.widget[OMAP_ABE_WIDGET(i)] * 25);
+                       opp |= abe->opp.widget[OMAP_ABE_WIDGET(i)];
+               }
+       }
+       opp = (1 << (fls(opp) - 1)) * 25;
+
+       /* OPP requested outside ABE driver (e.g. McPDM) */
+       requested_opp = abe_opp_get_requested(abe);
+       dev_dbg(abe->dev, "opp: calculated %d requested %d selected %d\n",
+               opp, requested_opp, max(opp, requested_opp));
+
+       pm_runtime_get_sync(abe->dev);
+       abe_opp_set_level(abe, max(opp, requested_opp));
+       pm_runtime_put_sync(abe->dev);
+
+       mutex_unlock(&abe->opp.mutex);
+       return 0;
+}
+
+int abe_opp_stream_event(struct snd_soc_dapm_context *dapm, int event)
+{
+       struct snd_soc_platform *platform = dapm->platform;
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+
+       if (abe->active) {
+               dev_dbg(abe->dev, "opp: stream event %d\n", event);
+               abe_opp_recalc_level(abe);
+       }
+
+       return 0;
+}
diff --git a/sound/soc/omap/omap-abe-pcm.c b/sound/soc/omap/omap-abe-pcm.c
new file mode 100644 (file)
index 0000000..0444bc4
--- /dev/null
@@ -0,0 +1,1230 @@
+/*
+ * omap-abe.c  --  OMAP ALSA SoC DAI driver using Audio Backend
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Contact: Liam Girdwood <lrg@ti.com>
+ *          Misael Lopez Cruz <misael.lopez@ti.com>
+ *          Sebastien Guiriec <s-guiriec@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/delay.h>
+
+#include <linux/omap-dma.h>
+#define OMAP44XX_DMA_ABE_REQ_0                 101
+#define OMAP44XX_DMA_ABE_REQ_1                 102
+#define OMAP44XX_DMA_ABE_REQ_2                 103
+#define OMAP44XX_DMA_ABE_REQ_3                 104
+#define OMAP44XX_DMA_ABE_REQ_4                 105
+#define OMAP44XX_DMA_ABE_REQ_5                 106
+#define OMAP44XX_DMA_ABE_REQ_6                 107
+#define OMAP44XX_DMA_ABE_REQ_7                 108
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpcm.h>
+
+#include "omap-abe-priv.h"
+#include "omap-pcm.h"
+
+/*
+ * Stream DMA parameters
+ */
+static struct omap_pcm_dma_data omap_abe_dai_dma_params[7][2] = {
+       {
+               {
+                       .name = "Media Playback",
+                       .dma_req = OMAP44XX_DMA_ABE_REQ_0,
+                       .data_type = 32,
+               },
+               {
+                       .name = "Media Capture1",
+                       .dma_req = OMAP44XX_DMA_ABE_REQ_3,
+                       .data_type = 32,
+               },
+       },
+       {
+               {},
+               {
+                       .name = "Media Capture2",
+                       .dma_req = OMAP44XX_DMA_ABE_REQ_4,
+                       .data_type = 32,
+               },
+       },
+       {
+               {
+                       .name = "Voice Playback",
+                       .dma_req = OMAP44XX_DMA_ABE_REQ_1,
+                       .data_type = 32,
+               },
+               {
+                       .name = "Voice Capture",
+                       .dma_req = OMAP44XX_DMA_ABE_REQ_2,
+                       .data_type = 32,
+               },
+       },
+       {
+               {
+                       .name = "Tones Playback",
+                       .dma_req = OMAP44XX_DMA_ABE_REQ_5,
+                       .data_type = 32,
+               },
+               {},
+       },
+       {
+               {
+                       .name = "Vibra Playback",
+                       .dma_req = OMAP44XX_DMA_ABE_REQ_6,
+                       .data_type = 32,
+               },
+               {},
+       },
+       {
+               {
+                       .name = "MODEM Playback",
+                       .dma_req = OMAP44XX_DMA_ABE_REQ_1,
+                       .data_type = 32,
+               },
+               {
+                       .name = "MODEM Capture",
+                       .dma_req = OMAP44XX_DMA_ABE_REQ_2,
+                       .data_type = 32,
+               },
+       },
+       {
+               {
+                       .name = "Low Power Playback",
+                       .dma_req = OMAP44XX_DMA_ABE_REQ_0,
+                       .data_type = 32,
+               },
+               {},
+       },
+};
+
+static int omap_abe_dl1_enabled(struct omap_abe *abe)
+{
+       /* DL1 path is common for PDM_DL1, BT_VX_DL and MM_EXT_DL */
+       return omap_abe_port_is_enabled(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_PDM_DL1]) +
+               omap_abe_port_is_enabled(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_BT_VX_DL]) +
+               omap_abe_port_is_enabled(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_MM_EXT_DL]);
+}
+
+static void mute_be(struct snd_soc_pcm_runtime *be,
+               struct snd_soc_dai *dai, int stream)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(be->dev, "%s: %s %d\n", __func__, be->cpu_dai->name, stream);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               switch (be->dai_link->be_id) {
+               case OMAP_ABE_DAI_PDM_DL1:
+               case OMAP_ABE_DAI_BT_VX:
+               case OMAP_ABE_DAI_MM_FM:
+                       /*
+                        * DL1 Mixer->SDT Mixer and DL1 gain are common for
+                        * PDM_DL1, BT_VX_DL and MM_EXT_DL, mute those gains
+                        * only if the last active BE
+                        */
+                       if (omap_abe_dl1_enabled(abe) == 1) {
+                               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DL1_LEFT);
+                               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DL1_RIGHT);
+                               omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXSDT_DL);
+                       }
+                       break;
+               case OMAP_ABE_DAI_PDM_DL2:
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DL2_LEFT);
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DL2_RIGHT);
+                       break;
+               case OMAP_ABE_DAI_MODEM:
+                       break;
+               }
+       } else {
+               switch (be->dai_link->be_id) {
+               case OMAP_ABE_DAI_PDM_UL:
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_AMIC_LEFT);
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_AMIC_RIGHT);
+                       break;
+               case OMAP_ABE_DAI_BT_VX:
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_BTUL_LEFT);
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_BTUL_RIGHT);
+                       break;
+               case OMAP_ABE_DAI_MM_FM:
+               case OMAP_ABE_DAI_MODEM:
+                       break;
+               case OMAP_ABE_DAI_DMIC0:
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC1_LEFT);
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC1_RIGHT);
+                       break;
+               case OMAP_ABE_DAI_DMIC1:
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC2_LEFT);
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC2_RIGHT);
+                       break;
+               case OMAP_ABE_DAI_DMIC2:
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC3_LEFT);
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC3_RIGHT);
+                       break;
+               }
+       }
+}
+
+static void unmute_be(struct snd_soc_pcm_runtime *be,
+               struct snd_soc_dai *dai, int stream)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(be->dev, "%s: %s %d\n", __func__, be->cpu_dai->name, stream);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               switch (be->dai_link->be_id) {
+               case OMAP_ABE_DAI_PDM_DL1:
+               case OMAP_ABE_DAI_BT_VX:
+               case OMAP_ABE_DAI_MM_FM:
+                       /*
+                        * DL1 Mixer->SDT Mixer and DL1 gain are common for
+                        * PDM_DL1, BT_VX_DL and MM_EXT_DL, unmute when any
+                        * of them becomes active
+                        */
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DL1_LEFT);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DL1_RIGHT);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXSDT_DL);
+                       break;
+               case OMAP_ABE_DAI_PDM_DL2:
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DL2_LEFT);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DL2_RIGHT);
+                       break;
+               case OMAP_ABE_DAI_MODEM:
+                       break;
+               }
+       } else {
+
+               switch (be->dai_link->be_id) {
+               case OMAP_ABE_DAI_PDM_UL:
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_AMIC_LEFT);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_AMIC_RIGHT);
+                       break;
+               case OMAP_ABE_DAI_BT_VX:
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_BTUL_LEFT);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_BTUL_RIGHT);
+                       break;
+               case OMAP_ABE_DAI_MM_FM:
+               case OMAP_ABE_DAI_MODEM:
+                       break;
+               case OMAP_ABE_DAI_DMIC0:
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC1_LEFT);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC1_RIGHT);
+                       break;
+               case OMAP_ABE_DAI_DMIC1:
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC2_LEFT);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC2_RIGHT);
+                       break;
+               case OMAP_ABE_DAI_DMIC2:
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC3_LEFT);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC3_RIGHT);
+                       break;
+               }
+       }
+}
+
+static void enable_be_port(struct snd_soc_pcm_runtime *be,
+               struct snd_soc_dai *dai, int stream)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+       struct omap_aess_data_format format;
+
+       dev_dbg(be->dev, "%s: %s %d\n", __func__, be->cpu_dai->name, stream);
+
+       switch (be->dai_link->be_id) {
+       case OMAP_ABE_DAI_PDM_DL1:
+       case OMAP_ABE_DAI_PDM_DL2:
+       case OMAP_ABE_DAI_PDM_UL:
+               /* McPDM is a special case, handled by McPDM driver */
+               break;
+       case OMAP_ABE_DAI_BT_VX:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+
+                       /* port can only be configured if it's not running */
+                       if (omap_abe_port_is_enabled(abe->aess,
+                                       abe->dai.port[OMAP_ABE_BE_PORT_BT_VX_DL]))
+                               return;
+
+                       /* BT_DL connection to McBSP 1 ports */
+                       format.f = 8000;
+                       format.samp_format = STEREO_RSHIFTED_16;
+                       omap_aess_connect_serial_port(abe->aess, OMAP_ABE_BT_VX_DL_PORT, &format, MCBSP1_TX);
+                       omap_abe_port_enable(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_BT_VX_DL]);
+               } else {
+
+                       /* port can only be configured if it's not running */
+                       if (omap_abe_port_is_enabled(abe->aess,
+                                       abe->dai.port[OMAP_ABE_BE_PORT_BT_VX_UL]))
+                               return;
+
+                       /* BT_UL connection to McBSP 1 ports */
+                       format.f = 8000;
+                       format.samp_format = STEREO_RSHIFTED_16;
+                       omap_aess_connect_serial_port(abe->aess, OMAP_ABE_BT_VX_UL_PORT, &format, MCBSP1_RX);
+                       omap_abe_port_enable(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_BT_VX_UL]);
+               }
+               break;
+       case OMAP_ABE_DAI_MM_FM:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+
+                       /* port can only be configured if it's not running */
+                       if (omap_abe_port_is_enabled(abe->aess,
+                                       abe->dai.port[OMAP_ABE_BE_PORT_MM_EXT_DL]))
+                               return;
+
+                       /* MM_EXT connection to McBSP 2 ports */
+                       format.f = 48000;
+                       format.samp_format = STEREO_RSHIFTED_16;
+                       omap_aess_connect_serial_port(abe->aess, OMAP_ABE_MM_EXT_OUT_PORT, &format, MCBSP2_TX);
+                       omap_abe_port_enable(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_MM_EXT_DL]);
+               } else {
+
+                       /* port can only be configured if it's not running */
+                       if (omap_abe_port_is_enabled(abe->aess,
+                                       abe->dai.port[OMAP_ABE_BE_PORT_MM_EXT_UL]))
+                               return;
+
+                       /* MM_EXT connection to McBSP 2 ports */
+                       format.f = 48000;
+                       format.samp_format = STEREO_RSHIFTED_16;
+                       omap_aess_connect_serial_port(abe->aess, OMAP_ABE_MM_EXT_IN_PORT, &format, MCBSP2_RX);
+                       omap_abe_port_enable(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_MM_EXT_UL]);
+               }
+               break;
+       case OMAP_ABE_DAI_DMIC0:
+               omap_abe_port_enable(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_DMIC0]);
+               break;
+       case OMAP_ABE_DAI_DMIC1:
+               omap_abe_port_enable(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_DMIC1]);
+               break;
+       case OMAP_ABE_DAI_DMIC2:
+               omap_abe_port_enable(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_DMIC2]);
+               break;
+       }
+}
+
+static void enable_fe_port(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai, int stream)
+{
+       struct snd_soc_pcm_runtime *fe = substream->private_data;
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(fe->dev, "%s: %s %d\n", __func__, dai->name, stream);
+
+       switch (dai->id) {
+       case OMAP_ABE_FRONTEND_DAI_MEDIA:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       omap_abe_port_enable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_MM_DL1]);
+               else
+                       omap_abe_port_enable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_MM_UL1]);
+               break;
+       case OMAP_ABE_FRONTEND_DAI_LP_MEDIA:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       omap_abe_port_enable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_MM_DL_LP]);
+               break;
+       case OMAP_ABE_FRONTEND_DAI_MEDIA_CAPTURE:
+               if (stream == SNDRV_PCM_STREAM_CAPTURE)
+                       omap_abe_port_enable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_MM_UL2]);
+               break;
+       case OMAP_ABE_FRONTEND_DAI_MODEM:
+       case OMAP_ABE_FRONTEND_DAI_VOICE:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       omap_abe_port_enable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_VX_DL]);
+               else
+                       omap_abe_port_enable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_VX_UL]);
+               break;
+       case OMAP_ABE_FRONTEND_DAI_TONES:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       omap_abe_port_enable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_TONES]);
+               break;
+       }
+}
+
+static void disable_be_port(struct snd_soc_pcm_runtime *be,
+               struct snd_soc_dai *dai, int stream)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(be->dev, "%s: %s %d\n", __func__, be->cpu_dai->name, stream);
+
+       switch (be->dai_link->be_id) {
+       /* McPDM is a special case, handled by McPDM driver */
+       case OMAP_ABE_DAI_PDM_DL1:
+       case OMAP_ABE_DAI_PDM_DL2:
+       case OMAP_ABE_DAI_PDM_UL:
+               break;
+       case OMAP_ABE_DAI_BT_VX:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       omap_abe_port_disable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_BE_PORT_BT_VX_DL]);
+               else
+                       omap_abe_port_disable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_BE_PORT_BT_VX_UL]);
+               break;
+       case OMAP_ABE_DAI_MM_FM:
+       case OMAP_ABE_DAI_MODEM:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       omap_abe_port_disable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_BE_PORT_MM_EXT_DL]);
+               else
+                       omap_abe_port_disable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_BE_PORT_MM_EXT_UL]);
+               break;
+       case OMAP_ABE_DAI_DMIC0:
+               omap_abe_port_disable(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_DMIC0]);
+               break;
+       case OMAP_ABE_DAI_DMIC1:
+               omap_abe_port_disable(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_DMIC1]);
+               break;
+       case OMAP_ABE_DAI_DMIC2:
+               omap_abe_port_disable(abe->aess,
+                               abe->dai.port[OMAP_ABE_BE_PORT_DMIC2]);
+               break;
+       }
+}
+
+static void disable_fe_port(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai, int stream)
+{
+       struct snd_soc_pcm_runtime *fe = substream->private_data;
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(fe->dev, "%s: %s %d\n", __func__, dai->name, stream);
+
+       switch (dai->id) {
+       case OMAP_ABE_FRONTEND_DAI_MEDIA:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       omap_abe_port_disable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_MM_DL1]);
+               else
+                       omap_abe_port_disable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_MM_UL1]);
+               break;
+       case OMAP_ABE_FRONTEND_DAI_LP_MEDIA:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       omap_abe_port_disable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_MM_DL_LP]);
+               break;
+       case OMAP_ABE_FRONTEND_DAI_MEDIA_CAPTURE:
+               if (stream == SNDRV_PCM_STREAM_CAPTURE)
+                       omap_abe_port_disable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_MM_UL2]);
+               break;
+       case OMAP_ABE_FRONTEND_DAI_MODEM:
+       case OMAP_ABE_FRONTEND_DAI_VOICE:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       omap_abe_port_disable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_VX_DL]);
+               else
+                       omap_abe_port_disable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_VX_UL]);
+               break;
+       case OMAP_ABE_FRONTEND_DAI_TONES:
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       omap_abe_port_disable(abe->aess,
+                                       abe->dai.port[OMAP_ABE_FE_PORT_TONES]);
+               break;
+       }
+}
+
+static void mute_fe_port_capture(struct snd_soc_pcm_runtime *fe, struct snd_soc_dai *dai, int mute)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(fe->dev, "%s: %s FE %s\n",
+                       __func__, mute ? "mute" : "unmute",
+                       fe->dai_link->name);
+
+       switch (fe->cpu_dai->id) {
+       case OMAP_ABE_FRONTEND_DAI_MEDIA_CAPTURE:
+               if (mute) {
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL1_MM_UL2);
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL2_MM_UL2);
+               } else {
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL1_MM_UL2);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL2_MM_UL2);
+               }
+               break;
+       case OMAP_ABE_FRONTEND_DAI_MODEM:
+       case OMAP_ABE_FRONTEND_DAI_VOICE:
+               if (mute) {
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXSDT_UL);
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXAUDUL_UPLINK);
+               } else {
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXSDT_UL);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXAUDUL_UPLINK);
+               }
+               break;
+       case OMAP_ABE_FRONTEND_DAI_MEDIA:
+       default:
+               break;
+       }
+}
+
+static void mute_fe_port_playback(struct snd_soc_pcm_runtime *fe, struct snd_soc_dai *dai, int mute)
+{
+
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(fe->dev, "%s: %s FE %s\n",
+                       __func__, mute ? "mute" : "unmute",
+                       fe->dai_link->name);
+
+       switch (fe->cpu_dai->id) {
+       case OMAP_ABE_FRONTEND_DAI_MEDIA:
+       case OMAP_ABE_FRONTEND_DAI_LP_MEDIA:
+               if (mute) {
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL1_MM_DL);
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL2_MM_DL);
+               } else {
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL1_MM_DL);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL2_MM_DL);
+               }
+               break;
+       case OMAP_ABE_FRONTEND_DAI_VOICE:
+       case OMAP_ABE_FRONTEND_DAI_MODEM:
+               if (mute) {
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL1_VX_DL);
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL2_VX_DL);
+               } else {
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL1_VX_DL);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL2_VX_DL);
+               }
+               break;
+       case OMAP_ABE_FRONTEND_DAI_TONES:
+               if (mute) {
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL1_TONES);
+                       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL2_TONES);
+               } else {
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL1_TONES);
+                       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL2_TONES);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static void mute_fe_port(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai, int stream)
+{
+       struct snd_soc_pcm_runtime *fe = substream->private_data;
+
+       dev_dbg(fe->dev, "%s: %s %d\n", __func__, dai->name, stream);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               mute_fe_port_playback(fe, dai, 1);
+       else
+               mute_fe_port_capture(fe, dai, 1);
+}
+
+static void unmute_fe_port(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai, int stream)
+{
+       struct snd_soc_pcm_runtime *fe = substream->private_data;
+
+       dev_dbg(fe->dev, "%s: %s %d\n", __func__, dai->name, stream);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               mute_fe_port_playback(fe, dai, 0);
+       else
+               mute_fe_port_capture(fe, dai, 0);
+}
+
+static void capture_trigger(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai, int cmd)
+{
+       struct snd_soc_pcm_runtime *fe = substream->private_data;
+       struct snd_soc_dpcm *dpcm;
+       struct snd_pcm_substream *be_substream;
+       int stream = substream->stream;
+       enum snd_soc_dpcm_state state;
+
+       dev_dbg(fe->dev, "%s: %s %d\n", __func__, fe->cpu_dai->name, stream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+
+               /* mute and enable BE ports */
+               list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+                       struct snd_soc_pcm_runtime *be = dpcm->be;
+
+                       /* does this trigger() apply to this BE and stream ? */
+                       if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+                               continue;
+
+                       /* we only need to start BEs that are prepared or stopped */
+                       state = snd_soc_dpcm_be_get_state(be, stream);
+                       if ((state != SND_SOC_DPCM_STATE_PREPARE) &&
+                           (state != SND_SOC_DPCM_STATE_STOP))
+                               continue;
+
+                       be_substream = snd_soc_dpcm_get_substream(be, stream);
+
+                       /* mute the BE port */
+                       mute_be(be, dai, stream);
+
+                       /* enable the BE port */
+                       enable_be_port(be, dai, stream);
+
+                       /* DAI work must be started/stopped at least 250us after ABE */
+                       udelay(250);
+
+                       /* trigger the BE port */
+                       snd_soc_dai_trigger(be_substream, cmd, be->cpu_dai);
+
+                       snd_soc_dpcm_be_set_state(be, stream, SND_SOC_DPCM_STATE_START);
+               }
+
+               /* does this trigger() apply to the FE ? */
+               if (snd_soc_dpcm_fe_can_update(fe, stream)) {
+                       /* Enable Frontend sDMA  */
+                       snd_soc_platform_trigger(substream, cmd, fe->platform);
+
+                       enable_fe_port(substream, dai, stream);
+                       /* unmute FE port */
+                       unmute_fe_port(substream, dai, stream);
+               }
+
+               /* Restore ABE GAINS AMIC */
+               list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+                       struct snd_soc_pcm_runtime *be = dpcm->be;
+
+                       /* does this trigger() apply to this BE and stream ? */
+                       if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+                               continue;
+
+                       /* unmute this BE port */
+                       unmute_be(be, dai, stream);
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               /* Enable sDMA */
+               snd_soc_platform_trigger(substream, cmd, fe->platform);
+               enable_fe_port(substream, dai, stream);
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               /* Disable sDMA */
+               disable_fe_port(substream, dai, stream);
+               snd_soc_platform_trigger(substream, cmd, fe->platform);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+
+               /* does this trigger() apply to the FE ? */
+               if (snd_soc_dpcm_fe_can_update(fe, stream)) {
+                       /* mute FE port */
+                       mute_fe_port(substream, dai, stream);
+                       /* Disable sDMA */
+                       disable_fe_port(substream, dai, stream);
+                       snd_soc_platform_trigger(substream, cmd, fe->platform);
+               }
+
+               /* disable BE ports */
+               list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+                       struct snd_soc_pcm_runtime *be = dpcm->be;
+
+                       /* does this trigger() apply to this BE and stream ? */
+                       if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+                               continue;
+
+                       /* we dont need to stop BEs that are already stopped */
+                       state = snd_soc_dpcm_be_get_state(be, stream);
+                       if (state != SND_SOC_DPCM_STATE_START)
+                               continue;
+
+                       /* only stop if last running user */
+                       if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+                               continue;
+
+                       be_substream = snd_soc_dpcm_get_substream(be, stream);
+
+                       /* mute the BE port */
+                       mute_be(be, dai, stream);
+
+                       /* disable the BE port */
+                       disable_be_port(be, dai, stream);
+
+                       /* DAI work must be started/stopped at least 250us after ABE */
+                       udelay(250);
+
+                       /* trigger BE port */
+                       snd_soc_dai_trigger(be_substream, cmd, be->cpu_dai);
+
+                       snd_soc_dpcm_be_set_state(be, stream, SND_SOC_DPCM_STATE_STOP);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static void playback_trigger(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai, int cmd)
+{
+       struct snd_soc_pcm_runtime *fe = substream->private_data;
+       struct snd_soc_dpcm *dpcm;
+       struct snd_pcm_substream *be_substream;
+       int stream = substream->stream;
+       enum snd_soc_dpcm_state state;
+
+       dev_dbg(fe->dev, "%s: %s %d\n", __func__, fe->cpu_dai->name, stream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+
+               /* mute and enable ports */
+               list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+                       struct snd_soc_pcm_runtime *be = dpcm->be;
+
+                       /* does this trigger() apply to the FE ? */
+                       if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+                               continue;
+
+                       /* we only need to start BEs that are prepared or stopped */
+                       state = snd_soc_dpcm_be_get_state(be, stream);
+                       if ((state != SND_SOC_DPCM_STATE_PREPARE) &&
+                           (state != SND_SOC_DPCM_STATE_STOP))
+                               continue;
+
+                       be_substream = snd_soc_dpcm_get_substream(be, stream);
+
+                       /* mute BE port */
+                       mute_be(be, dai, stream);
+
+                       /* enabled BE port */
+                       enable_be_port(be, dai, stream);
+
+                       /* DAI work must be started/stopped at least 250us after ABE */
+                       udelay(250);
+
+                       /* trigger BE port */
+                       snd_soc_dai_trigger(be_substream, cmd, be->cpu_dai);
+
+                       /* unmute the BE port */
+                       unmute_be(be, dai, stream);
+
+                       snd_soc_dpcm_be_set_state(be, stream, SND_SOC_DPCM_STATE_START);
+               }
+
+               /* does this trigger() apply to the FE ? */
+               if (snd_soc_dpcm_fe_can_update(fe, stream)) {
+
+                       /* Enable Frontend sDMA  */
+                       snd_soc_platform_trigger(substream, cmd, fe->platform);
+                       enable_fe_port(substream, dai, stream);
+               }
+
+               /* unmute FE port (sensitive to runtime udpates) */
+               unmute_fe_port(substream, dai, stream);
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               /* Enable Frontend sDMA  */
+               snd_soc_platform_trigger(substream, cmd, fe->platform);
+               enable_fe_port(substream, dai, stream);
+
+               /* unmute FE port */
+               unmute_fe_port(substream, dai, stream);
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               /* mute FE port */
+               mute_fe_port(substream, dai, stream);
+               /* disable Frontend sDMA  */
+               disable_fe_port(substream, dai, stream);
+               snd_soc_platform_trigger(substream, cmd, fe->platform);
+
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               /* does this trigger() apply to the FE ? */
+               if (snd_soc_dpcm_fe_can_update(fe, stream)) {
+                       /* mute FE port (sensitive to runtime udpates) */
+                       mute_fe_port(substream, dai, stream);
+                       /* disable the transfer */
+                       disable_fe_port(substream, dai, stream);
+                       snd_soc_platform_trigger(substream, cmd, fe->platform);
+
+               }
+
+               /* disable BE ports */
+               list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+                       struct snd_soc_pcm_runtime *be = dpcm->be;
+
+                       /* does this trigger() apply to this BE and stream ? */
+                       if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+                               continue;
+
+                       /* we dont need to stop BEs that are already stopped */
+                       state = snd_soc_dpcm_be_get_state(be, stream);
+                       if (state != SND_SOC_DPCM_STATE_START)
+                               continue;
+
+                       /* only stop if last running user */
+                       if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+                               continue;
+
+                       be_substream = snd_soc_dpcm_get_substream(be, stream);
+
+                       /* mute the BE port */
+                       mute_be(be, dai, stream);
+
+                       /* disable the BE */
+                       disable_be_port(be, dai, stream);
+
+                       /* DAI work must be started/stopped at least 250us after ABE */
+                       udelay(250);
+
+                       /*  trigger the BE port */
+                       snd_soc_dai_trigger(be_substream, cmd, be->cpu_dai);
+
+                       snd_soc_dpcm_be_set_state(be, stream, SND_SOC_DPCM_STATE_STOP);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static int omap_abe_dai_startup(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+       struct omap_pcm_dma_data *dma_data;
+
+       dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+       abe->dai.num_active++;
+
+       dma_data = &omap_abe_dai_dma_params[dai->id][substream->stream];
+       snd_soc_dai_set_dma_data(dai, substream, dma_data);
+
+       return 0;
+}
+
+static int omap_abe_dai_hw_params(struct snd_pcm_substream *substream,
+                       struct snd_pcm_hw_params *params,
+                       struct snd_soc_dai *dai)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+       struct omap_pcm_dma_data *dma_data;
+       struct omap_aess_data_format format;
+       struct omap_aess_dma dma_sink;
+       struct omap_aess_dma dma_params;
+
+       dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+       dma_data = snd_soc_dai_get_dma_data(dai, substream);
+       switch (params_channels(params)) {
+       case 1:
+               if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
+                       format.samp_format = MONO_16_16;
+               else
+                       format.samp_format = MONO_MSB;
+               break;
+       case 2:
+               if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
+                       format.samp_format = STEREO_16_16;
+               else
+                       format.samp_format = STEREO_MSB;
+               break;
+       case 3:
+               format.samp_format = THREE_MSB;
+               break;
+       case 4:
+               format.samp_format = FOUR_MSB;
+               break;
+       case 5:
+               format.samp_format = FIVE_MSB;
+               break;
+       case 6:
+               format.samp_format = SIX_MSB;
+               break;
+       case 7:
+               format.samp_format = SEVEN_MSB;
+               break;
+       case 8:
+               format.samp_format = EIGHT_MSB;
+               break;
+       default:
+               dev_err(dai->dev, "%d channels not supported",
+                       params_channels(params));
+               return -EINVAL;
+       }
+
+       format.f = params_rate(params);
+
+       switch (dai->id) {
+       case OMAP_ABE_FRONTEND_DAI_MEDIA:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       omap_aess_connect_cbpr_dmareq_port(abe->aess, OMAP_ABE_MM_DL_PORT, &format, ABE_CBPR0_IDX,
+                                       &dma_sink);
+                       omap_aess_read_port_address(abe->aess, OMAP_ABE_MM_DL_PORT, &dma_params);
+               } else {
+                       omap_aess_connect_cbpr_dmareq_port(abe->aess, OMAP_ABE_MM_UL_PORT, &format,  ABE_CBPR3_IDX,
+                                       &dma_sink);
+                       omap_aess_read_port_address(abe->aess, OMAP_ABE_MM_UL_PORT, &dma_params);
+               }
+               break;
+       case OMAP_ABE_FRONTEND_DAI_LP_MEDIA:
+               return 0;
+               break;
+       case OMAP_ABE_FRONTEND_DAI_MEDIA_CAPTURE:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       return -EINVAL;
+               else {
+                       omap_aess_connect_cbpr_dmareq_port(abe->aess, OMAP_ABE_MM_UL2_PORT, &format,  ABE_CBPR4_IDX,
+                                       &dma_sink);
+                       omap_aess_read_port_address(abe->aess, OMAP_ABE_MM_UL2_PORT, &dma_params);
+               }
+               break;
+       case OMAP_ABE_FRONTEND_DAI_VOICE:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       omap_aess_connect_cbpr_dmareq_port(abe->aess, OMAP_ABE_VX_DL_PORT, &format, ABE_CBPR1_IDX,
+                                       &dma_sink);
+                       omap_aess_read_port_address(abe->aess, OMAP_ABE_VX_DL_PORT, &dma_params);
+               } else {
+                       omap_aess_connect_cbpr_dmareq_port(abe->aess, OMAP_ABE_VX_UL_PORT, &format,  ABE_CBPR2_IDX,
+                                       &dma_sink);
+                       omap_aess_read_port_address(abe->aess, OMAP_ABE_VX_UL_PORT, &dma_params);
+               }
+               break;
+       case OMAP_ABE_FRONTEND_DAI_TONES:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       omap_aess_connect_cbpr_dmareq_port(abe->aess, OMAP_ABE_TONES_DL_PORT, &format, ABE_CBPR5_IDX,
+                                       &dma_sink);
+                       omap_aess_read_port_address(abe->aess, OMAP_ABE_TONES_DL_PORT, &dma_params);
+               } else
+                       return -EINVAL;
+               break;
+       case OMAP_ABE_FRONTEND_DAI_MODEM:
+
+               /* MODEM is special case where data IO is performed by McBSP2
+                * directly onto VX_DL and VX_UL (instead of SDMA).
+                */
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       /* Vx_DL connection to McBSP 2 ports */
+                       format.samp_format = STEREO_RSHIFTED_16;
+                       omap_aess_connect_serial_port(abe->aess, OMAP_ABE_VX_DL_PORT, &format, MCBSP2_RX);
+                       omap_aess_read_port_address(abe->aess, OMAP_ABE_VX_DL_PORT, &dma_params);
+               } else {
+                       /* Vx_UL connection to McBSP 2 ports */
+                       format.samp_format = STEREO_RSHIFTED_16;
+                       omap_aess_connect_serial_port(abe->aess, OMAP_ABE_VX_UL_PORT, &format, MCBSP2_TX);
+                       omap_aess_read_port_address(abe->aess, OMAP_ABE_VX_UL_PORT, &dma_params);
+               }
+               break;
+       default:
+               dev_err(dai->dev, "port %d not supported\n", dai->id);
+               return -EINVAL;
+       }
+
+       /* configure frontend SDMA data */
+       dma_data->port_addr = (unsigned long)dma_params.data;
+       dma_data->packet_size = dma_params.iter;
+
+       return 0;
+}
+
+
+static int omap_abe_dai_bespoke_trigger(struct snd_pcm_substream *substream,
+                                 int cmd, struct snd_soc_dai *dai)
+{
+       dev_dbg(dai->dev, "%s: %s cmd %d\n", __func__, dai->name, cmd);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               playback_trigger(substream, dai, cmd);
+       else
+               capture_trigger(substream, dai, cmd);
+
+       return 0;
+}
+
+
+static void omap_abe_dai_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+       int err;
+
+       dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+       /* shutdown the ABE if last user */
+       if (!abe->active && !omap_aess_check_activity(abe->aess)) {
+               omap_aess_set_opp_processing(abe->aess, ABE_OPP25);
+               abe->opp.level = 25;
+               omap_aess_stop_event_generator(abe->aess);
+               udelay(250);
+               if (abe->device_scale) {
+                       err = abe->device_scale(abe->dev, abe->dev, abe->opp.freqs[0]);
+                       if (err)
+                               dev_err(abe->dev, "failed to scale to lowest OPP\n");
+               }
+       }
+
+       abe->dai.num_active--;
+}
+
+#ifdef CONFIG_PM
+static int omap_abe_dai_suspend(struct snd_soc_dai *dai)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(dai->dev, "%s: %s active %d\n",
+               __func__, dai->name, dai->active);
+
+       if (!dai->active)
+               return 0;
+
+       if (++abe->dai.num_suspended < abe->dai.num_active)
+               return 0;
+
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXSDT_UL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXSDT_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXAUDUL_MM_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXAUDUL_TONES);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXAUDUL_UPLINK);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXAUDUL_VX_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXVXREC_TONES);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXVXREC_VX_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXVXREC_MM_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXVXREC_VX_UL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL1_MM_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL1_MM_UL2);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL1_VX_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL1_TONES);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL2_MM_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL2_MM_UL2);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL2_VX_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXDL2_TONES);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXECHO_DL1);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXECHO_DL2);
+
+       return 0;
+}
+
+static int omap_abe_dai_resume(struct snd_soc_dai *dai)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(dai->dev, "%s: %s active %d\n",
+               __func__, dai->name, dai->active);
+
+       if (!dai->active)
+               return 0;
+
+       if (abe->dai.num_suspended-- < abe->dai.num_active)
+               return 0;
+
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXSDT_UL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXSDT_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXAUDUL_MM_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXAUDUL_TONES);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXAUDUL_UPLINK);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXAUDUL_VX_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXVXREC_TONES);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXVXREC_VX_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXVXREC_MM_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXVXREC_VX_UL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL1_MM_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL1_MM_UL2);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL1_VX_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL1_TONES);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL2_MM_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL2_MM_UL2);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL2_VX_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXDL2_TONES);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXECHO_DL1);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXECHO_DL2);
+
+       return 0;
+}
+#else
+#define omap_abe_dai_suspend   NULL
+#define omap_abe_dai_resume            NULL
+#endif
+
+static int omap_abe_dai_probe(struct snd_soc_dai *dai)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+       int i;
+
+       abe->aess = omap_abe_port_mgr_get();
+       if (!abe->aess)
+               goto err;
+
+       for (i = 0; i <= OMAP_ABE_MAX_PORT_ID; i++) {
+
+               abe->dai.port[i] = omap_abe_port_open(abe->aess, i);
+               if (abe->dai.port[i] == NULL)
+                       goto err_port;
+       }
+
+       return 0;
+
+err_port:
+       for (--i; i >= 0; i--)
+               omap_abe_port_close(abe->aess, abe->dai.port[i]);
+       omap_abe_port_mgr_put(abe->aess);
+err:
+       return -ENODEV;
+}
+
+static int omap_abe_dai_remove(struct snd_soc_dai *dai)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+       int i;
+
+       for (i = 0; i <= OMAP_ABE_MAX_PORT_ID; i++)
+               omap_abe_port_close(abe->aess, abe->dai.port[i]);
+
+       omap_abe_port_mgr_put(abe->aess);
+       return 0;
+}
+
+static struct snd_soc_dai_ops omap_abe_dai_ops = {
+       .startup        = omap_abe_dai_startup,
+       .shutdown       = omap_abe_dai_shutdown,
+       .hw_params      = omap_abe_dai_hw_params,
+       .bespoke_trigger = omap_abe_dai_bespoke_trigger,
+};
+
+struct snd_soc_dai_driver omap_abe_dai[] = {
+       {       /* Multimedia Playback and Capture */
+               .name = "MultiMedia1",
+               .probe = omap_abe_dai_probe,
+               .remove = omap_abe_dai_remove,
+               .suspend = omap_abe_dai_suspend,
+               .resume = omap_abe_dai_resume,
+               .playback = {
+                       .stream_name = "MM1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+               },
+               .capture = {
+                       .stream_name = "MM1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 6,
+                       .rates = SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FMTBIT_S32_LE,
+               },
+               .ops = &omap_abe_dai_ops,
+       },
+       {       /* Multimedia Capture */
+               .name = "MultiMedia2",
+               .suspend = omap_abe_dai_suspend,
+               .resume = omap_abe_dai_resume,
+               .capture = {
+                       .stream_name = "MM2 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+               },
+               .ops = &omap_abe_dai_ops,
+       },
+       {       /* Voice Playback and Capture */
+               .name = "Voice",
+               .suspend = omap_abe_dai_suspend,
+               .resume = omap_abe_dai_resume,
+               .playback = {
+                       .stream_name = "Voice Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+               },
+               .capture = {
+                       .stream_name = "Voice Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+               },
+               .ops = &omap_abe_dai_ops,
+       },
+       {       /* Tones Playback */
+               .name = "Tones",
+               .suspend = omap_abe_dai_suspend,
+               .resume = omap_abe_dai_resume,
+               .playback = {
+                       .stream_name = "Tones Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+               },
+               .ops = &omap_abe_dai_ops,
+       },
+       {       /* MODEM Voice Playback and Capture */
+               .name = "MODEM",
+               .suspend = omap_abe_dai_suspend,
+               .resume = omap_abe_dai_resume,
+               .playback = {
+                       .stream_name = "Modem Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+               },
+               .capture = {
+                       .stream_name = "Modem Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+               },
+               .ops = &omap_abe_dai_ops,
+       },
+       {       /* Low Power HiFi Playback */
+               .name = "MultiMedia1 LP",
+               .suspend = omap_abe_dai_suspend,
+               .resume = omap_abe_dai_resume,
+               .playback = {
+                       .stream_name = "MMLP Playback",
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+               },
+               .ops = &omap_abe_dai_ops,
+       },
+};
diff --git a/sound/soc/omap/omap-abe-pm.c b/sound/soc/omap/omap-abe-pm.c
new file mode 100644 (file)
index 0000000..c4cf0a5
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * omap-abe.c  --  OMAP ALSA SoC DAI driver using Audio Backend
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Contact: Liam Girdwood <lrg@ti.com>
+ *          Misael Lopez Cruz <misael.lopez@ti.com>
+ *          Sebastien Guiriec <s-guiriec@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+
+#include <sound/soc.h>
+
+#include "omap-abe-priv.h"
+
+int abe_mixer_enable_mono(struct omap_abe *abe, int id, int enable);
+int abe_mixer_set_equ_profile(struct omap_abe *abe,
+               unsigned int id, unsigned int profile);
+
+void omap_abe_pm_get(struct snd_soc_platform *platform)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       pm_runtime_get_sync(abe->dev);
+}
+EXPORT_SYMBOL_GPL(omap_abe_pm_get);
+
+void omap_abe_pm_put(struct snd_soc_platform *platform)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       pm_runtime_put_sync(abe->dev);
+}
+EXPORT_SYMBOL_GPL(omap_abe_pm_put);
+
+void omap_abe_pm_shutdown(struct snd_soc_platform *platform)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+       int ret;
+
+       if (abe->active && omap_aess_check_activity(abe->aess))
+               return;
+
+       omap_aess_set_opp_processing(abe->aess, ABE_OPP25);
+       abe->opp.level = 25;
+
+       omap_aess_stop_event_generator(abe->aess);
+       udelay(250);
+       if (abe->device_scale) {
+               ret = abe->device_scale(abe->dev, abe->dev, abe->opp.freqs[0]);
+               if (ret)
+                       dev_err(abe->dev, "failed to scale to lowest OPP\n");
+       }
+}
+EXPORT_SYMBOL_GPL(omap_abe_pm_shutdown);
+
+void omap_abe_pm_set_mode(struct snd_soc_platform *platform, int mode)
+{
+       struct omap_abe *abe = snd_soc_platform_get_drvdata(platform);
+
+       abe->dc_offset.power_mode = mode;
+}
+EXPORT_SYMBOL(omap_abe_pm_set_mode);
+
+int abe_pm_save_context(struct omap_abe *abe)
+{
+       /* mute gains not associated with FEs/BEs */
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXAUDUL_MM_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXAUDUL_TONES);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXAUDUL_VX_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXVXREC_TONES);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXVXREC_VX_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXVXREC_MM_DL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXVXREC_VX_UL);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXECHO_DL1);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXECHO_DL2);
+
+       /*
+        * mute gains associated with DL1 BE
+        * ideally, these gains should be muted/saved when BE is muted, but
+        * when ABE McPDM is started for DL1 or DL2, PDM_DL1 port gets enabled
+        * which prevents to mute these gains since two ports on DL1 path are
+        * active when mute is called for BT_VX_DL or MM_EXT_DL.
+        *
+        * These gains are not restored along with the context because they
+        * are properly unmuted/restored when any of the DL1 BEs is unmuted
+        */
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DL1_LEFT);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DL1_RIGHT);
+       omap_aess_mute_gain(abe->aess, OMAP_AESS_MIXSDT_DL);
+
+       return 0;
+}
+
+int abe_pm_restore_context(struct omap_abe *abe)
+{
+       int i, ret;
+
+       if (abe->device_scale) {
+               ret = abe->device_scale(abe->dev, abe->dev,
+                               abe->opp.freqs[OMAP_ABE_OPP_50]);
+               if (ret) {
+                       dev_err(abe->dev, "failed to scale to OPP 50\n");
+                       return ret;
+               }
+       }
+
+       /* unmute gains not associated with FEs/BEs */
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXAUDUL_MM_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXAUDUL_TONES);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXAUDUL_VX_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXVXREC_TONES);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXVXREC_VX_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXVXREC_MM_DL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXVXREC_VX_UL);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXECHO_DL1);
+       omap_aess_unmute_gain(abe->aess, OMAP_AESS_MIXECHO_DL2);
+       omap_aess_set_router_configuration(abe->aess, (u32 *)abe->mixer.route_ul);
+
+       /* DC offset cancellation setting */
+       if (abe->dc_offset.power_mode)
+               omap_aess_write_pdmdl_offset(abe->aess, 1, abe->dc_offset.hsl * 2, abe->dc_offset.hsr * 2);
+       else
+               omap_aess_write_pdmdl_offset(abe->aess, 1, abe->dc_offset.hsl, abe->dc_offset.hsr);
+
+       omap_aess_write_pdmdl_offset(abe->aess, 2, abe->dc_offset.hfl, abe->dc_offset.hfr);
+
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_DL1_COEFS_ID, abe->equ.dl1.profile);
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_DL2_L_COEFS_ID, abe->equ.dl2l.profile);
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_DL2_R_COEFS_ID, abe->equ.dl2r.profile);
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_SDT_COEFS_ID, abe->equ.sdt.profile);
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_96_48_AMIC_COEFS_ID, abe->equ.amic.profile);
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_96_48_DMIC_COEFS_ID, abe->equ.dmic.profile);
+
+       for (i = 0; i < OMAP_ABE_NUM_MONO_MIXERS; i++)
+               abe_mixer_enable_mono(abe, MIX_DL1_MONO + i, abe->mixer.mono[i]);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+int abe_pm_suspend(struct snd_soc_dai *dai)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+       int ret = 0;
+
+       dev_dbg(dai->dev, "%s: %s active %d\n",
+               __func__, dai->name, dai->active);
+
+       if (!dai->active)
+               return 0;
+
+       pm_runtime_get_sync(abe->dev);
+
+       switch (dai->id) {
+       case OMAP_ABE_DAI_PDM_UL:
+               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_AMIC_LEFT);
+               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_AMIC_RIGHT);
+               break;
+       case OMAP_ABE_DAI_PDM_DL1:
+       case OMAP_ABE_DAI_PDM_DL2:
+               break;
+       case OMAP_ABE_DAI_BT_VX:
+               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_BTUL_LEFT);
+               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_BTUL_RIGHT);
+               break;
+       case OMAP_ABE_DAI_MM_FM:
+       case OMAP_ABE_DAI_MODEM:
+               break;
+       case OMAP_ABE_DAI_DMIC0:
+               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC1_LEFT);
+               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC1_RIGHT);
+               break;
+       case OMAP_ABE_DAI_DMIC1:
+               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC2_LEFT);
+               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC2_RIGHT);
+               break;
+       case OMAP_ABE_DAI_DMIC2:
+               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC3_LEFT);
+               omap_aess_mute_gain(abe->aess, OMAP_AESS_GAIN_DMIC3_RIGHT);
+               break;
+       default:
+               dev_err(dai->dev, "%s: invalid DAI id %d\n",
+                               __func__, dai->id);
+               break;
+       }
+
+       pm_runtime_put_sync(abe->dev);
+       return ret;
+}
+
+int abe_pm_resume(struct snd_soc_dai *dai)
+{
+       struct omap_abe *abe = snd_soc_dai_get_drvdata(dai);
+       int i, ret = 0;
+
+       dev_dbg(dai->dev, "%s: %s active %d\n",
+               __func__, dai->name, dai->active);
+
+       if (!dai->active)
+               return 0;
+
+       /* context retained, no need to restore */
+       if (abe->get_context_lost_count && abe->get_context_lost_count(abe->dev) == abe->context_lost)
+               return 0;
+       abe->context_lost = abe->get_context_lost_count(abe->dev);
+       pm_runtime_get_sync(abe->dev);
+       if (abe->device_scale) {
+               ret = abe->device_scale(abe->dev, abe->dev,
+                               abe->opp.freqs[OMAP_ABE_OPP_50]);
+               if (ret) {
+                       dev_err(abe->dev, "failed to scale to OPP 50\n");
+                       goto out;
+               }
+       }
+
+       omap_aess_reload_fw(abe->aess, abe->fw_text);
+
+       switch (dai->id) {
+       case OMAP_ABE_DAI_PDM_UL:
+               omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_AMIC_LEFT);
+               omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_AMIC_RIGHT);
+               break;
+       case OMAP_ABE_DAI_PDM_DL1:
+       case OMAP_ABE_DAI_PDM_DL2:
+               break;
+       case OMAP_ABE_DAI_BT_VX:
+               omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_BTUL_LEFT);
+               omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_BTUL_RIGHT);
+               break;
+       case OMAP_ABE_DAI_MM_FM:
+       case OMAP_ABE_DAI_MODEM:
+               break;
+       case OMAP_ABE_DAI_DMIC0:
+               omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC1_LEFT);
+               omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC1_RIGHT);
+               break;
+       case OMAP_ABE_DAI_DMIC1:
+               omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC2_LEFT);
+               omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC2_RIGHT);
+               break;
+       case OMAP_ABE_DAI_DMIC2:
+               omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC3_LEFT);
+               omap_aess_unmute_gain(abe->aess, OMAP_AESS_GAIN_DMIC3_RIGHT);
+               break;
+       default:
+               dev_err(dai->dev, "%s: invalid DAI id %d\n",
+                               __func__, dai->id);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       omap_aess_set_router_configuration(abe->aess, (u32 *)abe->mixer.route_ul);
+
+       if (abe->dc_offset.power_mode)
+               omap_aess_write_pdmdl_offset(abe->aess, 1, abe->dc_offset.hsl * 2, abe->dc_offset.hsr * 2);
+       else
+               omap_aess_write_pdmdl_offset(abe->aess, 1, abe->dc_offset.hsl, abe->dc_offset.hsr);
+
+       omap_aess_write_pdmdl_offset(abe->aess, 2, abe->dc_offset.hfl, abe->dc_offset.hfr);
+
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_DL1_COEFS_ID, abe->equ.dl1.profile);
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_DL2_L_COEFS_ID, abe->equ.dl2l.profile);
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_DL2_R_COEFS_ID, abe->equ.dl2r.profile);
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_SDT_COEFS_ID, abe->equ.sdt.profile);
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_96_48_AMIC_COEFS_ID, abe->equ.amic.profile);
+       abe_mixer_set_equ_profile(abe, OMAP_AESS_CMEM_96_48_DMIC_COEFS_ID, abe->equ.dmic.profile);
+
+       for (i = 0; i < OMAP_ABE_NUM_MONO_MIXERS; i++)
+               abe_mixer_enable_mono(abe, MIX_DL1_MONO + i, abe->mixer.mono[i]);
+out:
+       pm_runtime_put_sync(abe->dev);
+       return ret;
+}
+#else
+#define abe_suspend    NULL
+#define abe_resume     NULL
+#endif
diff --git a/sound/soc/omap/omap-abe-priv.h b/sound/soc/omap/omap-abe-priv.h
new file mode 100644 (file)
index 0000000..ff03795
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ * omap-abe.h
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Contact: Liam Girdwood <lrg@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __OMAP_ABE_PRIV_H__
+#define __OMAP_ABE_PRIV_H__
+
+#ifdef __KERNEL__
+
+#include <sound/soc-fw.h>
+
+#include "aess/abe.h"
+#include "aess/abe_gain.h"
+#include "aess/abe_aess.h"
+#include "aess/abe_mem.h"
+
+#endif
+
+#define OMAP_ABE_FRONTEND_DAI_MEDIA            0
+#define OMAP_ABE_FRONTEND_DAI_MEDIA_CAPTURE    1
+#define OMAP_ABE_FRONTEND_DAI_VOICE            2
+#define OMAP_ABE_FRONTEND_DAI_TONES            3
+#define OMAP_ABE_FRONTEND_DAI_MODEM            4
+#define OMAP_ABE_FRONTEND_DAI_LP_MEDIA         5
+#define OMAP_ABE_FRONTEND_DAI_NUM              6
+
+/* This must currently match the BE order in DSP */
+#define OMAP_ABE_DAI_PDM_UL                    0
+#define OMAP_ABE_DAI_PDM_DL1                   1
+#define OMAP_ABE_DAI_PDM_DL2                   2
+#define OMAP_ABE_DAI_BT_VX                     3
+#define OMAP_ABE_DAI_MM_FM                     4
+#define OMAP_ABE_DAI_MODEM                     5
+#define OMAP_ABE_DAI_DMIC0                     6
+#define OMAP_ABE_DAI_DMIC1                     7
+#define OMAP_ABE_DAI_DMIC2                     8
+#define OMAP_ABE_DAI_VXREC                     9
+#define OMAP_ABE_DAI_NUM                       10
+
+#define OMAP_ABE_MIXER(x)              (x)
+
+
+#define MIX_SWITCH_PDM_DL              OMAP_ABE_MIXER(1)
+#define MIX_SWITCH_BT_VX_DL            OMAP_ABE_MIXER(2)
+#define MIX_SWITCH_MM_EXT_DL           OMAP_ABE_MIXER(3)
+#define MIX_DL1_MONO           OMAP_ABE_MIXER(4)
+#define MIX_DL2_MONO           OMAP_ABE_MIXER(5)
+#define MIX_AUDUL_MONO         OMAP_ABE_MIXER(6)
+
+#define OMAP_ABE_VIRTUAL_SWITCH        36
+
+#define OMAP_ABE_NUM_MONO_MIXERS       (MIX_AUDUL_MONO - MIX_DL1_MONO + 1)
+#define OMAP_ABE_NUM_MIXERS            (MIX_AUDUL_MONO + 1)
+
+#define OMAP_ABE_MUX(x)                (x + 37)
+
+#define MUX_MM_UL10            OMAP_ABE_MUX(0)
+#define MUX_MM_UL11            OMAP_ABE_MUX(1)
+#define MUX_MM_UL12            OMAP_ABE_MUX(2)
+#define MUX_MM_UL13            OMAP_ABE_MUX(3)
+#define MUX_MM_UL14            OMAP_ABE_MUX(4)
+#define MUX_MM_UL15            OMAP_ABE_MUX(5)
+#define MUX_MM_UL20            OMAP_ABE_MUX(6)
+#define MUX_MM_UL21            OMAP_ABE_MUX(7)
+#define MUX_VX_UL0             OMAP_ABE_MUX(8)
+#define MUX_VX_UL1             OMAP_ABE_MUX(9)
+
+#define OMAP_ABE_NUM_MUXES             (MUX_VX_UL1 - MUX_MM_UL10)
+
+#define OMAP_ABE_WIDGET(x)             (x + OMAP_ABE_NUM_MIXERS + OMAP_ABE_NUM_MUXES)
+
+/* ABE AIF Frontend Widgets */
+#define OMAP_ABE_AIF_TONES_DL          OMAP_ABE_WIDGET(0)
+#define OMAP_ABE_AIF_VX_DL             OMAP_ABE_WIDGET(1)
+#define OMAP_ABE_AIF_VX_UL             OMAP_ABE_WIDGET(2)
+#define OMAP_ABE_AIF_MM_UL1            OMAP_ABE_WIDGET(3)
+#define OMAP_ABE_AIF_MM_UL2            OMAP_ABE_WIDGET(4)
+#define OMAP_ABE_AIF_MM_DL             OMAP_ABE_WIDGET(5)
+#define OMAP_ABE_AIF_MM_DL_LP          OMAP_ABE_AIF_MM_DL
+#define OMAP_ABE_AIF_MODEM_DL          OMAP_ABE_WIDGET(6)
+#define OMAP_ABE_AIF_MODEM_UL          OMAP_ABE_WIDGET(7)
+
+/* ABE AIF Backend Widgets */
+#define OMAP_ABE_AIF_PDM_UL1           OMAP_ABE_WIDGET(8)
+#define OMAP_ABE_AIF_PDM_DL1           OMAP_ABE_WIDGET(9)
+#define OMAP_ABE_AIF_PDM_DL2           OMAP_ABE_WIDGET(10)
+#define OMAP_ABE_AIF_BT_VX_UL          OMAP_ABE_WIDGET(11)
+#define OMAP_ABE_AIF_BT_VX_DL          OMAP_ABE_WIDGET(12)
+#define OMAP_ABE_AIF_MM_EXT_UL OMAP_ABE_WIDGET(13)
+#define OMAP_ABE_AIF_MM_EXT_DL OMAP_ABE_WIDGET(14)
+#define OMAP_ABE_AIF_DMIC0             OMAP_ABE_WIDGET(15)
+#define OMAP_ABE_AIF_DMIC1             OMAP_ABE_WIDGET(16)
+#define OMAP_ABE_AIF_DMIC2             OMAP_ABE_WIDGET(17)
+
+/* ABE ROUTE_UL MUX Widgets */
+#define OMAP_ABE_MUX_UL00              OMAP_ABE_WIDGET(18)
+#define OMAP_ABE_MUX_UL01              OMAP_ABE_WIDGET(19)
+#define OMAP_ABE_MUX_UL02              OMAP_ABE_WIDGET(20)
+#define OMAP_ABE_MUX_UL03              OMAP_ABE_WIDGET(21)
+#define OMAP_ABE_MUX_UL04              OMAP_ABE_WIDGET(22)
+#define OMAP_ABE_MUX_UL05              OMAP_ABE_WIDGET(23)
+#define OMAP_ABE_MUX_UL10              OMAP_ABE_WIDGET(24)
+#define OMAP_ABE_MUX_UL11              OMAP_ABE_WIDGET(25)
+#define OMAP_ABE_MUX_VX00              OMAP_ABE_WIDGET(26)
+#define OMAP_ABE_MUX_VX01              OMAP_ABE_WIDGET(27)
+
+/* ABE Volume and Mixer Widgets */
+#define OMAP_ABE_MIXER_DL1             OMAP_ABE_WIDGET(28)
+#define OMAP_ABE_MIXER_DL2             OMAP_ABE_WIDGET(29)
+#define OMAP_ABE_VOLUME_DL1            OMAP_ABE_WIDGET(30)
+#define OMAP_ABE_MIXER_AUDIO_UL        OMAP_ABE_WIDGET(31)
+#define OMAP_ABE_MIXER_VX_REC          OMAP_ABE_WIDGET(32)
+#define OMAP_ABE_MIXER_SDT             OMAP_ABE_WIDGET(33)
+#define OMAP_ABE_VSWITCH_DL1_PDM       OMAP_ABE_WIDGET(34)
+#define OMAP_ABE_VSWITCH_DL1_BT_VX     OMAP_ABE_WIDGET(35)
+#define OMAP_ABE_VSWITCH_DL1_MM_EXT    OMAP_ABE_WIDGET(36)
+#define OMAP_ABE_AIF_VXREC             OMAP_ABE_WIDGET(37)
+
+#define OMAP_ABE_NUM_WIDGETS           (OMAP_ABE_AIF_VXREC - OMAP_ABE_AIF_TONES_DL)
+#define OMAP_ABE_WIDGET_LAST           OMAP_ABE_AIF_VXREC
+
+#define OMAP_ABE_NUM_DAPM_REG          \
+       (OMAP_ABE_NUM_MIXERS + OMAP_ABE_NUM_MUXES + OMAP_ABE_NUM_WIDGETS)
+
+#define OMAP_ABE_ROUTES_UL             14
+
+/* Firmware coefficients and equalizers */
+#define OMAP_ABE_MAX_FW_SIZE           (1024 * 128)
+#define OMAP_ABE_MAX_COEFF_SIZE        (1024 * 4)
+#define OMAP_ABE_COEFF_NAME_SIZE       20
+#define OMAP_ABE_COEFF_TEXT_SIZE       20
+#define OMAP_ABE_COEFF_NUM_TEXTS       10
+#define OMAP_ABE_MAX_EQU               10
+#define OMAP_ABE_MAX_PROFILES  30
+
+#define OMAP_ABE_OPP_25                0
+#define OMAP_ABE_OPP_50                1
+#define OMAP_ABE_OPP_100               2
+#define OMAP_ABE_OPP_COUNT     3
+
+/* TODO: we need the names for each array memeber */
+#define OMAP_ABE_IO_RESOURCES  5
+#define OMAP_ABE_IO_DMEM 0
+#define OMAP_ABE_IO_CMEM 1
+#define OMAP_ABE_IO_SMEM 2
+#define OMAP_ABE_IO_PMEM 3
+#define OMAP_ABE_IO_AESS 4
+
+#define OMAP_ABE_EQU_AMIC      0
+#define OMAP_ABE_EQU_DL1       0
+#define OMAP_ABE_EQU_DL2L      0
+#define OMAP_ABE_EQU_DL2R      0
+#define OMAP_ABE_EQU_DMIC      0
+#define OMAP_ABE_EQU_SDT       0
+
+/* TODO: combine / sort */
+#define OMAP_ABE_MIXER_DEFAULT 128
+#define OMAP_ABE_MIXER_MONO    129
+#define OMAP_ABE_MIXER_ROUTER  130
+#define OMAP_ABE_MIXER_EQU     131
+#define OMAP_ABE_MIXER_SWITCH  132
+#define OMAP_ABE_MIXER_GAIN    133
+#define OMAP_ABE_MIXER_VOLUME  134
+
+#define OMAP_CONTROL_DEFAULT \
+       SOC_CONTROL_ID(OMAP_ABE_MIXER_DEFAULT, \
+               OMAP_ABE_MIXER_DEFAULT, \
+               SOC_CONTROL_TYPE_EXT)
+#define OMAP_CONTROL_MONO \
+       SOC_CONTROL_ID(OMAP_ABE_MIXER_MONO, \
+               OMAP_ABE_MIXER_MONO, \
+               SOC_CONTROL_TYPE_VOLSW)
+#define OMAP_CONTROL_ROUTER \
+       SOC_CONTROL_ID(OMAP_ABE_MIXER_ROUTER, \
+               OMAP_ABE_MIXER_ROUTER, \
+               SOC_CONTROL_TYPE_ENUM)
+#define OMAP_CONTROL_EQU \
+       SOC_CONTROL_ID(OMAP_ABE_MIXER_EQU, \
+               OMAP_ABE_MIXER_EQU, \
+               SOC_CONTROL_TYPE_ENUM)
+#define OMAP_CONTROL_SWITCH \
+       SOC_CONTROL_ID(OMAP_ABE_MIXER_DEFAULT, \
+               OMAP_ABE_MIXER_SWITCH, \
+               SOC_CONTROL_TYPE_VOLSW)
+#define OMAP_CONTROL_GAIN \
+       SOC_CONTROL_ID(OMAP_ABE_MIXER_GAIN, \
+               OMAP_ABE_MIXER_GAIN, \
+               SOC_CONTROL_TYPE_VOLSW)
+#define OMAP_CONTROL_VOLUME \
+       SOC_CONTROL_ID(OMAP_ABE_MIXER_VOLUME, \
+               OMAP_ABE_MIXER_VOLUME, \
+               SOC_CONTROL_TYPE_VOLSW)
+
+
+#ifdef __KERNEL__
+
+/*
+ * ABE Firmware Header.
+ * The ABE firmware blob has a header that describes each data section. This
+ * way we can store coefficients etc in the firmware.
+ */
+struct fw_header {
+       u32 version;    /* min version of ABE firmware required */
+       u32 pmem_size;
+       u32 cmem_size;
+       u32 dmem_size;
+       u32 smem_size;
+};
+
+struct abe_opp_req {
+       struct device *dev;
+       struct list_head node;
+       int opp;
+};
+
+struct omap_abe_debugfs {
+       /* its intended we can switch on/off individual debug items */
+       u32 format1; /* TODO: match flag names here to debug format flags */
+       u32 format2;
+       u32 format3;
+
+       /* ABE DMA buffer */
+       u32 buffer_bytes;
+       u32 circular;
+       u32 buffer_msecs;  /* size of buffer in secs */
+       u32 elem_bytes;
+       dma_addr_t buffer_addr;
+       wait_queue_head_t wait;
+       size_t reader_offset;
+       size_t dma_offset;
+       int complete;
+       char *buffer;
+       struct omap_pcm_dma_data *dma_data;
+       int dma_ch;
+       int dma_req;
+
+       /* debugfs */
+       struct dentry *d_root;
+       struct dentry *d_fmt1;
+       struct dentry *d_fmt2;
+       struct dentry *d_fmt3;
+       struct dentry *d_size;
+       struct dentry *d_data;
+       struct dentry *d_circ;
+       struct dentry *d_elem_bytes;
+       struct dentry *d_opp;
+       struct dentry *d_cmem;
+       struct dentry *d_pmem;
+       struct dentry *d_smem;
+       struct dentry *d_dmem;
+};
+
+struct omap_abe_dc_offset {
+       /* DC offset cancellation */
+       int power_mode;
+       u32 hsl;
+       u32 hsr;
+       u32 hfl;
+       u32 hfr;
+};
+
+struct omap_abe_opp {
+       struct mutex mutex;
+       struct mutex req_mutex;
+       int level;
+       unsigned long freqs[OMAP_ABE_OPP_COUNT];
+       u32 widget[OMAP_ABE_NUM_DAPM_REG + 1];
+       struct list_head req;
+       int req_count;
+};
+
+struct omap_abe_modem {
+       struct snd_pcm_substream *substream[2];
+       struct snd_soc_dai *dai;
+};
+
+struct omap_abe_mmap {
+       int first_irq;
+};
+
+struct omap_abe_coeff {
+       int profile; /* current enabled profile */
+       int num_profiles;
+       int profile_size;
+       void *coeff_data;
+};
+
+struct omap_abe_equ {
+       struct omap_abe_coeff dl1;
+       struct omap_abe_coeff dl2l;
+       struct omap_abe_coeff dl2r;
+       struct omap_abe_coeff sdt;
+       struct omap_abe_coeff amic;
+       struct omap_abe_coeff dmic;
+};
+
+struct omap_abe_dai {
+       struct omap_abe_port *port[OMAP_ABE_MAX_PORT_ID + 1];
+       int num_active;
+       int num_suspended;
+};
+
+struct omap_abe_mixer {
+       int mono[OMAP_ABE_NUM_MONO_MIXERS];
+       u16 route_ul[16];
+};
+
+/*
+ * ABE private data.
+ */
+struct omap_abe {
+       struct device *dev;
+       struct omap_aess *aess;
+
+       struct clk *clk;
+       void __iomem *io_base[OMAP_ABE_IO_RESOURCES];
+       int irq;
+       int active;
+       struct mutex mutex;
+       int (*get_context_lost_count)(struct device *dev);
+       int (*device_scale)(struct device *req_dev,
+                           struct device *target_dev,
+                           unsigned long rate);
+       u32 context_lost;
+
+       struct omap_abe_opp opp;
+       struct omap_abe_dc_offset dc_offset;
+       struct omap_abe_modem modem;
+       struct omap_abe_mmap mmap;
+       struct omap_abe_equ equ;
+       struct omap_abe_dai dai;
+       struct omap_abe_mixer mixer;
+
+       /* firmware */
+       struct fw_header hdr;
+       u32 *fw_config;
+       u32 *fw_text;
+       const struct firmware *fw;
+       int num_equ;
+
+#ifdef CONFIG_DEBUG_FS
+       struct omap_abe_debugfs debugfs;
+#endif
+};
+
+/* External API for client component drivers */
+
+/* Power Management */
+void omap_abe_pm_shutdown(struct snd_soc_platform *platform);
+void omap_abe_pm_get(struct snd_soc_platform *platform);
+void omap_abe_pm_put(struct snd_soc_platform *platform);
+void omap_abe_pm_set_mode(struct snd_soc_platform *platform, int mode);
+
+/* Operating Point */
+int omap_abe_opp_new_request(struct snd_soc_platform *platform,
+               struct device *dev, int opp);
+int omap_abe_opp_free_request(struct snd_soc_platform *platform,
+               struct device *dev);
+
+/* DC Offset */
+void omap_abe_dc_set_hs_offset(struct snd_soc_platform *platform,
+       int left, int right, int step_mV);
+void omap_abe_dc_set_hf_offset(struct snd_soc_platform *platform,
+       int left, int right);
+void omap_abe_set_dl1_gains(struct snd_soc_platform *platform,
+       int left, int right);
+
+
+extern const struct snd_soc_fw_kcontrol_ops abe_ops[];
+
+#endif  /* __kernel__ */
+#endif /* End of __OMAP_MCPDM_H__ */
index e7d93fa412a9c3387eef239300b44de4cac19dcb..597e83b2fa430223bfe70b07c3e5003d7b357e19 100644 (file)
 #include <linux/mfd/twl6040.h>
 #include <linux/platform_data/omap-abe-twl6040.h>
 #include <linux/module.h>
+#include <linux/i2c.h>
 #include <linux/of.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dpcm.h>
 
 #include "omap-dmic.h"
 #include "omap-mcpdm.h"
 #include "omap-pcm.h"
+#include "omap-abe-priv.h"
+#include "mcbsp.h"
+#include "omap-mcbsp.h"
+#include "omap-dmic.h"
 #include "../codecs/twl6040.h"
 
-struct abe_twl6040 {
+struct omap_abe_data {
        int     jack_detection; /* board can detect jack events */
        int     mclk_freq;      /* MCLK frequency speed for twl6040 */
+       int     has_abe;
+       int     has_dmic;
+       int twl6040_power_mode;
+       int mcbsp_cfg;
+       struct snd_soc_platform *abe_platform;
+       struct snd_soc_codec *twl_codec;
+       struct i2c_client *tps6130x;
+       struct i2c_adapter *adapter;
 
        struct platform_device *dmic_codec_dev;
+       struct platform_device *spdif_codec_dev;
+};
+
+static struct i2c_board_info tps6130x_hwmon_info = {
+       I2C_BOARD_INFO("tps6130x", 0x33),
 };
 
-static int omap_abe_hw_params(struct snd_pcm_substream *substream,
+/* configure the TPS6130x Handsfree Boost Converter */
+static int omap_abe_tps6130x_configure(struct omap_abe_data *sdp4403)
+{
+       struct i2c_client *tps6130x = sdp4403->tps6130x;
+       u8 data[2];
+
+       data[0] = 0x01;
+       data[1] = 0x60;
+       if (i2c_master_send(tps6130x, data, 2) != 2)
+               dev_err(&tps6130x->dev, "I2C write to TPS6130x failed\n");
+
+       data[0] = 0x02;
+       if (i2c_master_send(tps6130x, data, 2) != 2)
+               dev_err(&tps6130x->dev, "I2C write to TPS6130x failed\n");
+       return 0;
+}
+
+static int mcpdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+                       struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *rate = hw_param_interval(params,
+                       SNDRV_PCM_HW_PARAM_RATE);
+
+       rate->min = rate->max = 96000;
+
+       return 0;
+}
+
+static int omap_abe_mcpdm_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_card *card = codec->card;
-       struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
+       struct snd_soc_card *card = rtd->card;
+       struct omap_abe_data *card_data = snd_soc_card_get_drvdata(card);
        int clk_id, freq;
        int ret;
 
        clk_id = twl6040_get_clk_id(rtd->codec);
        if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
-               freq = priv->mclk_freq;
+               freq = card_data->mclk_freq;
        else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
                freq = 32768;
-       else
+       else {
+               dev_err(card->dev, "invalid clock\n");
                return -EINVAL;
+       }
 
        /* set the codec mclk */
        ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq,
                                SND_SOC_CLOCK_IN);
-       if (ret) {
-               printk(KERN_ERR "can't set codec system clock\n");
+       if (ret)
+               dev_err(card->dev, "can't set codec system clock\n");
+
+       return ret;
+}
+
+static struct snd_soc_ops omap_abe_mcpdm_ops = {
+       .hw_params = omap_abe_mcpdm_hw_params,
+};
+
+static int omap_abe_mcbsp_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_card *card = rtd->card;
+       int ret;
+       unsigned int be_id, channels;
+
+       be_id = rtd->dai_link->be_id;
+
+       /* FM + MODEM + Bluetooth all use I2S config */
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               dev_err(card->dev, "can't set DAI %d configuration\n", be_id);
                return ret;
        }
+
+       if (params != NULL) {
+               struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+               /* Configure McBSP internal buffer usage */
+               /* this need to be done for playback and/or record */
+               channels = params_channels(params);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       omap_mcbsp_set_tx_threshold(mcbsp, channels);
+               else
+                       omap_mcbsp_set_rx_threshold(mcbsp, channels);
+       }
+
+       /* Set McBSP clock to external */
+       ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_FCLK,
+                                    64 * params_rate(params), SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               dev_err(card->dev, "can't set cpu system clock\n");
+
        return ret;
 }
 
-static struct snd_soc_ops omap_abe_ops = {
-       .hw_params = omap_abe_hw_params,
+static struct snd_soc_ops omap_abe_mcbsp_ops = {
+       .hw_params = omap_abe_mcbsp_hw_params,
 };
 
 static int omap_abe_dmic_hw_params(struct snd_pcm_substream *substream,
@@ -82,27 +174,57 @@ static int omap_abe_dmic_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_card *card = rtd->card;
        int ret = 0;
 
        ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_SYSCLK_PAD_CLKS,
                                     19200000, SND_SOC_CLOCK_IN);
        if (ret < 0) {
-               printk(KERN_ERR "can't set DMIC cpu system clock\n");
+               dev_err(card->dev, "can't set DMIC in system clock\n");
                return ret;
        }
        ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_ABE_DMIC_CLK, 2400000,
                                     SND_SOC_CLOCK_OUT);
-       if (ret < 0) {
-               printk(KERN_ERR "can't set DMIC output clock\n");
-               return ret;
-       }
-       return 0;
+       if (ret < 0)
+               dev_err(card->dev, "can't set DMIC output clock\n");
+
+       return ret;
 }
 
 static struct snd_soc_ops omap_abe_dmic_ops = {
        .hw_params = omap_abe_dmic_hw_params,
 };
 
+static int mcbsp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+                       struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *channels = hw_param_interval(params,
+                                               SNDRV_PCM_HW_PARAM_CHANNELS);
+       unsigned int be_id = rtd->dai_link->be_id;
+
+       if (be_id == OMAP_ABE_DAI_MM_FM || be_id == OMAP_ABE_DAI_BT_VX)
+               channels->min = 2;
+
+       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
+                                   SNDRV_PCM_FORMAT_S16_LE);
+       return 0;
+}
+
+static int dmic_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+                       struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *rate = hw_param_interval(params,
+                       SNDRV_PCM_HW_PARAM_RATE);
+
+       /* The ABE will covert the FE rate to 96k */
+       rate->min = rate->max = 96000;
+
+       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
+                                   SNDRV_PCM_FORMAT_S32_LE);
+       return 0;
+}
 /* Headset jack */
 static struct snd_soc_jack hs_jack;
 
@@ -118,23 +240,56 @@ static struct snd_soc_jack_pin hs_jack_pins[] = {
        },
 };
 
-/* SDP4430 machine DAPM */
+static int omap_abe_get_power_mode(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+       struct omap_abe_data *card_data = snd_soc_card_get_drvdata(card);
+
+       ucontrol->value.integer.value[0] = card_data->twl6040_power_mode;
+       return 0;
+}
+
+static int omap_abe_set_power_mode(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+       struct omap_abe_data *card_data = snd_soc_card_get_drvdata(card);
+
+       if (card_data->twl6040_power_mode == ucontrol->value.integer.value[0])
+               return 0;
+
+       card_data->twl6040_power_mode = ucontrol->value.integer.value[0];
+       omap_abe_pm_set_mode(card_data->abe_platform,
+                            card_data->twl6040_power_mode);
+
+       return 1;
+}
+
+static const char *power_texts[] = {"Low-Power", "High-Performance"};
+
+static const struct soc_enum omap_abe_enum[] = {
+       SOC_ENUM_SINGLE_EXT(2, power_texts),
+};
+
+static const struct snd_kcontrol_new omap_abe_controls[] = {
+       SOC_ENUM_EXT("TWL6040 Power Mode", omap_abe_enum[0],
+               omap_abe_get_power_mode, omap_abe_set_power_mode),
+};
+
+/* OMAP ABE TWL6040 machine DAPM */
 static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
        /* Outputs */
        SND_SOC_DAPM_HP("Headset Stereophone", NULL),
        SND_SOC_DAPM_SPK("Earphone Spk", NULL),
        SND_SOC_DAPM_SPK("Ext Spk", NULL),
        SND_SOC_DAPM_LINE("Line Out", NULL),
-       SND_SOC_DAPM_SPK("Vibrator", NULL),
 
        /* Inputs */
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
        SND_SOC_DAPM_MIC("Main Handset Mic", NULL),
        SND_SOC_DAPM_MIC("Sub Handset Mic", NULL),
        SND_SOC_DAPM_LINE("Line In", NULL),
-
-       /* Digital microphones */
-       SND_SOC_DAPM_MIC("Digital Mic", NULL),
 };
 
 static const struct snd_soc_dapm_route audio_map[] = {
@@ -150,21 +305,31 @@ static const struct snd_soc_dapm_route audio_map[] = {
        {"Line Out", NULL, "AUXL"},
        {"Line Out", NULL, "AUXR"},
 
-       {"Vibrator", NULL, "VIBRAL"},
-       {"Vibrator", NULL, "VIBRAR"},
-
        /* Routings for inputs */
-       {"HSMIC", NULL, "Headset Mic"},
-       {"Headset Mic", NULL, "Headset Mic Bias"},
+       {"HSMIC", NULL, "Headset Mic Bias"},
+       {"Headset Mic Bias", NULL, "Headset Mic"},
 
-       {"MAINMIC", NULL, "Main Handset Mic"},
-       {"Main Handset Mic", NULL, "Main Mic Bias"},
+       {"MAINMIC", NULL, "Main Mic Bias"},
+       {"Main Mic Bias", NULL, "Main Handset Mic"},
 
-       {"SUBMIC", NULL, "Sub Handset Mic"},
-       {"Sub Handset Mic", NULL, "Main Mic Bias"},
+       {"SUBMIC", NULL, "Main Mic Bias"},
+       {"Main Mic Bias", NULL, "Sub Handset Mic"},
 
        {"AFML", NULL, "Line In"},
        {"AFMR", NULL, "Line In"},
+
+       /* Connections between twl6040 and ABE */
+       {"Headset Playback", NULL, "PDM_DL1"},
+       {"Handsfree Playback", NULL, "PDM_DL2"},
+       {"PDM_UL1", NULL, "Capture"},
+
+       /* Bluetooth <--> ABE*/
+       {"omap-mcbsp.1 Playback", NULL, "BT_VX_DL"},
+       {"BT_VX_UL", NULL, "omap-mcbsp.1 Capture"},
+
+       /* FM <--> ABE */
+       {"omap-mcbsp.2 Playback", NULL, "MM_EXT_DL"},
+       {"MM_EXT_UL", NULL, "omap-mcbsp.2 Capture"},
 };
 
 static inline void twl6040_disconnect_pin(struct snd_soc_dapm_context *dapm,
@@ -174,26 +339,63 @@ static inline void twl6040_disconnect_pin(struct snd_soc_dapm_context *dapm,
                snd_soc_dapm_disable_pin(dapm, pin);
 }
 
+static int omap_abe_stream_event(struct snd_soc_dapm_context *dapm, int event)
+{
+       struct snd_soc_card *card = dapm->card;
+       struct omap_abe_data *card_data = snd_soc_card_get_drvdata(card);
+       int gain;
+
+       /*
+        * set DL1 gains dynamically according to the active output
+        * (Headset, Earpiece) and HSDAC power mode
+        */
+
+       gain = twl6040_get_dl1_gain(card_data->twl_codec) * 100;
+
+       omap_abe_set_dl1_gains(card_data->abe_platform, gain, gain);
+
+       return 0;
+}
+
 static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_card *card = codec->card;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       struct snd_soc_platform *platform = rtd->platform;
        struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev);
-       struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
-       int hs_trim;
+       struct omap_abe_data *card_data = snd_soc_card_get_drvdata(card);
+       u32 hsotrim, left_offset, right_offset, step_mV;
        int ret = 0;
 
-       /*
-        * Configure McPDM offset cancellation based on the HSOTRIM value from
-        * twl6040.
-        */
-       hs_trim = twl6040_get_trim_value(codec, TWL6040_TRIM_HSOTRIM);
-       omap_mcpdm_configure_dn_offsets(rtd, TWL6040_HSF_TRIM_LEFT(hs_trim),
-                                       TWL6040_HSF_TRIM_RIGHT(hs_trim));
+       card_data->twl_codec = codec;
+       card->dapm.stream_event = omap_abe_stream_event;
+
+       /* allow audio paths from the audio modem to run during suspend */
+       snd_soc_dapm_ignore_suspend(&card->dapm, "Ext Spk");
+       snd_soc_dapm_ignore_suspend(&codec->dapm, "AFML");
+       snd_soc_dapm_ignore_suspend(&codec->dapm, "AFMR");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
+       snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Stereophone");
+
+       /* DC offset cancellation computation only if ABE is enabled */
+       if (card_data->has_abe) {
+               hsotrim = twl6040_get_trim_value(codec, TWL6040_TRIM_HSOTRIM);
+               right_offset = TWL6040_HSF_TRIM_RIGHT(hsotrim);
+               left_offset = TWL6040_HSF_TRIM_LEFT(hsotrim);
+
+               step_mV = twl6040_get_hs_step_size(codec);
+               omap_abe_dc_set_hs_offset(platform, left_offset, right_offset,
+                                         step_mV);
+
+               /* ABE power control */
+               ret = snd_soc_add_card_controls(card, omap_abe_controls,
+                       ARRAY_SIZE(omap_abe_controls));
+               if (ret)
+                       return ret;
+       }
 
        /* Headset jack detection only if it is supported */
-       if (priv->jack_detection) {
+       if (card_data->jack_detection) {
                ret = snd_soc_jack_new(codec, "Headset Jack",
                                        SND_JACK_HEADSET, &hs_jack);
                if (ret)
@@ -201,68 +403,428 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
 
                ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
                                        hs_jack_pins);
+
                twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET);
        }
 
+       /*
+        * Should be only for SDP4430...
+        * but machine_is_* has been gone, so do it always
+        */
+       if (1) {
+               card_data->adapter = i2c_get_adapter(1);
+               if (!card_data->adapter) {
+                       dev_err(card->dev, "can't get i2c adapter\n");
+                       return -ENODEV;
+               }
+
+               card_data->tps6130x = i2c_new_device(card_data->adapter,
+                               &tps6130x_hwmon_info);
+               if (!card_data->tps6130x) {
+                       dev_err(card->dev, "can't add i2c device\n");
+                       i2c_put_adapter(card_data->adapter);
+                       return -ENODEV;
+               }
+
+               omap_abe_tps6130x_configure(card_data);
+       }
+
        /*
         * NULL pdata means we booted with DT. In this case the routing is
         * provided and the card is fully routed, no need to mark pins.
         */
+
        if (!pdata)
                return ret;
 
        /* Disable not connected paths if not used */
-       twl6040_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
-       twl6040_disconnect_pin(dapm, pdata->has_hf, "Ext Spk");
-       twl6040_disconnect_pin(dapm, pdata->has_ep, "Earphone Spk");
-       twl6040_disconnect_pin(dapm, pdata->has_aux, "Line Out");
-       twl6040_disconnect_pin(dapm, pdata->has_vibra, "Vibrator");
-       twl6040_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
-       twl6040_disconnect_pin(dapm, pdata->has_mainmic, "Main Handset Mic");
-       twl6040_disconnect_pin(dapm, pdata->has_submic, "Sub Handset Mic");
-       twl6040_disconnect_pin(dapm, pdata->has_afm, "Line In");
+       twl6040_disconnect_pin(&codec->dapm, pdata->has_hs, "Headset Stereophone");
+       twl6040_disconnect_pin(&codec->dapm, pdata->has_hf, "Ext Spk");
+       twl6040_disconnect_pin(&codec->dapm, pdata->has_ep, "Earphone Spk");
+       twl6040_disconnect_pin(&codec->dapm, pdata->has_aux, "Line Out");
+       twl6040_disconnect_pin(&codec->dapm, pdata->has_vibra, "Vibrator");
+       twl6040_disconnect_pin(&codec->dapm, pdata->has_hsmic, "Headset Mic");
+       twl6040_disconnect_pin(&codec->dapm, pdata->has_mainmic, "Main Handset Mic");
+       twl6040_disconnect_pin(&codec->dapm, pdata->has_submic, "Sub Handset Mic");
+       twl6040_disconnect_pin(&codec->dapm, pdata->has_afm, "Line In");
 
        return ret;
 }
 
+static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
+       SND_SOC_DAPM_MIC("Digital Mic 0", NULL),
+       SND_SOC_DAPM_MIC("Digital Mic 1", NULL),
+       SND_SOC_DAPM_MIC("Digital Mic 2", NULL),
+};
+
 static const struct snd_soc_dapm_route dmic_audio_map[] = {
-       {"DMic", NULL, "Digital Mic"},
-       {"Digital Mic", NULL, "Digital Mic1 Bias"},
+       /* Digital Mics: DMic0, DMic1, DMic2 with bias */
+       {"DMIC0", NULL, "omap-dmic-abe.0 Capture"},
+       {"omap-dmic-abe.0 Capture", NULL, "Digital Mic1 Bias"},
+       {"Digital Mic1 Bias", NULL, "Digital Mic 0"},
+
+       {"DMIC1", NULL, "omap-dmic-abe.1 Capture"},
+       {"omap-dmic-abe.1 Capture", NULL, "Digital Mic1 Bias"},
+       {"Digital Mic1 Bias", NULL, "Digital Mic 1"},
+
+       {"DMIC2", NULL, "omap-dmic-abe.2 Capture"},
+       {"omap-dmic-abe.2 Capture", NULL, "Digital Mic1 Bias"},
+       {"Digital Mic1 Bias", NULL, "Digital Mic 2"},
 };
 
 static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       struct snd_soc_card *card = rtd->card;
+       struct snd_soc_dapm_context *dapm = &card->dapm;
+       int ret;
+
+       ret = snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets,
+                               ARRAY_SIZE(dmic_dapm_widgets));
+       if (ret)
+               return ret;
 
-       return snd_soc_dapm_add_routes(dapm, dmic_audio_map,
+       ret = snd_soc_dapm_add_routes(dapm, dmic_audio_map,
                                ARRAY_SIZE(dmic_audio_map));
+       if (ret < 0)
+               return ret;
+
+       snd_soc_dapm_ignore_suspend(dapm, "Digital Mic 0");
+       snd_soc_dapm_ignore_suspend(dapm, "Digital Mic 1");
+       snd_soc_dapm_ignore_suspend(dapm, "Digital Mic 2");
+       return 0;
+}
+
+static int omap_abe_twl6040_dl2_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_platform *platform = rtd->platform;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_card *card = codec->card;
+       struct omap_abe_data *card_data = snd_soc_card_get_drvdata(card);
+       u32 hfotrim, left_offset, right_offset;
+
+       /* DC offset cancellation computation only if ABE is enabled */
+       if (card_data->has_abe) {
+               /* DC offset cancellation computation */
+               hfotrim = twl6040_get_trim_value(codec, TWL6040_TRIM_HFOTRIM);
+               right_offset = TWL6040_HSF_TRIM_RIGHT(hfotrim);
+               left_offset = TWL6040_HSF_TRIM_LEFT(hfotrim);
+
+               omap_abe_dc_set_hf_offset(platform, left_offset, right_offset);
+       }
+
+       return 0;
+}
+
+static int omap_abe_twl6040_fe_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_platform *platform = rtd->platform;
+       struct snd_soc_card *card = rtd->card;
+       struct omap_abe_data *card_data = snd_soc_card_get_drvdata(card);
+
+       card_data->abe_platform = platform;
+
+       return 0;
 }
 
 /* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link abe_twl6040_dai_links[] = {
-       {
-               .name = "TWL6040",
-               .stream_name = "TWL6040",
-               .cpu_dai_name = "omap-mcpdm",
-               .codec_dai_name = "twl6040-legacy",
-               .platform_name = "omap-pcm-audio",
-               .codec_name = "twl6040-codec",
-               .init = omap_abe_twl6040_init,
-               .ops = &omap_abe_ops,
-       },
-       {
-               .name = "DMIC",
-               .stream_name = "DMIC Capture",
-               .cpu_dai_name = "omap-dmic",
-               .codec_dai_name = "dmic-hifi",
-               .platform_name = "omap-pcm-audio",
-               .codec_name = "dmic-codec",
-               .init = omap_abe_dmic_init,
-               .ops = &omap_abe_dmic_ops,
-       },
+static struct snd_soc_dai_link legacy_dmic_dai = {
+       /* Legacy DMIC */
+       SND_SOC_DAI_CONNECT("Legacy DMIC", "dmic-codec", "omap-pcm-audio",
+                           "dmic-hifi", "omap-dmic"),
+       SND_SOC_DAI_OPS(&omap_abe_dmic_ops, omap_abe_dmic_init),
+};
+
+static struct snd_soc_dai_link legacy_mcpdm_dai = {
+       /* Legacy McPDM */
+       SND_SOC_DAI_CONNECT("Legacy McPDM", "twl6040-codec", "omap-pcm-audio",
+                           "twl6040-legacy", "mcpdm-legacy"),
+       SND_SOC_DAI_OPS(&omap_abe_mcpdm_ops, NULL),
+};
+
+static struct snd_soc_dai_link legacy_mcbsp_dai = {
+       /* Legacy McBSP */
+       SND_SOC_DAI_CONNECT("Legacy McBSP", "snd-soc-dummy", "omap-pcm-audio",
+                           "snd-soc-dummy-dai", "omap-mcbsp.2"),
+       SND_SOC_DAI_OPS(&omap_abe_mcbsp_ops, NULL),
+       SND_SOC_DAI_IGNORE_SUSPEND,
+};
+
+static struct snd_soc_dai_link legacy_mcasp_dai = {
+       /* Legacy SPDIF */
+       SND_SOC_DAI_CONNECT("Legacy SPDIF", "spdif-dit", "omap-pcm-audio",
+                           "dit-hifi", "omap-mcasp"),
+       SND_SOC_DAI_IGNORE_SUSPEND,
+};
+
+static struct snd_soc_dai_link abe_fe_dai[] = {
+
+/*
+ * Frontend DAIs - i.e. userspace visible interfaces (ALSA PCMs)
+ */
+{
+       /* ABE Media Capture */
+       SND_SOC_DAI_FE_LINK("OMAP ABE Media1", "omap-pcm-audio", "MultiMedia1"),
+       SND_SOC_DAI_FE_TRIGGER(SND_SOC_DPCM_TRIGGER_BESPOKE, SND_SOC_DPCM_TRIGGER_BESPOKE),
+       SND_SOC_DAI_OPS(NULL, omap_abe_twl6040_fe_init),
+       SND_SOC_DAI_IGNORE_PMDOWN,
+},
+{
+       /* ABE Media Capture */
+       SND_SOC_DAI_FE_LINK("OMAP ABE Media2", "omap-pcm-audio", "MultiMedia2"),
+       SND_SOC_DAI_FE_TRIGGER(SND_SOC_DPCM_TRIGGER_BESPOKE, SND_SOC_DPCM_TRIGGER_BESPOKE),
+},
+{
+       /* ABE Voice */
+       SND_SOC_DAI_FE_LINK("OMAP ABE Voice", "omap-pcm-audio", "Voice"),
+       SND_SOC_DAI_FE_TRIGGER(SND_SOC_DPCM_TRIGGER_BESPOKE, SND_SOC_DPCM_TRIGGER_BESPOKE),
+},
+{
+       /* ABE Tones */
+       SND_SOC_DAI_FE_LINK("OMAP ABE Tones", "omap-pcm-audio", "Tones"),
+       SND_SOC_DAI_FE_TRIGGER(SND_SOC_DPCM_TRIGGER_BESPOKE, SND_SOC_DPCM_TRIGGER_BESPOKE),
+},
+{
+       /* MODEM */
+       SND_SOC_DAI_FE_LINK("OMAP ABE MODEM", "aess", "MODEM"),
+       SND_SOC_DAI_FE_TRIGGER(SND_SOC_DPCM_TRIGGER_BESPOKE, SND_SOC_DPCM_TRIGGER_BESPOKE),
+       SND_SOC_DAI_OPS(NULL, omap_abe_twl6040_fe_init),
+       SND_SOC_DAI_IGNORE_SUSPEND, SND_SOC_DAI_IGNORE_PMDOWN,
+       SND_SOC_DAI_LINK_NO_HOST,
+},
+{
+       /* Low power ping - pong */
+       SND_SOC_DAI_FE_LINK("OMAP ABE Media LP", "aess", "MultiMedia1 LP"),
+       SND_SOC_DAI_FE_TRIGGER(SND_SOC_DPCM_TRIGGER_BESPOKE, SND_SOC_DPCM_TRIGGER_BESPOKE),
+},
+};
+
+/*
+ * Backend DAIs - i.e. dynamically matched interfaces, invisible to userspace.
+ * Matched to above interfaces at runtime, based upon use case.
+ */
+
+static struct snd_soc_dai_link abe_be_mcpdm_dai[] = {
+{
+       /* McPDM DL1 - Headset */
+       SND_SOC_DAI_CONNECT("McPDM-DL1", "twl6040-codec", "aess",
+                           "twl6040-dl1", "mcpdm-dl1"),
+       SND_SOC_DAI_BE_LINK(OMAP_ABE_DAI_PDM_DL1, mcpdm_be_hw_params_fixup),
+       SND_SOC_DAI_OPS(&omap_abe_mcpdm_ops, omap_abe_twl6040_init),
+       SND_SOC_DAI_IGNORE_SUSPEND, SND_SOC_DAI_IGNORE_PMDOWN,
+},
+{
+       /* McPDM UL1 - Analog Capture */
+       SND_SOC_DAI_CONNECT("McPDM-UL1", "twl6040-codec", "aess",
+                           "twl6040-ul", "mcpdm-ul1"),
+       SND_SOC_DAI_BE_LINK(OMAP_ABE_DAI_PDM_UL, mcpdm_be_hw_params_fixup),
+       SND_SOC_DAI_OPS(&omap_abe_mcpdm_ops, NULL),
+       SND_SOC_DAI_IGNORE_SUSPEND, SND_SOC_DAI_IGNORE_PMDOWN,
+},
+{
+       /* McPDM DL2 - Handsfree */
+       SND_SOC_DAI_CONNECT("McPDM-DL2", "twl6040-codec", "aess",
+                           "twl6040-dl2", "mcpdm-dl2"),
+       SND_SOC_DAI_BE_LINK(OMAP_ABE_DAI_PDM_DL2, mcpdm_be_hw_params_fixup),
+       SND_SOC_DAI_OPS(&omap_abe_mcpdm_ops, omap_abe_twl6040_dl2_init),
+       SND_SOC_DAI_IGNORE_SUSPEND, SND_SOC_DAI_IGNORE_PMDOWN,
+},
+};
+
+static struct snd_soc_dai_link abe_be_mcbsp1_dai = {
+       /* McBSP 1 - Bluetooth */
+       SND_SOC_DAI_CONNECT("McBSP-1", "snd-soc-dummy", "aess",
+                           "snd-soc-dummy-dai", "omap-mcbsp.1"),
+       SND_SOC_DAI_BE_LINK(OMAP_ABE_DAI_BT_VX, mcbsp_be_hw_params_fixup),
+       SND_SOC_DAI_OPS(&omap_abe_mcbsp_ops, NULL),
+       SND_SOC_DAI_IGNORE_SUSPEND, SND_SOC_DAI_IGNORE_PMDOWN,
+};
+
+static struct snd_soc_dai_link abe_be_mcbsp2_dai = {
+       /* McBSP 2 - MODEM or FM */
+       SND_SOC_DAI_CONNECT("McBSP-2", "snd-soc-dummy", "aess",
+                           "snd-soc-dummy-dai", "omap-mcbsp.2"),
+       SND_SOC_DAI_BE_LINK(OMAP_ABE_DAI_MM_FM, mcbsp_be_hw_params_fixup),
+       SND_SOC_DAI_OPS(&omap_abe_mcbsp_ops, NULL),
+       SND_SOC_DAI_IGNORE_SUSPEND, SND_SOC_DAI_IGNORE_PMDOWN,
+};
+
+static struct snd_soc_dai_link abe_be_dmic_dai[] = {
+{
+       /* DMIC0 */
+       SND_SOC_DAI_CONNECT("DMIC-0", "dmic-codec", "aess",
+                           "dmic-hifi", "omap-dmic-abe-dai-0"),
+       SND_SOC_DAI_BE_LINK(OMAP_ABE_DAI_DMIC0, dmic_be_hw_params_fixup),
+       SND_SOC_DAI_OPS(&omap_abe_dmic_ops, NULL),
+},
+{
+       /* DMIC1 */
+       SND_SOC_DAI_CONNECT("DMIC-1", "dmic-codec", "aess",
+                           "dmic-hifi", "omap-dmic-abe-dai-1"),
+       SND_SOC_DAI_BE_LINK(OMAP_ABE_DAI_DMIC1, dmic_be_hw_params_fixup),
+       SND_SOC_DAI_OPS(&omap_abe_dmic_ops, NULL),
+},
+{
+       /* DMIC2 */
+       SND_SOC_DAI_CONNECT("DMIC-2", "dmic-codec", "aess",
+                           "dmic-hifi", "omap-dmic-abe-dai-2"),
+       SND_SOC_DAI_BE_LINK(OMAP_ABE_DAI_DMIC2, dmic_be_hw_params_fixup),
+       SND_SOC_DAI_OPS(&omap_abe_dmic_ops, NULL),
+},
 };
 
+/* TODO: Peter - this will need some logic for DTS DAI link creation */
+static int omap_abe_add_dai_links(struct snd_soc_card *card,
+       struct omap_abe_twl6040_data *pdata)
+{
+       struct omap_abe_data *card_data = snd_soc_card_get_drvdata(card);
+       struct device_node *node = card->dev->of_node;
+       int ret, i;
+
+
+       if (node) {
+               struct device_node *dai_node, *aess_node;
+
+               aess_node = of_parse_phandle(node, "ti,aess", 0);
+               if (!aess_node) {
+                       dev_err(card->dev, "AESS node is not provided\n");
+                       return -EINVAL;
+               }
+
+               for (i = 4; i < ARRAY_SIZE(abe_fe_dai); i++) {
+                       abe_fe_dai[i].platform_name  = NULL;
+                       abe_fe_dai[i].platform_of_node = aess_node;
+               }
+
+               dai_node = of_parse_phandle(node, "ti,mcpdm", 0);
+               if (!dai_node) {
+                               dev_err(card->dev, "McPDM node is not provided\n");
+                               return -EINVAL;
+               }
+
+               for (i = 0; i < ARRAY_SIZE(abe_be_mcpdm_dai); i++) {
+                       abe_be_mcpdm_dai[i].platform_name  = NULL;
+                       abe_be_mcpdm_dai[i].platform_of_node = aess_node;
+               }
+
+               for (i = 0; i < ARRAY_SIZE(abe_be_dmic_dai); i++) {
+                       abe_be_dmic_dai[i].platform_name  = NULL;
+                       abe_be_dmic_dai[i].platform_of_node = aess_node;
+               }
+
+               dai_node = of_parse_phandle(node, "ti,mcbsp1", 0);
+               if (!dai_node) {
+                       dev_err(card->dev,"McBSP1 node is not provided\n");
+                       return -EINVAL;
+               }
+               abe_be_mcbsp1_dai.cpu_dai_name  = NULL;
+               abe_be_mcbsp1_dai.cpu_of_node = dai_node;
+               abe_be_mcbsp1_dai.platform_name  = NULL;
+               abe_be_mcbsp1_dai.platform_of_node = aess_node;
+
+               dai_node = of_parse_phandle(node, "ti,mcbsp2", 0);
+               if (!dai_node) {
+                       dev_err(card->dev,"McBSP2 node is not provided\n");
+                       return -EINVAL;
+               }
+               abe_be_mcbsp2_dai.cpu_dai_name  = NULL;
+               abe_be_mcbsp2_dai.cpu_of_node = dai_node;
+               abe_be_mcbsp2_dai.platform_name  = NULL;
+               abe_be_mcbsp2_dai.platform_of_node = aess_node;
+       }
+
+       /* Add the ABE FEs */
+       ret = snd_soc_card_new_dai_links(card, abe_fe_dai,
+               ARRAY_SIZE(abe_fe_dai));
+       if (ret < 0)
+               return ret;
+
+       /* McPDM BEs */
+       ret = snd_soc_card_new_dai_links(card, abe_be_mcpdm_dai,
+               ARRAY_SIZE(abe_be_mcpdm_dai));
+       if (ret < 0)
+               return ret;
+
+       /* McBSP1 BEs */
+       ret = snd_soc_card_new_dai_links(card, &abe_be_mcbsp1_dai, 1);
+       if (ret < 0)
+               return ret;
+
+       /* McBSP2 BEs */
+       ret = snd_soc_card_new_dai_links(card, &abe_be_mcbsp2_dai, 1);
+       if (ret < 0)
+               return ret;
+       /* DMIC BEs */
+       if (card_data->has_dmic) {
+               ret = snd_soc_card_new_dai_links(card, abe_be_dmic_dai,
+                       ARRAY_SIZE(abe_be_dmic_dai));
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/* TODO: Peter - this will need some logic for DTS DAI link creation */
+static int omap_abe_add_legacy_dai_links(struct snd_soc_card *card,
+       struct omap_abe_twl6040_data *pdata)
+{
+       struct omap_abe_data *card_data = snd_soc_card_get_drvdata(card);
+       struct device_node *node = card->dev->of_node;
+       struct device_node *dai_node;
+       int has_mcasp = 1;
+       int ret;
+
+       if (node) {
+               dai_node = of_parse_phandle(node, "ti,mcpdm", 0);
+               if (!dai_node) {
+                               dev_err(card->dev, "McPDM node is not provided\n");
+                               return -EINVAL;
+               }
+
+               dai_node = of_parse_phandle(node, "ti,mcbsp2", 0);
+               if (!dai_node) {
+                       dev_err(card->dev,"McBSP2 node is not provided\n");
+                       return -EINVAL;
+               }
+               legacy_mcbsp_dai.cpu_dai_name  = NULL;
+               legacy_mcbsp_dai.cpu_of_node = dai_node;
+
+               dai_node = of_parse_phandle(node, "ti,mcasp", 0);
+               if (!dai_node) {
+                       dev_warn(card->dev,"McASP node is not provided\n");
+                       has_mcasp = 0;
+               } else {
+                       legacy_mcasp_dai.cpu_dai_name  = NULL;
+                       legacy_mcasp_dai.cpu_of_node = dai_node;
+               }
+       }
+       /* Add the Legacy McPDM */
+       ret = snd_soc_card_new_dai_links(card, &legacy_mcpdm_dai, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Add the Legacy McBSP */
+       ret = snd_soc_card_new_dai_links(card, &legacy_mcbsp_dai, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Add the Legacy McASP */
+       if (has_mcasp) {
+               ret = snd_soc_card_new_dai_links(card, &legacy_mcasp_dai, 1);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Add the Legacy DMICs */
+       if (card_data->has_dmic) {
+               ret = snd_soc_card_new_dai_links(card, &legacy_dmic_dai, 1);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
 /* Audio machine driver */
 static struct snd_soc_card omap_abe_card = {
        .owner = THIS_MODULE,
@@ -271,6 +833,7 @@ static struct snd_soc_card omap_abe_card = {
        .num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets),
        .dapm_routes = audio_map,
        .num_dapm_routes = ARRAY_SIZE(audio_map),
+
 };
 
 static int omap_abe_probe(struct platform_device *pdev)
@@ -278,17 +841,17 @@ static int omap_abe_probe(struct platform_device *pdev)
        struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev);
        struct device_node *node = pdev->dev.of_node;
        struct snd_soc_card *card = &omap_abe_card;
-       struct abe_twl6040 *priv;
-       int num_links = 0;
-       int ret = 0;
+       struct omap_abe_data *card_data;
+       int ret;
 
        card->dev = &pdev->dev;
 
-       priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL);
-       if (priv == NULL)
+       card_data = devm_kzalloc(&pdev->dev, sizeof(*card_data), GFP_KERNEL);
+       if (card_data == NULL)
                return -ENOMEM;
 
-       priv->dmic_codec_dev = ERR_PTR(-EINVAL);
+       card_data->dmic_codec_dev = ERR_PTR(-EINVAL);
+       card_data->spdif_codec_dev = ERR_PTR(-EINVAL);
 
        if (node) {
                struct device_node *dai_node;
@@ -311,31 +874,46 @@ static int omap_abe_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev, "McPDM node is not provided\n");
                        return -EINVAL;
                }
-               abe_twl6040_dai_links[0].cpu_dai_name  = NULL;
-               abe_twl6040_dai_links[0].cpu_of_node = dai_node;
+
+               dai_node = of_parse_phandle(node, "ti,aess", 0);
+               if (dai_node) {
+                       card_data->has_abe = 1;
+               } else {
+                       dev_dbg(&pdev->dev, "AESS node is not provided\n");
+                       card_data->has_abe = 0;
+               }
 
                dai_node = of_parse_phandle(node, "ti,dmic", 0);
                if (dai_node) {
-                       num_links = 2;
-                       abe_twl6040_dai_links[1].cpu_dai_name  = NULL;
-                       abe_twl6040_dai_links[1].cpu_of_node = dai_node;
+                       card_data->has_dmic = 1;
 
-                       priv->dmic_codec_dev = platform_device_register_simple(
+                       card_data->dmic_codec_dev =
+                                       platform_device_register_simple(
                                                "dmic-codec", -1, NULL, 0);
-                       if (IS_ERR(priv->dmic_codec_dev)) {
+                       if (IS_ERR(card_data->dmic_codec_dev)) {
                                dev_err(&pdev->dev,
                                        "Can't instantiate dmic-codec\n");
-                               return PTR_ERR(priv->dmic_codec_dev);
+                               return PTR_ERR(card_data->dmic_codec_dev);
                        }
                } else {
-                       num_links = 1;
+                       dev_dbg(&pdev->dev, "DMIC node is not provided\n");
+                       card_data->has_dmic = 0;
+               }
+
+               card_data->spdif_codec_dev = platform_device_register_simple(
+                                               "spdif-dit", -1, NULL, 0);
+               if (IS_ERR(card_data->spdif_codec_dev)) {
+                       dev_err(&pdev->dev,
+                               "Can't instantiate spdif-dit\n");
+                       ret = PTR_ERR(card_data->spdif_codec_dev);
+                       goto err_dmic_unregister;
                }
 
-               priv->jack_detection = of_property_read_bool(node,
-                                                          "ti,jack-detection");
+               of_property_read_u32(node, "ti,jack-detection",
+                                    &card_data->jack_detection);
                of_property_read_u32(node, "ti,mclk-freq",
-                                    &priv->mclk_freq);
-               if (!priv->mclk_freq) {
+                                    &card_data->mclk_freq);
+               if (!card_data->mclk_freq) {
                        dev_err(&pdev->dev, "MCLK frequency not provided\n");
                        ret = -EINVAL;
                        goto err_unregister;
@@ -350,55 +928,66 @@ static int omap_abe_probe(struct platform_device *pdev)
                        return -ENODEV;
                }
 
-               if (pdata->has_dmic)
-                       num_links = 2;
-               else
-                       num_links = 1;
-
-               priv->jack_detection = pdata->jack_detection;
-               priv->mclk_freq = pdata->mclk_freq;
+               card_data->jack_detection = pdata->jack_detection;
+               card_data->mclk_freq = pdata->mclk_freq;
+               card_data->has_abe = pdata->has_abe;
+               card_data->has_dmic = pdata->has_dmic;
        } else {
                dev_err(&pdev->dev, "Missing pdata\n");
                return -ENODEV;
        }
 
-
-       if (!priv->mclk_freq) {
+       if (!card_data->mclk_freq) {
                dev_err(&pdev->dev, "MCLK frequency missing\n");
-               ret = -ENODEV;
+               ret = -EINVAL;
                goto err_unregister;
        }
 
-       card->dai_link = abe_twl6040_dai_links;
-       card->num_links = num_links;
+       snd_soc_card_set_drvdata(card, card_data);
 
-       snd_soc_card_set_drvdata(card, priv);
+       if (card_data->has_abe) {
+               ret = omap_abe_add_dai_links(card, pdata);
+               if (ret < 0)
+                       goto err_unregister;
+       }
+
+       ret = omap_abe_add_legacy_dai_links(card, pdata);
+       if (ret < 0)
+               goto err_unregister;
 
        ret = snd_soc_register_card(card);
        if (ret) {
-               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
-                       ret);
+               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
                goto err_unregister;
        }
 
-       return 0;
+       return ret;
 
 err_unregister:
-       if (!IS_ERR(priv->dmic_codec_dev))
-               platform_device_unregister(priv->dmic_codec_dev);
+       if (!IS_ERR(card_data->spdif_codec_dev))
+               platform_device_unregister(card_data->spdif_codec_dev);
+err_dmic_unregister:
+       if (!IS_ERR(card_data->dmic_codec_dev))
+               platform_device_unregister(card_data->dmic_codec_dev);
 
+       snd_soc_card_reset_dai_links(card);
        return ret;
 }
 
 static int omap_abe_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
-       struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
+       struct omap_abe_data *card_data = snd_soc_card_get_drvdata(card);
 
        snd_soc_unregister_card(card);
-
-       if (!IS_ERR(priv->dmic_codec_dev))
-               platform_device_unregister(priv->dmic_codec_dev);
+       i2c_unregister_device(card_data->tps6130x);
+       i2c_put_adapter(card_data->adapter);
+
+       if (!IS_ERR(card_data->dmic_codec_dev))
+               platform_device_unregister(card_data->dmic_codec_dev);
+       if (!IS_ERR(card_data->spdif_codec_dev))
+               platform_device_unregister(card_data->spdif_codec_dev);
+       snd_soc_card_reset_dai_links(card);
 
        return 0;
 }
index ba49ccd9eed9149f234907c6efb66af5587328e6..6e5c7b8a9678ff2fc3410f1d1a92f0186714924b 100644 (file)
 #include "omap-pcm.h"
 #include "omap-dmic.h"
 
+#define OMAP_DMIC_LEGACY_MODE  0x0
+#define OMAP_DMIC_ABE_MODE     0x1
+
+#define OMAP_DMIC_DAI_MODE_MASK        0x0f
+
 struct omap_dmic {
        struct device *dev;
        void __iomem *io_base;
@@ -53,7 +58,9 @@ struct omap_dmic {
        int sysclk;
        int threshold;
        u32 ch_enabled;
-       bool active;
+       int active;
+       bool abe_mode;
+       int running;
        struct mutex mutex;
 };
 
@@ -94,7 +101,6 @@ static inline void omap_dmic_stop(struct omap_dmic *dmic)
        /* Disable DMA request generation */
        omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_CLR_REG,
                        OMAP_DMIC_DMA_ENABLE);
-
 }
 
 static inline int dmic_is_enabled(struct omap_dmic *dmic)
@@ -107,14 +113,25 @@ static int omap_dmic_dai_startup(struct snd_pcm_substream *substream,
                                  struct snd_soc_dai *dai)
 {
        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
+       int dai_abe_mode = dai->id & OMAP_DMIC_DAI_MODE_MASK;
        int ret = 0;
 
        mutex_lock(&dmic->mutex);
 
-       if (!dai->active)
-               dmic->active = 1;
-       else
-               ret = -EBUSY;
+       if (!dmic->active++) {
+               dmic->abe_mode = dai_abe_mode;
+               /* DMIC FIFO configuration */
+               if (dmic->abe_mode == OMAP_DMIC_LEGACY_MODE)
+                       dmic->threshold = OMAP_DMIC_THRES_MAX - 3;
+               else
+                       dmic->threshold = 2;
+       } else if (dmic->abe_mode != dai_abe_mode) {
+               dev_err(dmic->dev, "Trying %s, while DMIC is in %s.\n",
+                       dai_abe_mode ? "ABE mode" : "Legacy mode",
+                       dmic->abe_mode ? "ABE mode" : "Legacy mode");
+               dmic->active--;
+               ret = -EINVAL;
+       }
 
        mutex_unlock(&dmic->mutex);
 
@@ -129,8 +146,7 @@ static void omap_dmic_dai_shutdown(struct snd_pcm_substream *substream,
 
        mutex_lock(&dmic->mutex);
 
-       if (!dai->active)
-               dmic->active = 0;
+       dmic->active--;
 
        mutex_unlock(&dmic->mutex);
 }
@@ -204,7 +220,7 @@ static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream,
 {
        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
        struct omap_pcm_dma_data *dma_data;
-       int channels;
+       int channels, select_channels;
 
        dmic->clk_div = omap_dmic_select_divider(dmic, params_rate(params));
        if (dmic->clk_div < 0) {
@@ -215,7 +231,12 @@ static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream,
 
        dmic->ch_enabled = 0;
        channels = params_channels(params);
-       switch (channels) {
+       if (dmic->abe_mode == OMAP_DMIC_LEGACY_MODE)
+               select_channels = channels;
+       else
+               select_channels = 6;
+
+       switch (select_channels) {
        case 6:
                dmic->ch_enabled |= OMAP_DMIC_UP3_ENABLE;
        case 4:
@@ -241,6 +262,10 @@ static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream,
        struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
        u32 ctrl;
 
+       /* Do not alter the configuration runtime */
+       if (dmic_is_enabled(dmic))
+               return 0;
+
        /* Configure uplink threshold */
        omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL_REG, dmic->threshold);
 
@@ -271,10 +296,12 @@ static int omap_dmic_dai_trigger(struct snd_pcm_substream *substream,
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               omap_dmic_start(dmic);
+               if (!dmic->running++)
+                       omap_dmic_start(dmic);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
-               omap_dmic_stop(dmic);
+               if (!--dmic->running)
+                       omap_dmic_stop(dmic);
                break;
        default:
                break;
@@ -420,8 +447,6 @@ static int omap_dmic_probe(struct snd_soc_dai *dai)
        omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, 0x00);
        pm_runtime_put_sync(dmic->dev);
 
-       /* Configure DMIC threshold value */
-       dmic->threshold = OMAP_DMIC_THRES_MAX - 3;
        return 0;
 }
 
@@ -434,8 +459,15 @@ static int omap_dmic_remove(struct snd_soc_dai *dai)
        return 0;
 }
 
-static struct snd_soc_dai_driver omap_dmic_dai = {
+#define DMIC_LEGACY_DAI                (OMAP_DMIC_LEGACY_MODE | (0 << 4))
+#define DMIC_ABE_DAI_1         (OMAP_DMIC_ABE_MODE | (1 << 4))
+#define DMIC_ABE_DAI_2         (OMAP_DMIC_ABE_MODE | (2 << 4))
+#define DMIC_ABE_DAI_3         (OMAP_DMIC_ABE_MODE | (3 << 4))
+
+static struct snd_soc_dai_driver omap_dmic_dai[] = {
+{
        .name = "omap-dmic",
+       .id     = DMIC_LEGACY_DAI,
        .probe = omap_dmic_probe,
        .remove = omap_dmic_remove,
        .capture = {
@@ -446,6 +478,43 @@ static struct snd_soc_dai_driver omap_dmic_dai = {
                .sig_bits = 24,
        },
        .ops = &omap_dmic_dai_ops,
+},
+{
+       .name = "omap-dmic-abe-dai-0",
+       .id     = DMIC_ABE_DAI_1,
+       .capture = {
+               .stream_name = "omap-dmic-abe.0 Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+               .formats = SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .ops = &omap_dmic_dai_ops,
+},
+{
+       .name = "omap-dmic-abe-dai-1",
+       .id     = DMIC_ABE_DAI_2,
+       .capture = {
+               .stream_name = "omap-dmic-abe.1 Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+               .formats = SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .ops = &omap_dmic_dai_ops,
+},
+{
+       .name = "omap-dmic-abe-dai-2",
+       .id     = DMIC_ABE_DAI_3,
+       .capture = {
+               .stream_name = "omap-dmic-abe.2 Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+               .formats = SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .ops = &omap_dmic_dai_ops,
+},
 };
 
 static int asoc_dmic_probe(struct platform_device *pdev)
@@ -507,7 +576,8 @@ static int asoc_dmic_probe(struct platform_device *pdev)
                goto err_put_clk;
        }
 
-       ret = snd_soc_register_dai(&pdev->dev, &omap_dmic_dai);
+       ret = snd_soc_register_dais(&pdev->dev, omap_dmic_dai,
+                       ARRAY_SIZE(omap_dmic_dai));
        if (ret)
                goto err_put_clk;
 
@@ -522,7 +592,7 @@ static int asoc_dmic_remove(struct platform_device *pdev)
 {
        struct omap_dmic *dmic = platform_get_drvdata(pdev);
 
-       snd_soc_unregister_dai(&pdev->dev);
+       snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(omap_dmic_dai));
        clk_put(dmic->fclk);
 
        return 0;
index d4eaa92e518ec22af5c3751be13653dff5b268f9..1e32bf884a8aaec6b50e2c6dbde12d4f09c617a0 100644 (file)
  */
 
 #include <linux/module.h>
+#include <linux/of.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <asm/mach-types.h>
 #include <video/omapdss.h>
 
-#define DRV_NAME "omap-hdmi-audio"
+#define DRV_NAME "omap-hdmi-audio-card"
+
+struct hdmi_card_data {
+       struct platform_device *codec_pdev;
+};
 
 static struct snd_soc_dai_link omap_hdmi_dai = {
        .name = "HDMI",
        .stream_name = "HDMI",
-       .cpu_dai_name = "omap-hdmi-audio-dai",
+       .cpu_dai_name = "omap-hdmi-audio",
        .platform_name = "omap-pcm-audio",
        .codec_name = "hdmi-audio-codec",
        .codec_dai_name = "omap-hdmi-hifi",
@@ -48,32 +53,89 @@ static struct snd_soc_card snd_soc_omap_hdmi = {
 static int omap_hdmi_probe(struct platform_device *pdev)
 {
        struct snd_soc_card *card = &snd_soc_omap_hdmi;
+       struct device_node *node = pdev->dev.of_node;
+       struct hdmi_card_data *card_data;
        int ret;
 
        card->dev = &pdev->dev;
 
+       card_data = devm_kzalloc(&pdev->dev, sizeof(*card_data), GFP_KERNEL);
+       if (!card_data)
+               return -ENOMEM;
+
+       card_data->codec_pdev = ERR_PTR(-EINVAL);
+
+       /* for DT boot */
+       if (node) {
+               struct device_node *dev_node;
+
+               if (snd_soc_of_parse_card_name(card, "ti,model")) {
+                       dev_err(&pdev->dev, "Card name is not provided\n");
+                       return -ENODEV;
+               }
+
+               printk(KERN_ERR "card name is %s", card->name);
+
+               dev_node = of_parse_phandle(node, "ti,hdmi_audio", 0);
+               if (!dev_node) {
+                       dev_err(&pdev->dev, "hdmi node is not provided\n");
+                       return -EINVAL;
+               }
+
+               dev_node = of_parse_phandle(node, "ti,level_shifter", 0);
+               if (!dev_node) {
+                       dev_err(&pdev->dev, "level shifter node is not provided\n");
+                       return -EINVAL;
+               }
+               card_data->codec_pdev = platform_device_register_simple("hdmi-audio-codec",
+                                                           -1, NULL, 0);
+               if (IS_ERR(card_data->codec_pdev)) {
+                       dev_err(&pdev->dev,
+                               "Cannot instantiate hdmi-audio-codec\n");
+                       return PTR_ERR(card_data->codec_pdev);
+               }
+       }
+
+       snd_soc_card_set_drvdata(card, card_data);
        ret = snd_soc_register_card(card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
                card->dev = NULL;
-               return ret;
+               goto err_register_card;
        }
+
        return 0;
+
+err_register_card:
+       if (!IS_ERR(card_data->codec_pdev))
+               platform_device_unregister(card_data->codec_pdev);
+       return ret;
 }
 
 static int omap_hdmi_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct hdmi_card_data *card_data;
 
+       card_data = snd_soc_card_get_drvdata(card);
+       if (!IS_ERR(card_data->codec_pdev))
+               platform_device_unregister(card_data->codec_pdev);
        snd_soc_unregister_card(card);
        card->dev = NULL;
        return 0;
 }
 
+static const struct of_device_id omap_hdmi_of_match[] = {
+       {.compatible = "ti,omap-hdmi-tpd12s015-audio", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, omap_hdmi_of_match);
+
 static struct platform_driver omap_hdmi_driver = {
        .driver = {
                .name = DRV_NAME,
                .owner = THIS_MODULE,
+               .of_match_table = omap_hdmi_of_match,
        },
        .probe = omap_hdmi_probe,
        .remove = omap_hdmi_remove,
index 7ea24819d570ec912f60fc978497578bddeb753b..b65a57484d087c6615428268170585bc43233caa 100644 (file)
@@ -37,7 +37,7 @@
 #include "omap-pcm.h"
 #include "omap-hdmi.h"
 
-#define DRV_NAME "omap-hdmi-audio-dai"
+#define DRV_NAME "omap-hdmi-audio"
 
 struct hdmi_priv {
        struct omap_pcm_dma_data dma_params;
@@ -110,6 +110,8 @@ static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
        /*
         * fill the IEC-60958 channel status word
         */
+       /* initialize the word bytes */
+       memset(iec->status, 0, sizeof(iec->status));
 
        /* specify IEC-60958-3 (commercial use) */
        iec->status[0] &= ~IEC958_AES0_PROFESSIONAL;
@@ -281,8 +283,7 @@ static int omap_hdmi_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       hdmi_data->dma_params.port_addr =  hdmi_rsrc->start
-               + OMAP_HDMI_AUDIO_DMA_PORT;
+       hdmi_data->dma_params.port_addr =  hdmi_rsrc->start;
 
        hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_DMA, 0);
        if (!hdmi_rsrc) {
@@ -331,7 +332,7 @@ static int omap_hdmi_remove(struct platform_device *pdev)
        snd_soc_unregister_dai(&pdev->dev);
 
        if (hdmi_data == NULL) {
-               dev_err(&pdev->dev, "cannot obtain HDMi data\n");
+               dev_err(&pdev->dev, "cannot obtain HDMI data\n");
                return -ENODEV;
        }
 
index 6ad2bf4f269783e3d95513a19c3b8290368d8127..33d7a935876df155d5d989e260229b486c9e6e33 100644 (file)
@@ -25,8 +25,6 @@
 #ifndef __OMAP_HDMI_H__
 #define __OMAP_HDMI_H__
 
-#define OMAP_HDMI_AUDIO_DMA_PORT 0x8c
-
 #define OMAP_HDMI_RATES        (SNDRV_PCM_RATE_32000 | \
                                SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
                                SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
diff --git a/sound/soc/omap/omap-mcasp.c b/sound/soc/omap/omap-mcasp.c
new file mode 100644 (file)
index 0000000..84e16f0
--- /dev/null
@@ -0,0 +1,717 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI OMAP processor
+ *
+ * Multi-channel Audio Serial Port Driver
+ *
+ * Author: Jon Hunter <jon-hunter@ti.com>,
+ *         Dan Milea <dan.milea@ti.com>,
+ *
+ * Based upon McASP driver written for TI DaVinci
+ *
+ * Copyright:   (C) 2011  Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "omap-pcm.h"
+#include "omap-mcasp.h"
+
+/*
+ * McASP register definitions
+ */
+#define OMAP_MCASP_PID_REG             0x00
+#define OMAP_MCASP_SYSCONFIG_REG       0x04
+
+#define OMAP_MCASP_PFUNC_REG           0x10
+#define OMAP_MCASP_PDIR_REG            0x14
+#define OMAP_MCASP_PDOUT_REG           0x18
+#define OMAP_MCASP_PDIN_REG            0x1c
+#define OMAP_MCASP_PDSET_REG           0x1c
+#define OMAP_MCASP_PDCLR_REG           0x20
+
+#define OMAP_MCASP_GBLCTL_REG          0x44
+#define OMAP_MCASP_AMUTE_REG           0x48
+
+#define OMAP_MCASP_TXDITCTL_REG                0x50
+
+#define OMAP_MCASP_TXMASK_REG          0xa4
+#define OMAP_MCASP_TXFMT_REG           0xa8
+#define OMAP_MCASP_TXFMCTL_REG         0xac
+
+#define OMAP_MCASP_ACLKXCTL_REG                0xb0
+#define OMAP_MCASP_AHCLKXCTL_REG       0xb4
+#define OMAP_MCASP_TXTDM_REG           0xb8
+#define OMAP_MCASP_EVTCTLX_REG         0xbc
+
+#define OMAP_MCASP_TXSTAT_REG          0xc0
+#define OMAP_MCASP_TXSTAT_MASK         0x1ff
+
+#define OMAP_MCASP_TXTDMSLOT_REG       0xc4
+#define OMAP_MCASP_TXCLKCHK_REG                0xc8
+#define OMAP_MCASP_TXEVTCTL_REG                0xcc
+
+/* Left(even TDM Slot) Channel Status Register File */
+#define OMAP_MCASP_DITCSRA_REG 0x100
+/* Right(odd TDM slot) Channel Status Register File */
+#define OMAP_MCASP_DITCSRB_REG 0x118
+/* Left(even TDM slot) User Data Register File */
+#define OMAP_MCASP_DITUDRA_REG 0x130
+/* Right(odd TDM Slot) User Data Register File */
+#define OMAP_MCASP_DITUDRB_REG 0x148
+
+/* Serializer n Control Register */
+#define OMAP_MCASP_XRSRCTL0_REG        0x180
+
+/* Transmit Buffer for Serializer */
+#define OMAP_MCASP_TXBUF0_REG  0x200
+
+/*
+ * OMAP_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits
+ */
+#define AXR0           BIT(0)
+#define PFUNC_AMUTE    BIT(25)
+#define ACLKX          BIT(26)
+#define AHCLKX         BIT(27)
+#define AFSX           BIT(28)
+
+/*
+ * OMAP_MCASP_PDIR_REG - Pin Direction Register Bits
+ */
+#define AXR0           BIT(0)
+#define PDIR_AMUTE     BIT(25)
+#define ACLKX          BIT(26)
+#define AHCLKX         BIT(27)
+#define AFSX           BIT(28)
+
+/*
+ * OMAP_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits
+ */
+#define DITEN  BIT(0)  /* Transmit DIT mode enable/disable */
+#define VA     BIT(2)
+#define VB     BIT(3)
+
+/*
+ * OMAP_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits
+ */
+#define TXROT(val)     (val)
+#define TXROT_MASK     TXROT(0x7)
+#define TXSEL          BIT(3)
+#define TXSSZ(val)     (val<<4)
+#define TXSSZ_MASK     TXSSZ(0xf<<4)
+#define TXPAD(val)     (val<<13)
+#define TXORD          BIT(15)
+#define FSXDLY(val)    (val<<16)
+
+#define ROTATE_24      0x6
+#define SLOTSIZE_32    0xf
+
+/*
+ * OMAP_MCASP_TXFMCTL_REG -  Transmit Frame Control Register Bits
+ */
+#define FSXPOL         BIT(0)
+#define AFSXE          BIT(1)
+#define FSXDUR         BIT(4)
+#define FSXMOD(val)    (val<<7)
+
+/*
+ * OMAP_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits
+ */
+#define ACLKXDIV(val)  (val)
+#define ACLKXE         BIT(5)
+#define TX_ASYNC       BIT(6)
+
+/*
+ * OMAP_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
+ *     Register Bits
+ */
+#define AHCLKXDIV(val) (val)
+#define AHCLKXE                BIT(15)
+
+/*
+ * OMAP_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register bits
+ */
+#define EVTCTLX_XUNDRN         BIT(0)
+
+/*
+ * OMAP_MCASP_TXSTAT_REG - Transmit Status Register Bits
+ */
+#define TXSTAT_XUNDRN  (0x1 << 0)
+#define TXSTAT_XSYNCERR        (0x1 << 1)
+#define TXSTAT_XCKFAIL (0x1 << 2)
+#define TXSTAT_XDMSLOT (0x1 << 3)
+#define TXSTAT_XLAST   (0x1 << 4)
+#define TXSTAT_XDATA   (0x1 << 5)
+#define TXSTAT_XSTAFRM (0x1 << 6)
+#define TXSTAT_XDMAERR (0x1 << 7)
+#define TXSTAT_XERR    (0x1 << 8)
+
+/*
+ * OMAP_MCASP_XRSRCTL_BASE_REG -  Serializer Control Register Bits
+ */
+#define MODE(val)      (val)
+#define TXSTATE                BIT(4)
+
+/*
+ * OMAP_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration
+ */
+#define TXTDMS(n)      (1<<n)
+
+/*
+ * OMAP_MCASP_GBLCTL_REG -  Global Control Register Bits
+ */
+#define TXCLKRST       BIT(8)  /* Transmitter Clock Divider Reset */
+#define TXHCLKRST      BIT(9)  /* Transmitter High Frequency Clock Divider*/
+#define TXSERCLR       BIT(10) /* Transmit Serializer Clear */
+#define TXSMRST                BIT(11) /* Transmitter State Machine Reset */
+#define TXFSRST                BIT(12) /* Frame Sync Generator Reset */
+
+/*
+ * OMAP_MCASP_AMUTE_REG -  Mute Control Register Bits
+ */
+#define MUTENA(val)    (val)
+#define MUTEINPOL      BIT(2)
+#define MUTEINENA      BIT(3)
+#define MUTEIN         BIT(4)
+#define MUTEX          BIT(6)
+#define MUTEFSX                BIT(8)
+#define MUTEBADCLKX    BIT(10)
+#define MUTETXDMAERR   BIT(12)
+
+/*
+ * OMAP_MCASP_TXEVTCTL_REG - Transmitter DMA Event Control Register bits
+ */
+#define TXDATADMADIS   BIT(0)
+
+#define MCASP_ALLOWED_PPM      100
+
+/*
+ * OMAP_MCASP_DITCSRA_REG/OMAP_MCASP_DITCSRB_REG
+ */
+#define OMAP_MCASP_DITCSR_44100HZ      (0x0 << 24)
+#define OMAP_MCASP_DITCSR_48000HZ      (0x2 << 24)
+#define OMAP_MCASP_DITCSR_32000HZ      (0x3 << 24)
+#define OMAP_MCASP_DITCSR_22050HZ      (0x4 << 24)
+#define OMAP_MCASP_DITCSR_24000HZ      (0x6 << 24)
+#define OMAP_MCASP_DITCSR_88200HZ      (0x8 << 24)
+#define OMAP_MCASP_DITCSR_96000HZ      (0xA << 24)
+#define OMAP_MCASP_DITCSR_176400HZ     (0xC << 24)
+#define OMAP_MCASP_DITCSR_192000HZ     (0xE << 24)
+
+/*
+ * Stream DMA parameters
+ */
+static struct omap_pcm_dma_data omap_mcasp_dai_dma_params[] = {
+       {
+               .name = "Audio playback",
+               /* TODO: this might fail... We should get them as resource */
+               .port_addr = OMAP44XX_MCASP_DAT_BASE + OMAP_MCASP_TXBUF0_REG,
+       },
+};
+
+static inline void mcasp_set_bits(void __iomem *reg, u32 val)
+{
+       __raw_writel(__raw_readl(reg) | val, reg);
+}
+
+static inline void mcasp_clr_bits(void __iomem *reg, u32 val)
+{
+       __raw_writel((__raw_readl(reg) & ~(val)), reg);
+}
+
+static inline void mcasp_mod_bits(void __iomem *reg, u32 val, u32 mask)
+{
+       __raw_writel((__raw_readl(reg) & ~mask) | val, reg);
+}
+
+static inline void mcasp_set_reg(void __iomem *reg, u32 val)
+{
+       __raw_writel(val, reg);
+}
+
+static inline u32 mcasp_get_reg(void __iomem *reg)
+{
+       return (unsigned int)__raw_readl(reg);
+}
+
+static inline void mcasp_set_ctl_reg(void __iomem *regs, u32 val)
+{
+       int i = 0;
+
+       mcasp_set_bits(regs, val);
+
+       /* programming GBLCTL needs to read back from GBLCTL and verfiy */
+       /* loop count is to avoid the lock-up */
+       for (i = 0; i < 1000; i++) {
+               if ((mcasp_get_reg(regs) & val) == val)
+                       break;
+       }
+
+       if (i == 1000 && ((mcasp_get_reg(regs) & val) != val))
+               pr_err("GBLCTL write error\n");
+}
+
+static int mcasp_compute_clock_dividers(long fclk_rate, int tgt_sample_rate,
+                       u32 *out_div_lo, u32 *out_div_hi)
+{
+       /* Given a particular functional clock rate and a target audio sample
+        * rate, determine the proper values for the ACLKXCTL and AHCLKXCTL, the
+        * dividers which produce the high frequency transmit master clock and
+        * the transmit clock.
+        */
+       u32 divisor;
+       unsigned long ppm;
+       int sample_rate;
+       u32 i;
+       BUG_ON(!out_div_lo);
+       BUG_ON(!out_div_hi);
+
+       /* Start by making sure the fclk is divisible by 128 (the number of
+        * clocks present in a single S/PDIF frame.
+        */
+       if (fclk_rate & 0x7F)
+               return -EINVAL;
+
+       fclk_rate >>= 7;
+
+       /* rounded division: fclk_rate / tgt_sample_rate + 0.5 */
+       divisor = (2 * fclk_rate + tgt_sample_rate) / (2 * tgt_sample_rate);
+       if (!divisor)
+               return -EINVAL;
+
+       sample_rate = fclk_rate / divisor;
+
+       /* ppm calculation in two steps to avoid overflow */
+       ppm = abs(tgt_sample_rate - sample_rate);
+       ppm = (1000000 * ppm) / tgt_sample_rate;
+
+       if (ppm > MCASP_ALLOWED_PPM)
+               return -EINVAL;
+
+       /* At this point, divisor holds the product of the two divider values we
+        * need to use for ACLKXCTL and AHCLKXCTL.  ACLKXCTL holds a 5 bit
+        * divider [1, 32], while AHCLKXCTL holds a 12 bit divider [1, 4096].
+        * We need to make sure that we can factor divisor into two integers
+        * which will fit into these divider registers.  Find the largest 5-bit
+        * + 1 value which divides divisor and use that as our smaller divider.
+        * After removing this factor from divisor, if the result is <= 4096,
+        * then we have succeeded and will be able to produce the target sample
+        * rate.
+        */
+       for (i = 32; (i > 1) && (divisor % i); --i)
+               ; /* no body */
+
+       /* Make sure to subtract one, registers hold the value of the divider
+        * minus one (IOW, to divide by 5, the register gets programmed with the
+        * value 4. */
+       *out_div_lo = i - 1;
+       *out_div_hi = (divisor / i) - 1;
+
+       return (*out_div_hi <= 4096) ? 0 : -EINVAL;
+}
+
+static int omap_mcasp_start(struct omap_mcasp *mcasp)
+{
+       int i;
+       mcasp_set_ctl_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, TXHCLKRST);
+       mcasp_set_ctl_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, TXCLKRST);
+       mcasp_set_ctl_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, TXSERCLR);
+
+       /* Wait until the DMA has loaded the first sample into TXBUF before we
+        * let the TX state machine and frame sync generator out of reset. */
+       i = 0;
+       while (1) {
+               u32 reg = mcasp_get_reg(mcasp->base + OMAP_MCASP_TXSTAT_REG);
+               if (!(reg & TXSTAT_XDATA))
+                       break;
+
+               if (++i > 1000) {
+                       pr_err("Timeout waiting for DMA to load first sample of audio.\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       }
+
+       mcasp_set_ctl_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, TXSMRST);
+       mcasp_set_ctl_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, TXFSRST);
+       mcasp_clr_bits(mcasp->base + OMAP_MCASP_TXEVTCTL_REG, TXDATADMADIS);
+
+       /* enable IRQ sources */
+       mcasp_set_bits(mcasp->base + OMAP_MCASP_EVTCTLX_REG, EVTCTLX_XUNDRN);
+
+       return 0;
+}
+
+static void omap_mcasp_stop(struct omap_mcasp *mcasp)
+{
+       /* disable IRQ sources */
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_EVTCTLX_REG, 0);
+
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_GBLCTL_REG, 0);
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_TXSTAT_REG,
+                       OMAP_MCASP_TXSTAT_MASK);
+}
+
+/* S/PDIF */
+static int omap_mcasp_setup(struct omap_mcasp *mcasp, unsigned int rate)
+{
+       u32 aclkxdiv, ahclkxdiv, ditcsr;
+       int res;
+
+       /* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_TXFMCTL_REG,
+                                               AFSXE | FSXMOD(0x180));
+
+       /* Set the TX clock controls : div = 1 and internal */
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_ACLKXCTL_REG,
+                                               ACLKXE | TX_ASYNC);
+
+       /* Set the HS TX clock controls : div = 1 and internal */
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_AHCLKXCTL_REG, AHCLKXE);
+
+       /* The SPDIF bit clock is derived from the McASP functional clock.
+        * The McASP has two programmable clock dividers (aclkxdiv and
+        * ahclkxdiv) that are configured via the registers MCASP_ACLKXCTL
+        * and MCASP_AHCLKXCTL. For SPDIF the bit clock frequency should be
+        * 128 * sample rate freq. The dividers are defined as part of
+        * platform data as they are dependent upon the functional clock
+        * setting. Lookup the appropriate dividers for the sampling
+        * frequency that we are playing.
+        */
+       res = mcasp_compute_clock_dividers(clk_get_rate(mcasp->fclk),
+                               rate,
+                               &aclkxdiv,
+                               &ahclkxdiv);
+       if (res) {
+               dev_err(mcasp->dev,
+                       "%s: No valid McASP config for sampling rate (%d)!\n",
+                       __func__, rate);
+               return res;
+       }
+
+       switch (rate) {
+       case 22050:
+               ditcsr = OMAP_MCASP_DITCSR_22050HZ;
+               break;
+       case 24000:
+               ditcsr = OMAP_MCASP_DITCSR_24000HZ;
+               break;
+       case 32000:
+               ditcsr = OMAP_MCASP_DITCSR_32000HZ;
+               break;
+       case 44100:
+               ditcsr = OMAP_MCASP_DITCSR_44100HZ;
+               break;
+       case 48000:
+               ditcsr = OMAP_MCASP_DITCSR_48000HZ;
+               break;
+       case 88200:
+               ditcsr = OMAP_MCASP_DITCSR_88200HZ;
+               break;
+       case 96000:
+               ditcsr = OMAP_MCASP_DITCSR_96000HZ;
+               break;
+       case 176400:
+               ditcsr = OMAP_MCASP_DITCSR_176400HZ;
+               break;
+       case 192000:
+               ditcsr = OMAP_MCASP_DITCSR_192000HZ;
+               break;
+       default:
+               dev_err(mcasp->dev, "%s: Invalid sampling rate: %d\n",
+                       __func__, rate);
+               return -EINVAL;
+       }
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_DITCSRA_REG, ditcsr);
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_DITCSRB_REG, ditcsr);
+       mcasp_set_bits(mcasp->base + OMAP_MCASP_AHCLKXCTL_REG,
+                                       AHCLKXDIV(ahclkxdiv));
+       mcasp_set_bits(mcasp->base + OMAP_MCASP_ACLKXCTL_REG,
+                                       AHCLKXDIV(aclkxdiv));
+
+       /* Configure McASP formatter */
+       mcasp_mod_bits(mcasp->base + OMAP_MCASP_TXFMT_REG,
+                                       TXSSZ(SLOTSIZE_32), TXSSZ_MASK);
+       mcasp_mod_bits(mcasp->base + OMAP_MCASP_TXFMT_REG, TXROT(ROTATE_24),
+                                                       TXROT_MASK);
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_TXMASK_REG, 0xFFFF);
+
+       /* Set the TX tdm : for all the slots */
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_TXTDM_REG, 0xFFFFFFFF);
+
+       /* configure the serializer for transmit mode operation */
+       mcasp_set_bits(mcasp->base + OMAP_MCASP_XRSRCTL0_REG, MODE(1));
+
+       /* All PINS as McASP */
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_PFUNC_REG, 0);
+
+       mcasp_set_bits(mcasp->base + OMAP_MCASP_PDIR_REG, AXR0);
+
+       /* Enable the DIT */
+       mcasp_set_bits(mcasp->base + OMAP_MCASP_TXDITCTL_REG, DITEN);
+
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_TXSTAT_REG, 0xFF);
+
+       return 0;
+}
+
+static irqreturn_t omap_mcasp_irq_handler(int irq, void *data)
+{
+       struct omap_mcasp *mcasp = data;
+       u32 txstat;
+
+       txstat = mcasp_get_reg(mcasp->base + OMAP_MCASP_TXSTAT_REG);
+       if (txstat & TXSTAT_XUNDRN) {
+               dev_err(mcasp->dev, "%s: Underrun (0x%08x)\n", __func__,
+                       txstat);
+
+               /* Try to recover from this state */
+               spin_lock(&mcasp->lock);
+               if (likely(mcasp->stream_rate)) {
+                       dev_err(mcasp->dev, "%s: Trying to recover\n",
+                               __func__);
+                       omap_mcasp_stop(mcasp);
+                       omap_mcasp_setup(mcasp, mcasp->stream_rate);
+                       omap_mcasp_start(mcasp);
+               }
+               spin_unlock(&mcasp->lock);
+       }
+
+       mcasp_set_reg(mcasp->base + OMAP_MCASP_TXSTAT_REG, txstat);
+
+       return IRQ_HANDLED;
+}
+
+static int omap_mcasp_startup(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct omap_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+       pm_runtime_get_sync(mcasp->dev);
+
+       snd_soc_dai_set_dma_data(dai, substream,
+                                &omap_mcasp_dai_dma_params[substream->stream]);
+
+       return 0;
+}
+
+static void omap_mcasp_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct omap_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+       pm_runtime_put_sync(mcasp->dev);
+
+}
+
+static int omap_mcasp_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params,
+                                       struct snd_soc_dai *dai)
+{
+       struct omap_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+       omap_mcasp_stop(mcasp);
+
+       if (omap_mcasp_setup(mcasp, params_rate(params)) < 0)
+               return -EPERM;
+
+       return 0;
+}
+
+static int omap_mcasp_trigger(struct snd_pcm_substream *substream,
+                                    int cmd, struct snd_soc_dai *cpu_dai)
+{
+       struct omap_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&mcasp->lock, flags);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               mcasp->stream_rate = substream->runtime->rate;
+               ret = omap_mcasp_start(mcasp);
+               break;
+
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               mcasp->stream_rate = 0;
+               omap_mcasp_stop(mcasp);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       spin_unlock_irqrestore(&mcasp->lock, flags);
+
+       return ret;
+}
+
+static struct snd_soc_dai_ops omap_mcasp_dai_ops = {
+       .startup        = omap_mcasp_startup,
+       .shutdown       = omap_mcasp_shutdown,
+       .trigger        = omap_mcasp_trigger,
+       .hw_params      = omap_mcasp_hw_params,
+
+};
+
+#define MCASP_RATES    (SNDRV_PCM_RATE_32000 | \
+                        SNDRV_PCM_RATE_48000 | \
+                        SNDRV_PCM_RATE_96000 | \
+                        SNDRV_PCM_RATE_192000)
+
+static struct snd_soc_dai_driver omap_mcasp_dai = {
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 384,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               .rates = MCASP_RATES,
+       },
+       .ops = &omap_mcasp_dai_ops,
+};
+
+static int omap_mcasp_probe(struct platform_device *pdev)
+{
+       struct omap_mcasp *mcasp;
+       struct resource *res;
+       long fclk_rate;
+       int ret = 0;
+
+       mcasp = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcasp), GFP_KERNEL);
+       if (!mcasp) {
+               dev_err(&pdev->dev, "cannot allocate memory\n");
+               return -ENOMEM;
+       }
+
+       spin_lock_init(&mcasp->lock);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "axevt");
+       if (!res) {
+               dev_err(&pdev->dev, "no DMA resource\n");
+               return -ENODEV;
+       }
+       omap_mcasp_dai_dma_params[0].dma_req = res->start;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "no resource\n");
+               return -ENODEV;
+       }
+
+       mcasp->base = devm_request_and_ioremap(&pdev->dev, res);
+       if (!mcasp->base) {
+               dev_err(&pdev->dev, "cannot remap\n");
+               return -ENOMEM;
+       }
+
+       mcasp->irq = platform_get_irq(pdev, 0);
+       if (mcasp->irq < 0) {
+               dev_err(&pdev->dev, "invalid IRQ number\n");
+               return mcasp->irq;
+       }
+
+       ret = request_threaded_irq(mcasp->irq, NULL, omap_mcasp_irq_handler,
+                               IRQF_ONESHOT, "McASP", mcasp);
+       if (ret) {
+               dev_err(mcasp->dev, "IRQ request failed\n");
+               return ret;
+       }
+
+       mcasp->fclk = clk_get(&pdev->dev, "fck");
+       if (!mcasp->fclk) {
+               dev_err(mcasp->dev, "cant get fck\n");
+               ret = -ENODEV;
+               goto err_clk;
+       }
+
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_get_sync(&pdev->dev);
+
+       fclk_rate = clk_get_rate(mcasp->fclk);
+
+       platform_set_drvdata(pdev, mcasp);
+       mcasp->dev = &pdev->dev;
+
+       ret = snd_soc_register_dai(&pdev->dev, &omap_mcasp_dai);
+       if (ret < 0)
+               goto err_dai;
+
+       pm_runtime_put_sync(&pdev->dev);
+
+       return 0;
+
+err_dai:
+       pm_runtime_put_sync(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+err_clk:
+       free_irq(mcasp->irq, (void *)mcasp);
+       return ret;
+}
+
+static int omap_mcasp_remove(struct platform_device *pdev)
+{
+       struct omap_mcasp *mcasp = dev_get_drvdata(&pdev->dev);
+
+       snd_soc_unregister_dai(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+       clk_put(mcasp->fclk);
+       free_irq(mcasp->irq, (void *)mcasp);
+
+       return 0;
+}
+
+static const struct of_device_id omap_mcasp_of_match[] = {
+       { .compatible = "ti,omap4-mcasp", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, omap_mcasp_of_match);
+
+static struct platform_driver omap_mcasp_driver = {
+       .probe          = omap_mcasp_probe,
+       .remove         = omap_mcasp_remove,
+       .driver         = {
+               .name   = "omap-mcasp",
+               .owner  = THIS_MODULE,
+               .of_match_table = omap_mcasp_of_match,
+       },
+};
+
+static int __init omap_mcasp_init(void)
+{
+       return platform_driver_register(&omap_mcasp_driver);
+}
+module_init(omap_mcasp_init);
+
+static void __exit omap_mcasp_exit(void)
+{
+       platform_driver_unregister(&omap_mcasp_driver);
+}
+module_exit(omap_mcasp_exit);
+
+
+MODULE_AUTHOR("Jon Hunter <jon-hunter@ti.com>");
+MODULE_DESCRIPTION("TI OMAP McASP SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-mcasp");
diff --git a/sound/soc/omap/omap-mcasp.h b/sound/soc/omap/omap-mcasp.h
new file mode 100644 (file)
index 0000000..3fd1604
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI OMAP processor
+ *
+ * MCASP related definitions
+ *
+ * Author: Jon Hunter <jon-hunter@ti.com>,
+ *         Dan Milea <dan.milea@ti.com>,
+ *
+ * Based upon McASP driver written for TI DaVinci
+ *
+ * Copyright:   (C) 2011  Texas Instruments
+ *
+ * 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 OMAP_MCASP_H
+#define OMAP_MCASP_H
+
+#include <linux/io.h>
+
+#define OMAP44XX_MCASP_CFG_BASE                0x49028000
+#define OMAP44XX_MCASP_DAT_BASE                0x4902A000
+
+struct omap_mcasp {
+       struct device *dev;
+       void __iomem *base;
+       spinlock_t lock;
+       struct clk *fclk;
+       int irq;
+       unsigned int stream_rate;
+};
+
+#endif /* OMAP_MCASP_H */
index 2fe8be20945278dc04cf23e22ef112abbf746383..4c5345ad976c0fcb3d4c3fa11fc4690daed7640a 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/err.h>
+#include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/slab.h>
 #include "omap-mcpdm.h"
 #include "omap-pcm.h"
 
-#define OMAP44XX_MCPDM_L3_BASE         0x49032000
+#include "omap-abe-priv.h"
+
+#define MCPDM_LEGACY_MODE      0x0
+#define MCPDM_ABE_MODE         0x1
+
+#define MCPDM_DAI_MODE_MASK    0x0f
 
 struct omap_mcpdm {
        struct device *dev;
@@ -63,6 +69,13 @@ struct omap_mcpdm {
 
        /* McPDM dn offsets for rx1, and 2 channels */
        u32 dn_rx_offset;
+
+       int active;
+       int abe_mode;
+
+       struct omap_aess *abe;
+       struct omap_abe_port *dl_port;
+       struct omap_abe_port *ul_port;
 };
 
 /*
@@ -255,37 +268,76 @@ static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
                                  struct snd_soc_dai *dai)
 {
        struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+       int dai_abe_mode = dai->id & MCPDM_DAI_MODE_MASK;
+       int ret = 0;
 
        mutex_lock(&mcpdm->mutex);
 
+       /* nothing to do if already active */
+       if (mcpdm->active++)
+               goto out;
+
        if (!dai->active) {
                u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
 
                omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN);
-               omap_mcpdm_open_streams(mcpdm);
+
+               mcpdm->abe_mode = dai_abe_mode;
+
+               /* McPDM FIFO configuration */
+               mcpdm->dn_threshold = 2;
+               if (mcpdm->abe_mode == MCPDM_LEGACY_MODE)
+                       mcpdm->up_threshold = MCPDM_UP_THRES_MAX - 3;
+               else
+                       mcpdm->up_threshold = 2;
+       } else if (mcpdm->abe_mode != dai_abe_mode) {
+               dev_err(mcpdm->dev, "Trying %s, while McPDM is in %s.\n",
+                       dai_abe_mode ? "ABE mode" : "Legacy mode",
+                       mcpdm->abe_mode ? "ABE mode" : "Legacy mode");
+               ret = -EINVAL;
        }
+
+out:
        mutex_unlock(&mcpdm->mutex);
 
        snd_soc_dai_set_dma_data(dai, substream,
                                 &omap_mcpdm_dai_dma_params[substream->stream]);
 
-       return 0;
+       return ret;
 }
 
 static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
                                  struct snd_soc_dai *dai)
 {
        struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
 
        mutex_lock(&mcpdm->mutex);
 
+       if (--mcpdm->active)
+               goto out;
+
        if (!dai->active) {
                if (omap_mcpdm_active(mcpdm)) {
-                       omap_mcpdm_stop(mcpdm);
-                       omap_mcpdm_close_streams(mcpdm);
+                       if (mcpdm->abe_mode == MCPDM_LEGACY_MODE) {
+                               omap_mcpdm_stop(mcpdm);
+                               omap_mcpdm_close_streams(mcpdm);
+                       } else {
+                               omap_abe_port_disable(mcpdm->abe,
+                                                     mcpdm->dl_port);
+                               omap_abe_port_disable(mcpdm->abe,
+                                                     mcpdm->ul_port);
+                               usleep_range(250, 300);
+                               omap_mcpdm_stop(mcpdm);
+                               omap_mcpdm_close_streams(mcpdm);
+                               omap_abe_pm_shutdown(platform);
+                               omap_abe_pm_put(platform);
+                       }
                }
        }
 
+out:
        mutex_unlock(&mcpdm->mutex);
 }
 
@@ -299,6 +351,17 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
        int channels;
        int link_mask = 0;
 
+       dma_data = snd_soc_dai_get_dma_data(dai, substream);
+
+       /* ABE DAIs have fixed channels */
+       if ((dai->id & MCPDM_DAI_MODE_MASK) == MCPDM_ABE_MODE) {
+               mcpdm->dn_channels = MCPDM_PDM_DN_MASK | MCPDM_CMD_INT;
+               mcpdm->up_channels = MCPDM_PDM_UPLINK_EN(1) |
+                                       MCPDM_PDM_UPLINK_EN(2);
+               dma_data->packet_size = 16;
+               return 0;
+       }
+
        channels = params_channels(params);
        switch (channels) {
        case 5:
@@ -323,8 +386,6 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       dma_data = snd_soc_dai_get_dma_data(dai, substream);
-
        /* Configure McPDM channels, and DMA packet size */
        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
                mcpdm->dn_channels = link_mask << 3;
@@ -342,12 +403,33 @@ static int omap_mcpdm_prepare(struct snd_pcm_substream *substream,
                                  struct snd_soc_dai *dai)
 {
        struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+
+       /*  */
+       if (omap_mcpdm_active(mcpdm))
+               return 0;
 
-       if (!omap_mcpdm_active(mcpdm)) {
-               omap_mcpdm_start(mcpdm);
-               omap_mcpdm_reg_dump(mcpdm);
+       if (mcpdm->abe_mode == MCPDM_ABE_MODE) {
+               /* Check if ABE McPDM DL is already started */
+               if ((omap_abe_port_is_enabled(mcpdm->abe, mcpdm->dl_port)) ||
+                       (omap_abe_port_is_enabled(mcpdm->abe, mcpdm->ul_port)))
+                       return 0;
+
+               omap_abe_pm_get(platform);
+
+               /* start ATC before McPDM IP */
+               omap_abe_port_enable(mcpdm->abe, mcpdm->dl_port);
+               omap_abe_port_enable(mcpdm->abe, mcpdm->ul_port);
+
+               /* wait 250us for ABE tick */
+               usleep_range(250, 300);
        }
 
+       omap_mcpdm_open_streams(mcpdm);
+       omap_mcpdm_start(mcpdm);
+
+       omap_mcpdm_reg_dump(mcpdm);
        return 0;
 }
 
@@ -398,7 +480,15 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai)
 #define OMAP_MCPDM_RATES       (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 #define OMAP_MCPDM_FORMATS     SNDRV_PCM_FMTBIT_S32_LE
 
-static struct snd_soc_dai_driver omap_mcpdm_dai = {
+#define MCPDM_LEGACY_DAI       (MCPDM_LEGACY_MODE | (0 << 4))
+#define MCPDM_ABE_DAI_DL1      (MCPDM_ABE_MODE | (1 << 4))
+#define MCPDM_ABE_DAI_DL2      (MCPDM_ABE_MODE | (2 << 4))
+#define MCPDM_ABE_DAI_UL1      (MCPDM_ABE_MODE | (4 << 4))
+
+static struct snd_soc_dai_driver omap_mcpdm_dai[] = {
+{
+       .name = "mcpdm-legacy",
+       .id     = MCPDM_LEGACY_DAI,
        .probe = omap_mcpdm_probe,
        .remove = omap_mcpdm_remove,
        .probe_order = SND_SOC_COMP_ORDER_LATE,
@@ -418,6 +508,49 @@ static struct snd_soc_dai_driver omap_mcpdm_dai = {
                .sig_bits = 24,
        },
        .ops = &omap_mcpdm_dai_ops,
+},
+#if defined(CONFIG_SND_OMAP_SOC_ABE) ||\
+       defined(CONFIG_SND_OMAP_SOC_ABE_MODULE)
+{
+       .name = "mcpdm-dl1",
+       .id     = MCPDM_ABE_DAI_DL1,
+       .probe_order = SND_SOC_COMP_ORDER_LATE,
+       .remove_order = SND_SOC_COMP_ORDER_EARLY,
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = OMAP_MCPDM_RATES,
+               .formats = OMAP_MCPDM_FORMATS,
+       },
+       .ops = &omap_mcpdm_dai_ops,
+},
+{
+       .name = "mcpdm-dl2",
+       .id     = MCPDM_ABE_DAI_DL2,
+       .probe_order = SND_SOC_COMP_ORDER_LATE,
+       .remove_order = SND_SOC_COMP_ORDER_EARLY,
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = OMAP_MCPDM_RATES,
+               .formats = OMAP_MCPDM_FORMATS,
+       },
+       .ops = &omap_mcpdm_dai_ops,
+},
+{
+       .name = "mcpdm-ul1",
+       .id     = MCPDM_ABE_DAI_UL1,
+       .probe_order = SND_SOC_COMP_ORDER_LATE,
+       .remove_order = SND_SOC_COMP_ORDER_EARLY,
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = OMAP_MCPDM_RATES,
+               .formats = OMAP_MCPDM_FORMATS,
+       },
+       .ops = &omap_mcpdm_dai_ops,
+},
+#endif
 };
 
 void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd,
@@ -433,6 +566,7 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
 {
        struct omap_mcpdm *mcpdm;
        struct resource *res;
+       int ret;
 
        mcpdm = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcpdm), GFP_KERNEL);
        if (!mcpdm)
@@ -484,12 +618,53 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
 
        mcpdm->dev = &pdev->dev;
 
-       return snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai);
+#if defined(CONFIG_SND_OMAP_SOC_ABE) ||\
+       defined(CONFIG_SND_OMAP_SOC_ABE_MODULE)
+
+       mcpdm->abe = omap_abe_port_mgr_get();
+
+       mcpdm->dl_port = omap_abe_port_open(mcpdm->abe,
+                                           OMAP_ABE_BE_PORT_PDM_DL1);
+       if (mcpdm->dl_port == NULL) {
+               omap_abe_port_mgr_put(mcpdm->abe);
+               return -EINVAL;
+       }
+
+       mcpdm->ul_port = omap_abe_port_open(mcpdm->abe,
+                                           OMAP_ABE_BE_PORT_PDM_UL1);
+       if (mcpdm->ul_port == NULL) {
+               omap_abe_port_close(mcpdm->abe, mcpdm->dl_port);
+               omap_abe_port_mgr_put(mcpdm->abe);
+               return -EINVAL;
+       }
+#endif
+       ret = snd_soc_register_dais(&pdev->dev, omap_mcpdm_dai,
+                                   ARRAY_SIZE(omap_mcpdm_dai));
+       if (!ret)
+               return 0;
+
+#if defined(CONFIG_SND_OMAP_SOC_ABE) ||\
+       defined(CONFIG_SND_OMAP_SOC_ABE_MODULE)
+       omap_abe_port_close(mcpdm->abe, mcpdm->dl_port);
+       omap_abe_port_close(mcpdm->abe, mcpdm->ul_port);
+       omap_abe_port_mgr_put(mcpdm->abe);
+#endif
+       return ret;
 }
 
 static int asoc_mcpdm_remove(struct platform_device *pdev)
 {
+       struct omap_mcpdm *mcpdm = platform_get_drvdata(pdev);
+
        snd_soc_unregister_dai(&pdev->dev);
+
+#if defined(CONFIG_SND_OMAP_SOC_ABE) ||\
+       defined(CONFIG_SND_OMAP_SOC_ABE_MODULE)
+       omap_abe_port_close(mcpdm->abe, mcpdm->dl_port);
+       omap_abe_port_close(mcpdm->abe, mcpdm->ul_port);
+       omap_abe_port_mgr_put(mcpdm->abe);
+#endif
+
        return 0;
 }
 
@@ -516,3 +691,4 @@ MODULE_ALIAS("platform:omap-mcpdm");
 MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
 MODULE_DESCRIPTION("OMAP PDM SoC Interface");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-mcpdm");
index 4541d28b531495d7484f3a2dfcf4fe8472af451f..fd98509d0f49deea2bf474dc246edd04f251d6dd 100644 (file)
@@ -11,6 +11,8 @@
  * omap3evm (Author: Anuj Aggarwal <anuj.aggarwal@ti.com>)
  * overo (Author: Steve Sakoman <steve@sakoman.com>)
  * igep0020 (Author: Enric Balletbo i Serra <eballetbo@iseebcn.com>)
+ * zoom2 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
+ * sdp3430 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 #include <linux/platform_data/omap-twl4030.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
+#include <sound/jack.h>
 
 #include "omap-mcbsp.h"
 #include "omap-pcm.h"
 
+struct omap_twl4030 {
+       int jack_detect;        /* board can detect jack events */
+       struct snd_soc_jack hs_jack;
+};
+
 static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params)
 {
@@ -87,17 +97,164 @@ static struct snd_soc_ops omap_twl4030_ops = {
        .hw_params = omap_twl4030_hw_params,
 };
 
+static const struct snd_soc_dapm_widget dapm_widgets[] = {
+       SND_SOC_DAPM_SPK("Earpiece Spk", NULL),
+       SND_SOC_DAPM_SPK("Handsfree Spk", NULL),
+       SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+       SND_SOC_DAPM_SPK("Carkit Spk", NULL),
+
+       SND_SOC_DAPM_MIC("Main Mic", NULL),
+       SND_SOC_DAPM_MIC("Sub Mic", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Carkit Mic", NULL),
+       SND_SOC_DAPM_MIC("Digital0 Mic", NULL),
+       SND_SOC_DAPM_MIC("Digital1 Mic", NULL),
+       SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Headset Stereophone:  HSOL, HSOR */
+       {"Headset Stereophone", NULL, "HSOL"},
+       {"Headset Stereophone", NULL, "HSOR"},
+       /* External Speakers: HFL, HFR */
+       {"Handsfree Spk", NULL, "HFL"},
+       {"Handsfree Spk", NULL, "HFR"},
+       /* External Speakers: PredrivL, PredrivR */
+       {"Ext Spk", NULL, "PREDRIVEL"},
+       {"Ext Spk", NULL, "PREDRIVER"},
+       /* Carkit speakers:  CARKITL, CARKITR */
+       {"Carkit Spk", NULL, "CARKITL"},
+       {"Carkit Spk", NULL, "CARKITR"},
+       /* Earpiece */
+       {"Earpiece Spk", NULL, "EARPIECE"},
+
+       /* External Mics: MAINMIC, SUBMIC with bias */
+       {"MAINMIC", NULL, "Main Mic"},
+       {"Main Mic", NULL, "Mic Bias 1"},
+       {"SUBMIC", NULL, "Sub Mic"},
+       {"Sub Mic", NULL, "Mic Bias 2"},
+       /* Headset Mic: HSMIC with bias */
+       {"HSMIC", NULL, "Headset Mic"},
+       {"Headset Mic", NULL, "Headset Mic Bias"},
+       /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */
+       {"DIGIMIC0", NULL, "Digital0 Mic"},
+       {"Digital0 Mic", NULL, "Mic Bias 1"},
+       {"DIGIMIC1", NULL, "Digital1 Mic"},
+       {"Digital1 Mic", NULL, "Mic Bias 2"},
+       /* Carkit In: CARKITMIC */
+       {"CARKITMIC", NULL, "Carkit Mic"},
+       /* Aux In: AUXL, AUXR */
+       {"AUXL", NULL, "Line In"},
+       {"AUXR", NULL, "Line In"},
+};
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+       {
+               .pin = "Headset Mic",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headset Stereophone",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
+/* Headset jack detection gpios */
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+       {
+               .name = "hsdet-gpio",
+               .report = SND_JACK_HEADSET,
+               .debounce_time = 200,
+       },
+};
+
+static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
+                                         int connected, char *pin)
+{
+       if (!connected)
+               snd_soc_dapm_disable_pin(dapm, pin);
+}
+
+static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_card *card = codec->card;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
+       struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
+       int ret = 0;
+
+       /* Headset jack detection only if it is supported */
+       if (priv->jack_detect > 0) {
+               hs_jack_gpios[0].gpio = priv->jack_detect;
+
+               ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
+                                      &priv->hs_jack);
+               if (ret)
+                       return ret;
+
+               ret = snd_soc_jack_add_pins(&priv->hs_jack,
+                                           ARRAY_SIZE(hs_jack_pins),
+                                           hs_jack_pins);
+               if (ret)
+                       return ret;
+
+               ret = snd_soc_jack_add_gpios(&priv->hs_jack,
+                                            ARRAY_SIZE(hs_jack_gpios),
+                                            hs_jack_gpios);
+               if (ret)
+                       return ret;
+       }
+
+       /*
+        * NULL pdata means we booted with DT. In this case the routing is
+        * provided and the card is fully routed, no need to mark pins.
+        */
+       if (!pdata || !pdata->custom_routing)
+               return ret;
+
+       /* Disable not connected paths if not used */
+       twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk");
+       twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk");
+       twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
+       twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk");
+       twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk");
+
+       twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In");
+
+       return ret;
+}
+
 /* Digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
        {
-               .name = "TWL4030",
-               .stream_name = "TWL4030",
+               .name = "TWL4030 HiFi",
+               .stream_name = "TWL4030 HiFi",
                .cpu_dai_name = "omap-mcbsp.2",
                .codec_dai_name = "twl4030-hifi",
                .platform_name = "omap-pcm-audio",
                .codec_name = "twl4030-codec",
+               .init = omap_twl4030_init,
                .ops = &omap_twl4030_ops,
        },
+       {
+               .name = "TWL4030 Voice",
+               .stream_name = "TWL4030 Voice",
+               .cpu_dai_name = "omap-mcbsp.3",
+               .codec_dai_name = "twl4030-voice",
+               .platform_name = "omap-pcm-audio",
+               .codec_name = "twl4030-codec",
+               .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
+                          SND_SOC_DAIFMT_CBM_CFM,
+       },
 };
 
 /* Audio machine driver */
@@ -105,6 +262,11 @@ static struct snd_soc_card omap_twl4030_card = {
        .owner = THIS_MODULE,
        .dai_link = omap_twl4030_dai_links,
        .num_links = ARRAY_SIZE(omap_twl4030_dai_links),
+
+       .dapm_widgets = dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
+       .dapm_routes = audio_map,
+       .num_dapm_routes = ARRAY_SIZE(audio_map),
 };
 
 static int omap_twl4030_probe(struct platform_device *pdev)
@@ -112,12 +274,18 @@ static int omap_twl4030_probe(struct platform_device *pdev)
        struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev);
        struct device_node *node = pdev->dev.of_node;
        struct snd_soc_card *card = &omap_twl4030_card;
+       struct omap_twl4030 *priv;
        int ret = 0;
 
        card->dev = &pdev->dev;
 
+       priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL);
+       if (priv == NULL)
+               return -ENOMEM;
+
        if (node) {
                struct device_node *dai_node;
+               struct property *prop;
 
                if (snd_soc_of_parse_card_name(card, "ti,model")) {
                        dev_err(&pdev->dev, "Card name is not provided\n");
@@ -132,6 +300,27 @@ static int omap_twl4030_probe(struct platform_device *pdev)
                omap_twl4030_dai_links[0].cpu_dai_name  = NULL;
                omap_twl4030_dai_links[0].cpu_of_node = dai_node;
 
+               dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0);
+               if (!dai_node) {
+                       card->num_links = 1;
+               } else {
+                       omap_twl4030_dai_links[1].cpu_dai_name  = NULL;
+                       omap_twl4030_dai_links[1].cpu_of_node = dai_node;
+               }
+
+               priv->jack_detect = of_get_named_gpio(node,
+                                                     "ti,jack-det-gpio", 0);
+
+               /* Optional: audio routing can be provided */
+               prop = of_find_property(node, "ti,audio-routing", NULL);
+               if (prop) {
+                       ret = snd_soc_of_parse_audio_routing(card,
+                                                           "ti,audio-routing");
+                       if (ret)
+                               return ret;
+
+                       card->fully_routed = 1;
+               }
        } else if (pdata) {
                if (pdata->card_name) {
                        card->name = pdata->card_name;
@@ -139,11 +328,17 @@ static int omap_twl4030_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev, "Card name is not provided\n");
                        return -ENODEV;
                }
+
+               if (!pdata->voice_connected)
+                       card->num_links = 1;
+
+               priv->jack_detect = pdata->jack_detect;
        } else {
                dev_err(&pdev->dev, "Missing pdata\n");
                return -ENODEV;
        }
 
+       snd_soc_card_set_drvdata(card, priv);
        ret = snd_soc_register_card(card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
@@ -157,7 +352,12 @@ static int omap_twl4030_probe(struct platform_device *pdev)
 static int omap_twl4030_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
 
+       if (priv->jack_detect > 0)
+               snd_soc_jack_free_gpios(&priv->hs_jack,
+                                       ARRAY_SIZE(hs_jack_gpios),
+                                       hs_jack_gpios);
        snd_soc_unregister_card(card);
 
        return 0;
index 43d950a79ff9faf0f6496bd3e0bbba7f7326502e..805512f2555a0b95571a4608f6d4e35a71312c5f 100644 (file)
@@ -144,11 +144,11 @@ static const struct snd_soc_dapm_route omap3pandora_in_map[] = {
        {"AUXL", NULL, "Line In"},
        {"AUXR", NULL, "Line In"},
 
-       {"MAINMIC", NULL, "Mic Bias 1"},
-       {"Mic Bias 1", NULL, "Mic (internal)"},
+       {"MAINMIC", NULL, "Mic (internal)"},
+       {"Mic (internal)", NULL, "Mic Bias 1"},
 
-       {"SUBMIC", NULL, "Mic Bias 2"},
-       {"Mic Bias 2", NULL, "Mic (external)"},
+       {"SUBMIC", NULL, "Mic (external)"},
+       {"Mic (external)", NULL, "Mic Bias 2"},
 };
 
 static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c
deleted file mode 100644 (file)
index b462a2c..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * sdp3430.c  --  SoC audio for TI OMAP3430 SDP
- *
- * Author: Misael Lopez Cruz <x0052729@ti.com>
- *
- * Based on:
- * Author: Steve Sakoman <steve@sakoman.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/i2c/twl.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include <asm/mach-types.h>
-#include <linux/platform_data/gpio-omap.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-/* Register descriptions for twl4030 codec part */
-#include <linux/mfd/twl4030-audio.h>
-#include <linux/module.h>
-
-#include "omap-mcbsp.h"
-#include "omap-pcm.h"
-
-/* TWL4030 PMBR1 Register */
-#define TWL4030_INTBR_PMBR1            0x0D
-/* TWL4030 PMBR1 Register GPIO6 mux bit */
-#define TWL4030_GPIO6_PWM0_MUTE(value) (value << 2)
-
-static struct snd_soc_card snd_soc_sdp3430;
-
-static int sdp3430_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret;
-
-       /* Set the codec system clock for DAC and ADC */
-       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
-                                           SND_SOC_CLOCK_IN);
-       if (ret < 0) {
-               printk(KERN_ERR "can't set codec system clock\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static struct snd_soc_ops sdp3430_ops = {
-       .hw_params = sdp3430_hw_params,
-};
-
-/* Headset jack */
-static struct snd_soc_jack hs_jack;
-
-/* Headset jack detection DAPM pins */
-static struct snd_soc_jack_pin hs_jack_pins[] = {
-       {
-               .pin = "Headset Mic",
-               .mask = SND_JACK_MICROPHONE,
-       },
-       {
-               .pin = "Headset Stereophone",
-               .mask = SND_JACK_HEADPHONE,
-       },
-};
-
-/* Headset jack detection gpios */
-static struct snd_soc_jack_gpio hs_jack_gpios[] = {
-       {
-               .gpio = (OMAP_MAX_GPIO_LINES + 2),
-               .name = "hsdet-gpio",
-               .report = SND_JACK_HEADSET,
-               .debounce_time = 200,
-       },
-};
-
-/* SDP3430 machine DAPM */
-static const struct snd_soc_dapm_widget sdp3430_twl4030_dapm_widgets[] = {
-       SND_SOC_DAPM_MIC("Ext Mic", NULL),
-       SND_SOC_DAPM_SPK("Ext Spk", NULL),
-       SND_SOC_DAPM_MIC("Headset Mic", NULL),
-       SND_SOC_DAPM_HP("Headset Stereophone", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
-       /* External Mics: MAINMIC, SUBMIC with bias*/
-       {"MAINMIC", NULL, "Mic Bias 1"},
-       {"SUBMIC", NULL, "Mic Bias 2"},
-       {"Mic Bias 1", NULL, "Ext Mic"},
-       {"Mic Bias 2", NULL, "Ext Mic"},
-
-       /* External Speakers: HFL, HFR */
-       {"Ext Spk", NULL, "HFL"},
-       {"Ext Spk", NULL, "HFR"},
-
-       /* Headset Mic: HSMIC with bias */
-       {"HSMIC", NULL, "Headset Mic Bias"},
-       {"Headset Mic Bias", NULL, "Headset Mic"},
-
-       /* Headset Stereophone (Headphone): HSOL, HSOR */
-       {"Headset Stereophone", NULL, "HSOL"},
-       {"Headset Stereophone", NULL, "HSOR"},
-};
-
-static int sdp3430_twl4030_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-       int ret;
-
-       /* SDP3430 connected pins */
-       snd_soc_dapm_enable_pin(dapm, "Ext Mic");
-       snd_soc_dapm_enable_pin(dapm, "Ext Spk");
-       snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-       snd_soc_dapm_disable_pin(dapm, "Headset Stereophone");
-
-       /* TWL4030 not connected pins */
-       snd_soc_dapm_nc_pin(dapm, "AUXL");
-       snd_soc_dapm_nc_pin(dapm, "AUXR");
-       snd_soc_dapm_nc_pin(dapm, "CARKITMIC");
-       snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");
-       snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");
-
-       snd_soc_dapm_nc_pin(dapm, "OUTL");
-       snd_soc_dapm_nc_pin(dapm, "OUTR");
-       snd_soc_dapm_nc_pin(dapm, "EARPIECE");
-       snd_soc_dapm_nc_pin(dapm, "PREDRIVEL");
-       snd_soc_dapm_nc_pin(dapm, "PREDRIVER");
-       snd_soc_dapm_nc_pin(dapm, "CARKITL");
-       snd_soc_dapm_nc_pin(dapm, "CARKITR");
-
-       /* Headset jack detection */
-       ret = snd_soc_jack_new(codec, "Headset Jack",
-                               SND_JACK_HEADSET, &hs_jack);
-       if (ret)
-               return ret;
-
-       ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
-                               hs_jack_pins);
-       if (ret)
-               return ret;
-
-       ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
-                               hs_jack_gpios);
-
-       return ret;
-}
-
-static int sdp3430_twl4030_voice_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       unsigned short reg;
-
-       /* Enable voice interface */
-       reg = codec->driver->read(codec, TWL4030_REG_VOICE_IF);
-       reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN;
-       codec->driver->write(codec, TWL4030_REG_VOICE_IF, reg);
-
-       return 0;
-}
-
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link sdp3430_dai[] = {
-       {
-               .name = "TWL4030 I2S",
-               .stream_name = "TWL4030 Audio",
-               .cpu_dai_name = "omap-mcbsp.2",
-               .codec_dai_name = "twl4030-hifi",
-               .platform_name = "omap-pcm-audio",
-               .codec_name = "twl4030-codec",
-               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
-                          SND_SOC_DAIFMT_CBM_CFM,
-               .init = sdp3430_twl4030_init,
-               .ops = &sdp3430_ops,
-       },
-       {
-               .name = "TWL4030 PCM",
-               .stream_name = "TWL4030 Voice",
-               .cpu_dai_name = "omap-mcbsp.3",
-               .codec_dai_name = "twl4030-voice",
-               .platform_name = "omap-pcm-audio",
-               .codec_name = "twl4030-codec",
-               .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
-                          SND_SOC_DAIFMT_CBM_CFM,
-               .init = sdp3430_twl4030_voice_init,
-               .ops = &sdp3430_ops,
-       },
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_sdp3430 = {
-       .name = "SDP3430",
-       .owner = THIS_MODULE,
-       .dai_link = sdp3430_dai,
-       .num_links = ARRAY_SIZE(sdp3430_dai),
-
-       .dapm_widgets = sdp3430_twl4030_dapm_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(sdp3430_twl4030_dapm_widgets),
-       .dapm_routes = audio_map,
-       .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static struct platform_device *sdp3430_snd_device;
-
-static int __init sdp3430_soc_init(void)
-{
-       int ret;
-       u8 pin_mux;
-
-       if (!machine_is_omap_3430sdp())
-               return -ENODEV;
-       printk(KERN_INFO "SDP3430 SoC init\n");
-
-       sdp3430_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!sdp3430_snd_device) {
-               printk(KERN_ERR "Platform device allocation failed\n");
-               return -ENOMEM;
-       }
-
-       platform_set_drvdata(sdp3430_snd_device, &snd_soc_sdp3430);
-
-       /* Set TWL4030 GPIO6 as EXTMUTE signal */
-       twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux,
-                                               TWL4030_INTBR_PMBR1);
-       pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03);
-       pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02);
-       twl_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux,
-                                               TWL4030_INTBR_PMBR1);
-
-       ret = platform_device_add(sdp3430_snd_device);
-       if (ret)
-               goto err1;
-
-       return 0;
-
-err1:
-       printk(KERN_ERR "Unable to add platform device\n");
-       platform_device_put(sdp3430_snd_device);
-
-       return ret;
-}
-module_init(sdp3430_soc_init);
-
-static void __exit sdp3430_soc_exit(void)
-{
-       snd_soc_jack_free_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
-                               hs_jack_gpios);
-
-       platform_device_unregister(sdp3430_snd_device);
-}
-module_exit(sdp3430_soc_exit);
-
-MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
-MODULE_DESCRIPTION("ALSA SoC SDP3430");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c
deleted file mode 100644 (file)
index 771bff2..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * zoom2.c  --  SoC audio for Zoom2
- *
- * Author: Misael Lopez Cruz <x0052729@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-#include <linux/platform_data/gpio-omap.h>
-
-/* Register descriptions for twl4030 codec part */
-#include <linux/mfd/twl4030-audio.h>
-#include <linux/module.h>
-
-#include "omap-mcbsp.h"
-#include "omap-pcm.h"
-
-static int zoom2_hw_params(struct snd_pcm_substream *substream,
-                               struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret;
-
-       /* Set the codec system clock for DAC and ADC */
-       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
-                                       SND_SOC_CLOCK_IN);
-       if (ret < 0) {
-               printk(KERN_ERR "can't set codec system clock\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static struct snd_soc_ops zoom2_ops = {
-       .hw_params = zoom2_hw_params,
-};
-
-/* Zoom2 machine DAPM */
-static const struct snd_soc_dapm_widget zoom2_twl4030_dapm_widgets[] = {
-       SND_SOC_DAPM_MIC("Ext Mic", NULL),
-       SND_SOC_DAPM_SPK("Ext Spk", NULL),
-       SND_SOC_DAPM_MIC("Headset Mic", NULL),
-       SND_SOC_DAPM_HP("Headset Stereophone", NULL),
-       SND_SOC_DAPM_LINE("Aux In", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
-       /* External Mics: MAINMIC, SUBMIC with bias*/
-       {"MAINMIC", NULL, "Mic Bias 1"},
-       {"SUBMIC", NULL, "Mic Bias 2"},
-       {"Mic Bias 1", NULL, "Ext Mic"},
-       {"Mic Bias 2", NULL, "Ext Mic"},
-
-       /* External Speakers: HFL, HFR */
-       {"Ext Spk", NULL, "HFL"},
-       {"Ext Spk", NULL, "HFR"},
-
-       /* Headset Stereophone:  HSOL, HSOR */
-       {"Headset Stereophone", NULL, "HSOL"},
-       {"Headset Stereophone", NULL, "HSOR"},
-
-       /* Headset Mic: HSMIC with bias */
-       {"HSMIC", NULL, "Headset Mic Bias"},
-       {"Headset Mic Bias", NULL, "Headset Mic"},
-
-       /* Aux In: AUXL, AUXR */
-       {"Aux In", NULL, "AUXL"},
-       {"Aux In", NULL, "AUXR"},
-};
-
-static int zoom2_twl4030_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       /* TWL4030 not connected pins */
-       snd_soc_dapm_nc_pin(dapm, "CARKITMIC");
-       snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");
-       snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");
-       snd_soc_dapm_nc_pin(dapm, "EARPIECE");
-       snd_soc_dapm_nc_pin(dapm, "PREDRIVEL");
-       snd_soc_dapm_nc_pin(dapm, "PREDRIVER");
-       snd_soc_dapm_nc_pin(dapm, "CARKITL");
-       snd_soc_dapm_nc_pin(dapm, "CARKITR");
-
-       return 0;
-}
-
-static int zoom2_twl4030_voice_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       unsigned short reg;
-
-       /* Enable voice interface */
-       reg = codec->driver->read(codec, TWL4030_REG_VOICE_IF);
-       reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN;
-       codec->driver->write(codec, TWL4030_REG_VOICE_IF, reg);
-
-       return 0;
-}
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link zoom2_dai[] = {
-       {
-               .name = "TWL4030 I2S",
-               .stream_name = "TWL4030 Audio",
-               .cpu_dai_name = "omap-mcbsp.2",
-               .codec_dai_name = "twl4030-hifi",
-               .platform_name = "omap-pcm-audio",
-               .codec_name = "twl4030-codec",
-               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
-                          SND_SOC_DAIFMT_CBM_CFM,
-               .init = zoom2_twl4030_init,
-               .ops = &zoom2_ops,
-       },
-       {
-               .name = "TWL4030 PCM",
-               .stream_name = "TWL4030 Voice",
-               .cpu_dai_name = "omap-mcbsp.3",
-               .codec_dai_name = "twl4030-voice",
-               .platform_name = "omap-pcm-audio",
-               .codec_name = "twl4030-codec",
-               .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
-                          SND_SOC_DAIFMT_CBM_CFM,
-               .init = zoom2_twl4030_voice_init,
-               .ops = &zoom2_ops,
-       },
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_zoom2 = {
-       .name = "Zoom2",
-       .owner = THIS_MODULE,
-       .dai_link = zoom2_dai,
-       .num_links = ARRAY_SIZE(zoom2_dai),
-
-       .dapm_widgets = zoom2_twl4030_dapm_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(zoom2_twl4030_dapm_widgets),
-       .dapm_routes = audio_map,
-       .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static struct platform_device *zoom2_snd_device;
-
-static int __init zoom2_soc_init(void)
-{
-       int ret;
-
-       if (!machine_is_omap_zoom2())
-               return -ENODEV;
-       printk(KERN_INFO "Zoom2 SoC init\n");
-
-       zoom2_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!zoom2_snd_device) {
-               printk(KERN_ERR "Platform device allocation failed\n");
-               return -ENOMEM;
-       }
-
-       platform_set_drvdata(zoom2_snd_device, &snd_soc_zoom2);
-       ret = platform_device_add(zoom2_snd_device);
-       if (ret)
-               goto err1;
-
-       return 0;
-
-err1:
-       printk(KERN_ERR "Unable to add platform device\n");
-       platform_device_put(zoom2_snd_device);
-
-       return ret;
-}
-module_init(zoom2_soc_init);
-
-static void __exit zoom2_soc_exit(void)
-{
-       platform_device_unregister(zoom2_snd_device);
-}
-module_exit(zoom2_soc_exit);
-
-MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
-MODULE_DESCRIPTION("ALSA SoC Zoom2");
-MODULE_LICENSE("GPL");
-
index 2074e2daf9c6a341e002ff26771e05de9ce2f9a6..e1ffcdd9a6492678c604ab045a7a981cae562971 100644 (file)
@@ -79,17 +79,6 @@ static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_dapm_context *dapm = &codec->dapm;
        int err;
 
-       /* add palm27x specific widgets */
-       err = snd_soc_dapm_new_controls(dapm, palm27x_dapm_widgets,
-                               ARRAY_SIZE(palm27x_dapm_widgets));
-       if (err)
-               return err;
-
-       /* set up palm27x specific audio path audio_map */
-       err = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-       if (err)
-               return err;
-
        /* connected pins */
        if (machine_is_palmld())
                snd_soc_dapm_enable_pin(dapm, "MIC1");
@@ -149,10 +138,12 @@ static struct snd_soc_card palm27x_asoc = {
        .owner = THIS_MODULE,
        .dai_link = palm27x_dai,
        .num_links = ARRAY_SIZE(palm27x_dai),
+       .dapm_widgets = palm27x_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(palm27x_dapm_widgets),
+       .dapm_routes = audio_map,
+       .num_dapm_routes = ARRAY_SIZE(audio_map)
 };
 
-static struct platform_device *palm27x_snd_device;
-
 static int palm27x_asoc_probe(struct platform_device *pdev)
 {
        int ret;
@@ -169,27 +160,18 @@ static int palm27x_asoc_probe(struct platform_device *pdev)
        hs_jack_gpios[0].gpio = ((struct palm27x_asoc_info *)
                        (pdev->dev.platform_data))->jack_gpio;
 
-       palm27x_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!palm27x_snd_device)
-               return -ENOMEM;
-
-       platform_set_drvdata(palm27x_snd_device, &palm27x_asoc);
-       ret = platform_device_add(palm27x_snd_device);
-
-       if (ret != 0)
-               goto put_device;
-
-       return 0;
-
-put_device:
-       platform_device_put(palm27x_snd_device);
+       palm27x_asoc.dev = &pdev->dev;
 
+       ret = snd_soc_register_card(&palm27x_asoc);
+       if (ret)
+               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+                       ret);
        return ret;
 }
 
 static int palm27x_asoc_remove(struct platform_device *pdev)
 {
-       platform_device_unregister(palm27x_snd_device);
+       snd_soc_unregister_card(&palm27x_asoc);
        return 0;
 }
 
index d2d124f1dd1b2a3c83cdd9c51c104be78b4f18b9..808df74c32489b8315e04acf4f17407fcc429376 100644 (file)
 
 #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
 
+enum samsung_dai_type {
+       TYPE_PRI,
+       TYPE_SEC,
+};
+
 struct i2s_dai {
        /* Platform device for this DAI */
        struct platform_device *pdev;
@@ -651,6 +656,9 @@ static int i2s_startup(struct snd_pcm_substream *substream,
        /* Enforce set_sysclk in Master mode */
        i2s->rclk_srcrate = 0;
 
+       if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR))
+               writel(CON_RSTCLR, i2s->addr + I2SCON);
+
        spin_unlock_irqrestore(&lock, flags);
 
        return 0;
@@ -981,8 +989,7 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
                i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
        } else {        /* Create a new platform_device for Secondary */
                i2s->pdev = platform_device_register_resndata(NULL,
-                               pdev->name, pdev->id + SAMSUNG_I2S_SECOFF,
-                               NULL, 0, NULL, 0);
+                               "samsung-i2s-sec", -1, NULL, 0, NULL, 0);
                if (IS_ERR(i2s->pdev))
                        return NULL;
        }
@@ -993,6 +1000,11 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
        return i2s;
 }
 
+static inline int samsung_i2s_get_driver_data(struct platform_device *pdev)
+{
+       return platform_get_device_id(pdev)->driver_data;
+}
+
 static int samsung_i2s_probe(struct platform_device *pdev)
 {
        u32 dma_pl_chan, dma_cp_chan, dma_pl_sec_chan;
@@ -1001,10 +1013,13 @@ static int samsung_i2s_probe(struct platform_device *pdev)
        struct samsung_i2s *i2s_cfg;
        struct resource *res;
        u32 regs_base, quirks;
+       enum samsung_dai_type samsung_dai_type;
        int ret = 0;
 
        /* Call during Seconday interface registration */
-       if (pdev->id >= SAMSUNG_I2S_SECOFF) {
+       samsung_dai_type = samsung_i2s_get_driver_data(pdev);
+
+       if (samsung_dai_type == TYPE_SEC) {
                sec_dai = dev_get_drvdata(&pdev->dev);
                snd_soc_register_dai(&sec_dai->pdev->dev,
                        &sec_dai->i2s_dai_drv);
@@ -1143,9 +1158,22 @@ static int samsung_i2s_remove(struct platform_device *pdev)
        return 0;
 }
 
+static struct platform_device_id samsung_i2s_driver_ids[] = {
+       {
+               .name           = "samsung-i2s",
+               .driver_data    = TYPE_PRI,
+       }, {
+               .name           = "samsung-i2s-sec",
+               .driver_data    = TYPE_SEC,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(platform, samsung-i2s-driver-ids);
+
 static struct platform_driver samsung_i2s_driver = {
        .probe  = samsung_i2s_probe,
        .remove = samsung_i2s_remove,
+       .id_table = samsung_i2s_driver_ids,
        .driver = {
                .name = "samsung-i2s",
                .owner = THIS_MODULE,
index d420a7ca56cada50ac1db744a9df05fffa143168..7966afc934db3905d8c629cc3d83d82f7fd8f329 100644 (file)
 #ifndef __SND_SOC_SAMSUNG_I2S_H
 #define __SND_SOC_SAMSUNG_I2S_H
 
-/*
- * Maximum number of I2S blocks that any SoC can have.
- * The secondary interface of a CPU dai(if there exists any),
- * is indexed at [cpu-dai's ID + SAMSUNG_I2S_SECOFF]
- */
-#define SAMSUNG_I2S_SECOFF     4
-
 #define SAMSUNG_I2S_DIV_BCLK   1
 
 #define SAMSUNG_I2S_RCLKSRC_0  0
index 7e2b710763be05c96537214189612fae3c538aa9..7a16b32ed673372de134c3b87ba457b1f5e853d2 100644 (file)
@@ -193,9 +193,9 @@ static struct snd_soc_dai_link smdk_dai[] = {
        [SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */
                .name = "Sec_FIFO TX",
                .stream_name = "Playback",
-               .cpu_dai_name = "samsung-i2s.x",
+               .cpu_dai_name = "samsung-i2s-sec",
                .codec_dai_name = "wm8580-hifi-playback",
-               .platform_name = "samsung-i2s.x",
+               .platform_name = "samsung-i2s-sec",
                .codec_name = "wm8580.0-001b",
                .ops = &smdk_ops,
        },
@@ -223,9 +223,6 @@ static int __init smdk_audio_init(void)
        if (machine_is_smdkc100()
                        || machine_is_smdkv210() || machine_is_smdkc110()) {
                smdk.num_links = 3;
-               /* Secondary is at offset SAMSUNG_I2S_SECOFF from Primary */
-               str = (char *)smdk_dai[SEC_PLAYBACK].cpu_dai_name;
-               str[strlen(str) - 1] = '0' + SAMSUNG_I2S_SECOFF;
        } else if (machine_is_smdk6410()) {
                str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
                str[strlen(str) - 1] = '2';
index b0d0ab8bff5ae8299628cd1aec8d229af4a73af6..cc2f407e9f1bce604ec433489893ae3665b9edca 100644 (file)
@@ -134,9 +134,9 @@ static struct snd_soc_dai_link smdk_dai[] = {
        }, { /* Sec_Fifo Playback i/f */
                .name = "Sec_FIFO TX",
                .stream_name = "Sec_Dai",
-               .cpu_dai_name = "samsung-i2s.4",
+               .cpu_dai_name = "samsung-i2s-sec",
                .codec_dai_name = "wm8994-aif1",
-               .platform_name = "samsung-i2s.4",
+               .platform_name = "samsung-i2s-sec",
                .codec_name = "wm8994-codec",
                .ops = &smdk_ops,
        },
index a606d0f93d1cea4db2e88f47e8ba0d94b36aa59f..c724026a246f6ec90f80b11dfed4397fee6d7d37 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/scatterlist.h>
 #include <linux/sh_dma.h>
 #include <linux/slab.h>
 
 #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 
-typedef int (*set_rate_func)(struct device *dev, int rate, int enable);
-
 /*
  * bus options
  *
@@ -244,8 +244,7 @@ struct fsi_clk {
        struct clk *ick;
        struct clk *div;
        int (*set_rate)(struct device *dev,
-                       struct fsi_priv *fsi,
-                       unsigned long rate);
+                       struct fsi_priv *fsi);
 
        unsigned long rate;
        unsigned int count;
@@ -254,7 +253,6 @@ struct fsi_clk {
 struct fsi_priv {
        void __iomem *base;
        struct fsi_master *master;
-       struct sh_fsi_port_info *info;
 
        struct fsi_stream playback;
        struct fsi_stream capture;
@@ -270,8 +268,6 @@ struct fsi_priv {
        int enable_stream:1;
        int bit_clk_inv:1;
        int lr_clk_inv:1;
-
-       long rate;
 };
 
 struct fsi_stream_handler {
@@ -303,7 +299,7 @@ struct fsi_master {
        int irq;
        struct fsi_priv fsia;
        struct fsi_priv fsib;
-       struct fsi_core *core;
+       const struct fsi_core *core;
        spinlock_t lock;
 };
 
@@ -431,22 +427,6 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
        return fsi_get_priv_frm_dai(fsi_get_dai(substream));
 }
 
-static set_rate_func fsi_get_info_set_rate(struct fsi_priv *fsi)
-{
-       if (!fsi->info)
-               return NULL;
-
-       return fsi->info->set_rate;
-}
-
-static u32 fsi_get_info_flags(struct fsi_priv *fsi)
-{
-       if (!fsi->info)
-               return 0;
-
-       return fsi->info->flags;
-}
-
 static u32 fsi_get_port_shift(struct fsi_priv *fsi, struct fsi_stream *io)
 {
        int is_play = fsi_stream_is_play(fsi, io);
@@ -757,8 +737,7 @@ static int fsi_clk_init(struct device *dev,
                        int ick,
                        int div,
                        int (*set_rate)(struct device *dev,
-                                       struct fsi_priv *fsi,
-                                       unsigned long rate))
+                                       struct fsi_priv *fsi))
 {
        struct fsi_clk *clock = &fsi->clock;
        int is_porta = fsi_is_port_a(fsi);
@@ -829,8 +808,7 @@ static int fsi_clk_is_valid(struct fsi_priv *fsi)
 }
 
 static int fsi_clk_enable(struct device *dev,
-                         struct fsi_priv *fsi,
-                         unsigned long rate)
+                         struct fsi_priv *fsi)
 {
        struct fsi_clk *clock = &fsi->clock;
        int ret = -EINVAL;
@@ -839,7 +817,7 @@ static int fsi_clk_enable(struct device *dev,
                return ret;
 
        if (0 == clock->count) {
-               ret = clock->set_rate(dev, fsi, rate);
+               ret = clock->set_rate(dev, fsi);
                if (ret < 0) {
                        fsi_clk_invalid(fsi);
                        return ret;
@@ -946,11 +924,11 @@ static int fsi_clk_set_ackbpf(struct device *dev,
 }
 
 static int fsi_clk_set_rate_external(struct device *dev,
-                                    struct fsi_priv *fsi,
-                                    unsigned long rate)
+                                    struct fsi_priv *fsi)
 {
        struct clk *xck = fsi->clock.xck;
        struct clk *ick = fsi->clock.ick;
+       unsigned long rate = fsi->clock.rate;
        unsigned long xrate;
        int ackmd, bpfmd;
        int ret = 0;
@@ -978,11 +956,11 @@ static int fsi_clk_set_rate_external(struct device *dev,
 }
 
 static int fsi_clk_set_rate_cpg(struct device *dev,
-                               struct fsi_priv *fsi,
-                               unsigned long rate)
+                               struct fsi_priv *fsi)
 {
        struct clk *ick = fsi->clock.ick;
        struct clk *div = fsi->clock.div;
+       unsigned long rate = fsi->clock.rate;
        unsigned long target = 0; /* 12288000 or 11289600 */
        unsigned long actual, cout;
        unsigned long diff, min;
@@ -1063,85 +1041,6 @@ static int fsi_clk_set_rate_cpg(struct device *dev,
        return ret;
 }
 
-static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
-                             long rate, int enable)
-{
-       set_rate_func set_rate = fsi_get_info_set_rate(fsi);
-       int ret;
-
-       /*
-        * CAUTION
-        *
-        * set_rate will be deleted
-        */
-       if (!set_rate) {
-               if (enable)
-                       return fsi_clk_enable(dev, fsi, rate);
-               else
-                       return fsi_clk_disable(dev, fsi);
-       }
-
-       ret = set_rate(dev, rate, enable);
-       if (ret < 0) /* error */
-               return ret;
-
-       if (!enable)
-               return 0;
-
-       if (ret > 0) {
-               u32 data = 0;
-
-               switch (ret & SH_FSI_ACKMD_MASK) {
-               default:
-                       /* FALL THROUGH */
-               case SH_FSI_ACKMD_512:
-                       data |= (0x0 << 12);
-                       break;
-               case SH_FSI_ACKMD_256:
-                       data |= (0x1 << 12);
-                       break;
-               case SH_FSI_ACKMD_128:
-                       data |= (0x2 << 12);
-                       break;
-               case SH_FSI_ACKMD_64:
-                       data |= (0x3 << 12);
-                       break;
-               case SH_FSI_ACKMD_32:
-                       data |= (0x4 << 12);
-                       break;
-               }
-
-               switch (ret & SH_FSI_BPFMD_MASK) {
-               default:
-                       /* FALL THROUGH */
-               case SH_FSI_BPFMD_32:
-                       data |= (0x0 << 8);
-                       break;
-               case SH_FSI_BPFMD_64:
-                       data |= (0x1 << 8);
-                       break;
-               case SH_FSI_BPFMD_128:
-                       data |= (0x2 << 8);
-                       break;
-               case SH_FSI_BPFMD_256:
-                       data |= (0x3 << 8);
-                       break;
-               case SH_FSI_BPFMD_512:
-                       data |= (0x4 << 8);
-                       break;
-               case SH_FSI_BPFMD_16:
-                       data |= (0x7 << 8);
-                       break;
-               }
-
-               fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
-               udelay(10);
-               ret = 0;
-       }
-
-       return ret;
-}
-
 /*
  *             pio data transfer handler
  */
@@ -1637,7 +1536,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
                          struct fsi_stream *io,
                          struct device *dev)
 {
-       u32 flags = fsi_get_info_flags(fsi);
        u32 data = 0;
 
        /* clock setting */
@@ -1654,19 +1552,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
                data |= (1 << 4);
        if (fsi_is_clk_master(fsi))
                data <<= 8;
-       /* FIXME
-        *
-        * SH_FSI_xxx_INV style will be removed
-        */
-       if (SH_FSI_LRM_INV & flags)
-               data |= 1 << 12;
-       if (SH_FSI_BRM_INV & flags)
-               data |= 1 << 8;
-       if (SH_FSI_LRS_INV & flags)
-               data |= 1 << 4;
-       if (SH_FSI_BRS_INV & flags)
-               data |= 1 << 0;
-
        fsi_reg_write(fsi, CKG2, data);
 
        /* spdif ? */
@@ -1698,7 +1583,7 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
 
        /* start master clock */
        if (fsi_is_clk_master(fsi))
-               return fsi_set_master_clk(dev, fsi, fsi->rate, 1);
+               return fsi_clk_enable(dev, fsi);
 
        return 0;
 }
@@ -1708,7 +1593,7 @@ static int fsi_hw_shutdown(struct fsi_priv *fsi,
 {
        /* stop master clock */
        if (fsi_is_clk_master(fsi))
-               return fsi_set_master_clk(dev, fsi, fsi->rate, 0);
+               return fsi_clk_disable(dev, fsi);
 
        return 0;
 }
@@ -1719,7 +1604,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
        struct fsi_priv *fsi = fsi_get_priv(substream);
 
        fsi_clk_invalid(fsi);
-       fsi->rate = 0;
 
        return 0;
 }
@@ -1730,7 +1614,6 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
        struct fsi_priv *fsi = fsi_get_priv(substream);
 
        fsi_clk_invalid(fsi);
-       fsi->rate = 0;
 }
 
 static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -1795,7 +1678,6 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
 static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
        struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai);
-       set_rate_func set_rate = fsi_get_info_set_rate(fsi);
        int ret;
 
        /* set master/slave audio interface */
@@ -1831,14 +1713,6 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        }
 
        if (fsi_is_clk_master(fsi)) {
-               /*
-                * CAUTION
-                *
-                * set_rate will be deleted
-                */
-               if (set_rate)
-                       dev_warn(dai->dev, "set_rate will be removed soon\n");
-
                if (fsi->clk_cpg)
                        fsi_clk_init(dai->dev, fsi, 0, 1, 1,
                                     fsi_clk_set_rate_cpg);
@@ -1862,10 +1736,8 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
 {
        struct fsi_priv *fsi = fsi_get_priv(substream);
 
-       if (fsi_is_clk_master(fsi)) {
-               fsi->rate = params_rate(params);
-               fsi_clk_valid(fsi, fsi->rate);
-       }
+       if (fsi_is_clk_master(fsi))
+               fsi_clk_valid(fsi, params_rate(params));
 
        return 0;
 }
@@ -2017,6 +1889,33 @@ static struct snd_soc_platform_driver fsi_soc_platform = {
 /*
  *             platform function
  */
+static void fsi_of_parse(char *name,
+                        struct device_node *np,
+                        struct sh_fsi_port_info *info,
+                        struct device *dev)
+{
+       int i;
+       char prop[128];
+       unsigned long flags = 0;
+       struct {
+               char *name;
+               unsigned int val;
+       } of_parse_property[] = {
+               { "spdif-connection",           SH_FSI_FMT_SPDIF },
+               { "stream-mode-support",        SH_FSI_ENABLE_STREAM_MODE },
+               { "use-internal-clock",         SH_FSI_CLK_CPG },
+       };
+
+       for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) {
+               sprintf(prop, "%s,%s", name, of_parse_property[i].name);
+               if (of_get_property(np, prop, NULL))
+                       flags |= of_parse_property[i].val;
+       }
+       info->flags = flags;
+
+       dev_dbg(dev, "%s flags : %lx\n", name, info->flags);
+}
+
 static void fsi_port_info_init(struct fsi_priv *fsi,
                               struct sh_fsi_port_info *info)
 {
@@ -2044,23 +1943,40 @@ static void fsi_handler_init(struct fsi_priv *fsi,
        }
 }
 
+static struct of_device_id fsi_of_match[];
 static int fsi_probe(struct platform_device *pdev)
 {
        struct fsi_master *master;
-       const struct platform_device_id *id_entry;
-       struct sh_fsi_platform_info *info = pdev->dev.platform_data;
-       struct sh_fsi_port_info nul_info, *pinfo;
+       struct device_node *np = pdev->dev.of_node;
+       struct sh_fsi_platform_info info;
+       const struct fsi_core *core;
        struct fsi_priv *fsi;
        struct resource *res;
        unsigned int irq;
        int ret;
 
-       nul_info.flags  = 0;
-       nul_info.tx_id  = 0;
-       nul_info.rx_id  = 0;
+       memset(&info, 0, sizeof(info));
+
+       core = NULL;
+       if (np) {
+               const struct of_device_id *of_id;
 
-       id_entry = pdev->id_entry;
-       if (!id_entry) {
+               of_id = of_match_device(fsi_of_match, &pdev->dev);
+               if (of_id) {
+                       core = of_id->data;
+                       fsi_of_parse("fsia", np, &info.port_a, &pdev->dev);
+                       fsi_of_parse("fsib", np, &info.port_b, &pdev->dev);
+               }
+       } else {
+               const struct platform_device_id *id_entry = pdev->id_entry;
+               if (id_entry)
+                       core = (struct fsi_core *)id_entry->driver_data;
+
+               if (pdev->dev.platform_data)
+                       memcpy(&info, pdev->dev.platform_data, sizeof(info));
+       }
+
+       if (!core) {
                dev_err(&pdev->dev, "unknown fsi device\n");
                return -ENODEV;
        }
@@ -2087,17 +2003,15 @@ static int fsi_probe(struct platform_device *pdev)
 
        /* master setting */
        master->irq             = irq;
-       master->core            = (struct fsi_core *)id_entry->driver_data;
+       master->core            = core;
        spin_lock_init(&master->lock);
 
        /* FSI A setting */
-       pinfo           = (info) ? &info->port_a : &nul_info;
        fsi             = &master->fsia;
        fsi->base       = master->base;
        fsi->master     = master;
-       fsi->info       = pinfo;
-       fsi_port_info_init(fsi, pinfo);
-       fsi_handler_init(fsi, pinfo);
+       fsi_port_info_init(fsi, &info.port_a);
+       fsi_handler_init(fsi, &info.port_a);
        ret = fsi_stream_probe(fsi, &pdev->dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "FSIA stream probe failed\n");
@@ -2105,13 +2019,11 @@ static int fsi_probe(struct platform_device *pdev)
        }
 
        /* FSI B setting */
-       pinfo           = (info) ? &info->port_b : &nul_info;
        fsi             = &master->fsib;
        fsi->base       = master->base + 0x40;
        fsi->master     = master;
-       fsi->info       = pinfo;
-       fsi_port_info_init(fsi, pinfo);
-       fsi_handler_init(fsi, pinfo);
+       fsi_port_info_init(fsi, &info.port_b);
+       fsi_handler_init(fsi, &info.port_b);
        ret = fsi_stream_probe(fsi, &pdev->dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "FSIB stream probe failed\n");
@@ -2122,7 +2034,7 @@ static int fsi_probe(struct platform_device *pdev)
        dev_set_drvdata(&pdev->dev, master);
 
        ret = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0,
-                         id_entry->name, master);
+                              dev_name(&pdev->dev), master);
        if (ret) {
                dev_err(&pdev->dev, "irq request err\n");
                goto exit_fsib;
@@ -2248,6 +2160,13 @@ static struct fsi_core fsi2_core = {
        .b_mclk = B_MST_CTLR,
 };
 
+static struct of_device_id fsi_of_match[] = {
+       { .compatible = "renesas,sh_fsi",       .data = &fsi1_core},
+       { .compatible = "renesas,sh_fsi2",      .data = &fsi2_core},
+       {},
+};
+MODULE_DEVICE_TABLE(of, fsi_of_match);
+
 static struct platform_device_id fsi_id_table[] = {
        { "sh_fsi",     (kernel_ulong_t)&fsi1_core },
        { "sh_fsi2",    (kernel_ulong_t)&fsi2_core },
@@ -2259,6 +2178,7 @@ static struct platform_driver fsi_driver = {
        .driver         = {
                .name   = "fsi-pcm-audio",
                .pm     = &fsi_pm_ops,
+               .of_match_table = fsi_of_match,
        },
        .probe          = fsi_probe,
        .remove         = fsi_remove,
index 5fbfb06e80831ed97cc42bb91df52568f779fd7f..3ea79564a99360006ad702104424ae731644a6d2 100644 (file)
@@ -33,6 +33,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
                ret = platform->driver->compr_ops->open(cstream);
                if (ret < 0) {
@@ -61,15 +63,46 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
        codec_dai->active++;
        rtd->codec->active++;
 
+       mutex_unlock(&rtd->pcm_mutex);
+
        return 0;
 
 machine_err:
        if (platform->driver->compr_ops && platform->driver->compr_ops->free)
                platform->driver->compr_ops->free(cstream);
 out:
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
+/*
+ * Power down the audio subsystem pmdown_time msecs after close is called.
+ * This is to ensure there are no pops or clicks in between any music tracks
+ * due to DAPM power cycling.
+ */
+static void close_delayed_work(struct work_struct *work)
+{
+       struct snd_soc_pcm_runtime *rtd =
+                       container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+       dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n",
+                codec_dai->driver->playback.stream_name,
+                codec_dai->playback_active ? "active" : "inactive",
+                rtd->pop_wait ? "yes" : "no");
+
+       /* are we waiting on this codec DAI stream */
+       if (rtd->pop_wait == 1) {
+               rtd->pop_wait = 0;
+               snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
+                                         SND_SOC_DAPM_STREAM_STOP);
+       }
+
+       mutex_unlock(&rtd->pcm_mutex);
+}
+
 static int soc_compr_free(struct snd_compr_stream *cstream)
 {
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -78,6 +111,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_codec *codec = rtd->codec;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (cstream->direction == SND_COMPRESS_PLAYBACK) {
                cpu_dai->playback_active--;
                codec_dai->playback_active--;
@@ -112,10 +147,11 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
                        snd_soc_dapm_stream_event(rtd,
                                        SNDRV_PCM_STREAM_PLAYBACK,
                                        SND_SOC_DAPM_STREAM_STOP);
-               } else
+               } else {
                        rtd->pop_wait = 1;
                        schedule_delayed_work(&rtd->delayed_work,
                                msecs_to_jiffies(rtd->pmdown_time));
+               }
        } else {
                /* capture streams can be powered down now */
                snd_soc_dapm_stream_event(rtd,
@@ -123,6 +159,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
                        SND_SOC_DAPM_STREAM_STOP);
        }
 
+       mutex_unlock(&rtd->pcm_mutex);
        return 0;
 }
 
@@ -134,10 +171,12 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
                ret = platform->driver->compr_ops->trigger(cstream, cmd);
                if (ret < 0)
-                       return ret;
+                       goto out;
        }
 
        if (cmd == SNDRV_PCM_TRIGGER_START)
@@ -145,6 +184,8 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
        else if (cmd == SNDRV_PCM_TRIGGER_STOP)
                snd_soc_dai_digital_mute(codec_dai, 1);
 
+out:
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -155,6 +196,8 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream,
        struct snd_soc_platform *platform = rtd->platform;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        /* first we call set_params for the platform driver
         * this should configure the soc side
         * if the machine has compressed ops then we call that as well
@@ -164,18 +207,20 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream,
        if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) {
                ret = platform->driver->compr_ops->set_params(cstream, params);
                if (ret < 0)
-                       return ret;
+                       goto out;
        }
 
        if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) {
                ret = rtd->dai_link->compr_ops->set_params(cstream);
                if (ret < 0)
-                       return ret;
+                       goto out;
        }
 
        snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
                                SND_SOC_DAPM_STREAM_START);
 
+out:
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -186,9 +231,12 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream,
        struct snd_soc_platform *platform = rtd->platform;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->get_params)
                ret = platform->driver->compr_ops->get_params(cstream, params);
 
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -199,9 +247,12 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream,
        struct snd_soc_platform *platform = rtd->platform;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps)
                ret = platform->driver->compr_ops->get_caps(cstream, caps);
 
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -212,9 +263,12 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream,
        struct snd_soc_platform *platform = rtd->platform;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps)
                ret = platform->driver->compr_ops->get_codec_caps(cstream, codec);
 
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -224,9 +278,12 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes)
        struct snd_soc_platform *platform = rtd->platform;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->ack)
                ret = platform->driver->compr_ops->ack(cstream, bytes);
 
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -236,9 +293,12 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream,
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
        struct snd_soc_platform *platform = rtd->platform;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->pointer)
                 platform->driver->compr_ops->pointer(cstream, tstamp);
 
+       mutex_unlock(&rtd->pcm_mutex);
        return 0;
 }
 
@@ -285,6 +345,9 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
                return ret;
        }
 
+       /* DAPM dai link stream work */
+       INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
+
        rtd->compr = compr;
        compr->private_data = rtd;
 
index 2370063b58245946f71adfa05f361097d3e53374..eadb5454cce863e51e808a61375d032e05854ca1 100644 (file)
@@ -40,6 +40,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc-dpcm.h>
+#include <sound/soc-fw.h>
 #include <sound/initval.h>
 
 #define CREATE_TRACE_POINTS
@@ -1107,6 +1108,10 @@ static int soc_probe_codec(struct snd_soc_card *card,
                                "ASoC: failed to probe CODEC %d\n", ret);
                        goto err_probe;
                }
+               WARN(codec->dapm.idle_bias_off &&
+                       codec->dapm.bias_level != SND_SOC_BIAS_OFF,
+                       "codec %s can not start from non-off bias"
+                       " with idle_bias_off==1\n", codec->name);
        }
 
        /* If the driver didn't set I/O up try regmap */
@@ -1883,6 +1888,9 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
        /* remove and free each DAI */
        soc_remove_dai_links(card);
 
+       /* remove any dynamic kcontrols */
+       snd_soc_fw_dcontrols_remove_all(card, SND_SOC_FW_INDEX_ALL);
+
        soc_cleanup_card_debugfs(card);
 
        /* remove the card */
@@ -2224,6 +2232,9 @@ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
        const struct snd_pcm_hardware *hw)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
+       /* TODO: hostless mode - remove from new_pcm() ?*/
+       if (!runtime)
+               return 0;
        runtime->hw.info = hw->info;
        runtime->hw.formats = hw->formats;
        runtime->hw.period_bytes_min = hw->period_bytes_min;
@@ -2406,7 +2417,8 @@ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
        if (uinfo->value.enumerated.item > e->max - 1)
                uinfo->value.enumerated.item = e->max - 1;
        strcpy(uinfo->value.enumerated.name,
-               e->texts[uinfo->value.enumerated.item]);
+               snd_soc_get_enum_text(e, uinfo->value.enumerated.item));
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
@@ -2566,7 +2578,7 @@ int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
        if (uinfo->value.enumerated.item > e->max - 1)
                uinfo->value.enumerated.item = e->max - 1;
        strcpy(uinfo->value.enumerated.name,
-               e->texts[uinfo->value.enumerated.item]);
+               snd_soc_get_enum_text(e, uinfo->value.enumerated.item));
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext);
@@ -3122,9 +3134,12 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
        if (!codec->using_regmap)
                return -EINVAL;
 
-       data = ucontrol->value.bytes.data;
        len = params->num_regs * codec->val_bytes;
 
+       data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+       if (!data)
+               return -ENOMEM;
+
        /*
         * If we've got a mask then we need to preserve the register
         * bits.  We shouldn't modify the incoming data so take a
@@ -3137,10 +3152,6 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
 
                val &= params->mask;
 
-               data = kmemdup(data, len, GFP_KERNEL);
-               if (!data)
-                       return -ENOMEM;
-
                switch (codec->val_bytes) {
                case 1:
                        ((u8 *)data)[0] &= ~params->mask;
@@ -3162,8 +3173,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
        ret = regmap_raw_write(codec->control_data, params->base,
                               data, len);
 
-       if (params->mask)
-               kfree(data);
+       kfree(data);
 
        return ret;
 }
@@ -3629,6 +3639,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
        if (card->rtd == NULL)
                return -ENOMEM;
        card->num_rtd = 0;
+
        card->rtd_aux = &card->rtd[card->num_links];
 
        for (i = 0; i < card->num_links; i++)
@@ -3636,6 +3647,8 @@ int snd_soc_register_card(struct snd_soc_card *card)
 
        INIT_LIST_HEAD(&card->list);
        INIT_LIST_HEAD(&card->dapm_dirty);
+       INIT_LIST_HEAD(&card->denums);
+       INIT_LIST_HEAD(&card->dmixers);
        card->instantiated = 0;
        mutex_init(&card->mutex);
        mutex_init(&card->dapm_mutex);
@@ -3919,6 +3932,8 @@ int snd_soc_register_platform(struct device *dev,
        platform->dapm.platform = platform;
        platform->dapm.stream_event = platform_drv->stream_event;
        mutex_init(&platform->mutex);
+       INIT_LIST_HEAD(&platform->denums);
+       INIT_LIST_HEAD(&platform->dmixers);
 
        mutex_lock(&client_mutex);
        list_add(&platform->list, &platform_list);
@@ -4036,6 +4051,8 @@ int snd_soc_register_codec(struct device *dev,
        codec->driver = codec_drv;
        codec->num_dai = num_dai;
        mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->denums);
+       INIT_LIST_HEAD(&codec->dmixers);
 
        /* allocate CODEC register cache */
        if (codec_drv->reg_cache_size && codec_drv->reg_word_size) {
@@ -4127,6 +4144,38 @@ found:
 }
 EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
 
+int snd_soc_card_new_dai_links(struct snd_soc_card *card,
+       struct snd_soc_dai_link *new, int count)
+{
+       struct snd_soc_dai_link *links;
+       size_t bytes;
+
+       bytes = (count + card->num_links) * sizeof(struct snd_soc_dai_link);
+       links = devm_kzalloc(card->dev, bytes, GFP_KERNEL);
+       if (!links)
+               return -ENOMEM;
+
+       if (card->dai_link) {
+               memcpy(links, card->dai_link,
+                       card->num_links * sizeof(struct snd_soc_dai_link));
+               devm_kfree(card->dev, card->dai_link);
+       }
+       memcpy(links + card->num_links, new,
+               count * sizeof(struct snd_soc_dai_link));
+       card->dai_link = links;
+       card->num_links += count;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_new_dai_links);
+
+void snd_soc_card_reset_dai_links(struct snd_soc_card *card)
+{
+       card->dai_link = NULL;
+       card->num_links = 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_reset_dai_links);
+
 /* Retrieve a card's name from device tree */
 int snd_soc_of_parse_card_name(struct snd_soc_card *card,
                               const char *propname)
@@ -4208,6 +4257,121 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing);
 
+unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
+                                    const char *prefix)
+{
+       int ret, i;
+       char prop[128];
+       unsigned int format = 0;
+       int bit, frame;
+       const char *str;
+       struct {
+               char *name;
+               unsigned int val;
+       } of_fmt_table[] = {
+               { "i2s",        SND_SOC_DAIFMT_I2S },
+               { "right_j",    SND_SOC_DAIFMT_RIGHT_J },
+               { "left_j",     SND_SOC_DAIFMT_LEFT_J },
+               { "dsp_a",      SND_SOC_DAIFMT_DSP_A },
+               { "dsp_b",      SND_SOC_DAIFMT_DSP_B },
+               { "ac97",       SND_SOC_DAIFMT_AC97 },
+               { "pdm",        SND_SOC_DAIFMT_PDM},
+               { "msb",        SND_SOC_DAIFMT_MSB },
+               { "lsb",        SND_SOC_DAIFMT_LSB },
+       }, of_clock_table[] = {
+               { "continuous", SND_SOC_DAIFMT_CONT },
+               { "gated",      SND_SOC_DAIFMT_GATED },
+       };
+
+       if (!prefix)
+               prefix = "";
+
+       /*
+        * check "[prefix]format = xxx"
+        * SND_SOC_DAIFMT_FORMAT_MASK area
+        */
+       snprintf(prop, sizeof(prop), "%sformat", prefix);
+       ret = of_property_read_string(np, prop, &str);
+       if (ret == 0) {
+               for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) {
+                       if (strcmp(str, of_fmt_table[i].name) == 0) {
+                               format |= of_fmt_table[i].val;
+                               break;
+                       }
+               }
+       }
+
+       /*
+        * check "[prefix]clock-gating = xxx"
+        * SND_SOC_DAIFMT_CLOCK_MASK area
+        */
+       snprintf(prop, sizeof(prop), "%sclock-gating", prefix);
+       ret = of_property_read_string(np, prop, &str);
+       if (ret == 0) {
+               for (i = 0; i < ARRAY_SIZE(of_clock_table); i++) {
+                       if (strcmp(str, of_clock_table[i].name) == 0) {
+                               format |= of_clock_table[i].val;
+                               break;
+                       }
+               }
+       }
+
+       /*
+        * check "[prefix]bitclock-inversion"
+        * check "[prefix]frame-inversion"
+        * SND_SOC_DAIFMT_INV_MASK area
+        */
+       snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix);
+       bit = !!of_get_property(np, prop, NULL);
+
+       snprintf(prop, sizeof(prop), "%sframe-inversion", prefix);
+       frame = !!of_get_property(np, prop, NULL);
+
+       switch ((bit << 4) + frame) {
+       case 0x11:
+               format |= SND_SOC_DAIFMT_IB_IF;
+               break;
+       case 0x10:
+               format |= SND_SOC_DAIFMT_IB_NF;
+               break;
+       case 0x01:
+               format |= SND_SOC_DAIFMT_NB_IF;
+               break;
+       default:
+               /* SND_SOC_DAIFMT_NB_NF is default */
+               break;
+       }
+
+       /*
+        * check "[prefix]bitclock-master"
+        * check "[prefix]frame-master"
+        * SND_SOC_DAIFMT_MASTER_MASK area
+        */
+       snprintf(prop, sizeof(prop), "%sbitclock-master", prefix);
+       bit = !!of_get_property(np, prop, NULL);
+
+       snprintf(prop, sizeof(prop), "%sframe-master", prefix);
+       frame = !!of_get_property(np, prop, NULL);
+
+       switch ((bit << 4) + frame) {
+       case 0x11:
+               format |= SND_SOC_DAIFMT_CBM_CFM;
+               break;
+       case 0x10:
+               format |= SND_SOC_DAIFMT_CBM_CFS;
+               break;
+       case 0x01:
+               format |= SND_SOC_DAIFMT_CBS_CFM;
+               break;
+       default:
+               format |= SND_SOC_DAIFMT_CBS_CFS;
+               break;
+       }
+
+       return format;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
+
 static int __init snd_soc_init(void)
 {
 #ifdef CONFIG_DEBUG_FS
index 258acadb9e7d238a97b7af6508ffc21bd1a4488e..e7ae848cf83c7aa611db59e2219c6d6f6bfbe80b 100644 (file)
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/soc-fw.h>
 #include <sound/initval.h>
 
 #include <trace/events/asoc.h>
 
+#define NAME_SIZE      32
 #define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++;
 
 /* dapm power sequences - make this per codec in the future */
@@ -365,7 +367,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
 
                p->connect = 0;
                for (i = 0; i < e->max; i++) {
-                       if (!(strcmp(p->name, e->texts[i])) && item == i)
+                       if (!(strcmp(p->name, snd_soc_get_enum_text(e, i))) && item == i)
                                p->connect = 1;
                }
        }
@@ -381,7 +383,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
                 * that the default mux choice (the first) will be
                 * correctly powered up during initialization.
                 */
-               if (!strcmp(p->name, e->texts[0]))
+               if (!strcmp(p->name, snd_soc_get_enum_text(e, 0)))
                        p->connect = 1;
        }
        break;
@@ -399,7 +401,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
 
                p->connect = 0;
                for (i = 0; i < e->max; i++) {
-                       if (!(strcmp(p->name, e->texts[i])) && item == i)
+                       if (!(strcmp(p->name, snd_soc_get_enum_text(e, i))) && item == i)
                                p->connect = 1;
                }
        }
@@ -445,11 +447,11 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
        int i;
 
        for (i = 0; i < e->max; i++) {
-               if (!(strcmp(control_name, e->texts[i]))) {
+               if (!(strcmp(control_name, snd_soc_get_enum_text(e, i)))) {
                        list_add(&path->list, &dapm->card->paths);
                        list_add(&path->list_sink, &dest->sources);
                        list_add(&path->list_source, &src->sinks);
-                       path->name = (char*)e->texts[i];
+                       path->name = (char*)snd_soc_get_enum_text(e, i);
                        dapm_set_path_status(dest, path, 0);
                        return 0;
                }
@@ -797,10 +799,14 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
        }
 
        switch (widget->id) {
-       case snd_soc_dapm_adc:
-       case snd_soc_dapm_aif_out:
        case snd_soc_dapm_dai:
-               if (widget->active) {
+               if (widget->dai_endpoint) {
+                       widget->outputs = 0;
+                       return widget->outputs;
+               }
+       case snd_soc_dapm_aif_out:
+       case snd_soc_dapm_adc:
+               if (widget->active && list_empty(&widget->sinks)) {
                        widget->outputs = snd_soc_dapm_suspend_check(widget);
                        return widget->outputs;
                }
@@ -886,10 +892,14 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
 
        /* active stream ? */
        switch (widget->id) {
-       case snd_soc_dapm_dac:
-       case snd_soc_dapm_aif_in:
        case snd_soc_dapm_dai:
-               if (widget->active) {
+               if (widget->dai_endpoint) {
+                       widget->inputs = 0;
+                       return widget->inputs;
+               }
+       case snd_soc_dapm_aif_in:
+       case snd_soc_dapm_dac:
+               if (widget->active && list_empty(&widget->sources)) {
                        widget->inputs = snd_soc_dapm_suspend_check(widget);
                        return widget->inputs;
                }
@@ -969,6 +979,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
  * Queries DAPM graph as to whether an valid audio stream path exists for
  * the initial stream specified by name. This takes into account
  * current mixer and mux kcontrol settings. Creates list of valid widgets.
+ * Intended for internal use atm where caller holds the card mutex.
  *
  * Returns the number of valid paths or negative error.
  */
@@ -978,7 +989,6 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
        struct snd_soc_card *card = dai->card;
        int paths;
 
-       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
        dapm_reset(card);
 
        if (stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -988,7 +998,6 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
 
        trace_snd_soc_dapm_connected(paths, stream);
        dapm_clear_walk(&card->dapm);
-       mutex_unlock(&card->dapm_mutex);
 
        return paths;
 }
@@ -1113,7 +1122,7 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
 
        DAPM_UPDATE_STAT(w, power_checks);
 
-       if (w->active) {
+       if (w->active && list_empty(&w->sinks)) {
                in = is_connected_input_ep(w, NULL);
                dapm_clear_walk(w->dapm);
                return in != 0;
@@ -1129,7 +1138,7 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
 
        DAPM_UPDATE_STAT(w, power_checks);
 
-       if (w->active) {
+       if (w->active && list_empty(&w->sources)) {
                out = is_connected_output_ep(w, NULL);
                dapm_clear_walk(w->dapm);
                return out != 0;
@@ -1532,7 +1541,7 @@ static void dapm_widget_set_peer_power(struct snd_soc_dapm_widget *peer,
 
        /* If the peer is already in the state we're moving to then we
         * won't have an impact on it. */
-       if (power != peer->power)
+       if (power != peer->power || peer->id == snd_soc_dapm_dai)
                dapm_mark_dirty(peer, "peer state change");
 }
 
@@ -1542,7 +1551,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
 {
        struct snd_soc_dapm_path *path;
 
-       if (w->power == power)
+       if (w->power == power && w->id != snd_soc_dapm_dai)
                return;
 
        trace_snd_soc_dapm_widget_power(w, power);
@@ -1909,12 +1918,12 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
                if (path->kcontrol != kcontrol)
                        continue;
 
-               if (!path->name || !e->texts[mux])
+               if (!path->name || !snd_soc_get_enum_text(e, mux))
                        continue;
 
                found = 1;
                /* we now need to match the string in the enum to the path */
-               if (!(strcmp(path->name, e->texts[mux]))) {
+               if (!(strcmp(path->name, snd_soc_get_enum_text(e, mux)))) {
                        path->connect = 1; /* new connection */
                        dapm_mark_dirty(path->source, "mux connection");
                } else {
@@ -2093,6 +2102,11 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
                        kfree(p->long_name);
                        kfree(p);
                }
+
+               /* check and free and dynamic widget kcontrols */
+               if (w->index)
+                       snd_soc_fw_dcontrols_remove_widget(w);
+
                kfree(w->kcontrols);
                kfree(w->name);
                kfree(w);
@@ -3027,6 +3041,11 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        size_t name_len;
        int ret;
 
+       if (dapm_find_widget(dapm, widget->name, false)) {
+               dev_err(dapm->dev, "widget %s already exists for context\n",
+                       widget->name);
+       }
+
        if ((w = dapm_cnew_widget(widget)) == NULL)
                return NULL;
 
@@ -3330,46 +3349,68 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 {
        struct snd_soc_dapm_widget template;
        struct snd_soc_dapm_widget *w;
+       char name[NAME_SIZE];
 
        WARN_ON(dapm->dev != dai->dev);
 
        memset(&template, 0, sizeof(template));
        template.reg = SND_SOC_NOPM;
 
+       /* DAIs can belong to codecs and platform component devices so check
+          that we have not been created already with component */
+       if (dai->playback_widget || dai->capture_widget)
+               return 0;
+
+       if (!dai->driver->playback.channels_max)
+               goto capture;
+
        if (dai->driver->playback.stream_name) {
-               template.id = snd_soc_dapm_dai;
                template.name = dai->driver->playback.stream_name;
                template.sname = dai->driver->playback.stream_name;
+       } else {
+               snprintf(name, NAME_SIZE, "%s %s", dev_name(dai->dev), "Playback");
+               template.name = name;
+               template.sname = name;
+       }
 
-               dev_dbg(dai->dev, "ASoC: adding %s widget\n",
-                       template.name);
+       template.id = snd_soc_dapm_dai;
 
-               w = snd_soc_dapm_new_control(dapm, &template);
-               if (!w) {
-                       dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
-                               dai->driver->playback.stream_name);
-               }
+       dev_dbg(dai->dev, "ASoC: adding %s widget\n",
+               template.name);
 
-               w->priv = dai;
+       w = snd_soc_dapm_new_control(dapm, &template);
+       if (!w) {
+               dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
+                       dai->driver->playback.stream_name);
+       } else {
                dai->playback_widget = w;
+               w->dai = dai;
        }
 
+capture:
+       if (!dai->driver->capture.channels_max)
+               return 0;
+
        if (dai->driver->capture.stream_name) {
-               template.id = snd_soc_dapm_dai;
                template.name = dai->driver->capture.stream_name;
                template.sname = dai->driver->capture.stream_name;
+       } else {
+               snprintf(name, NAME_SIZE, "%s %s", dev_name(dai->dev), "Capture");
+               template.name = name;
+               template.sname = name;
+       }
+       template.id = snd_soc_dapm_dai;
 
-               dev_dbg(dai->dev, "ASoC: adding %s widget\n",
-                       template.name);
-
-               w = snd_soc_dapm_new_control(dapm, &template);
-               if (!w) {
-                       dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
-                               dai->driver->capture.stream_name);
-               }
+       dev_dbg(dai->dev, "ASoC: adding %s widget\n",
+               template.name);
 
-               w->priv = dai;
+       w = snd_soc_dapm_new_control(dapm, &template);
+       if (!w) {
+               dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
+                       dai->driver->capture.stream_name);
+       } else {
                dai->capture_widget = w;
+               w->dai = dai;
        }
 
        return 0;
@@ -3388,7 +3429,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
                if (dai_w->id != snd_soc_dapm_dai)
                        continue;
 
-               dai = dai_w->priv;
+               dai = dai_w->dai;
 
                /* ...find all widgets with the same stream and link them */
                list_for_each_entry(w, &card->widgets, list) {
@@ -3506,6 +3547,54 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
        mutex_unlock(&card->dapm_mutex);
 }
 
+/**
+ * snd_soc_dapm_dai_stream_event - send a stream event to the dapm core
+ * @DAI: Digital Audio interface
+ * @stream: stream name
+ * @event: stream event
+ *
+ * Sends a stream event to the dapm core. The core then makes any
+ * necessary widget power changes.
+ *
+ * !!! TODO: Investigate RTD for 6040 mute
+ *
+ * Returns 0 for success else error.
+ */
+void snd_soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
+       int event)
+{
+       struct snd_soc_dapm_widget *w;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               w = dai->playback_widget;
+       else
+               w = dai->capture_widget;
+
+       if (!w)
+               return;
+
+       dapm_mark_dirty(w, "stream event");
+       w->dai_endpoint = 1;
+
+       switch (event) {
+       case SND_SOC_DAPM_STREAM_START:
+               w->active = 1;
+               break;
+       case SND_SOC_DAPM_STREAM_STOP:
+               w->active = 0;
+               break;
+       case SND_SOC_DAPM_STREAM_SUSPEND:
+       case SND_SOC_DAPM_STREAM_RESUME:
+       case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
+       case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
+               break;
+       }
+
+       dapm_power_widgets(&dai->card->dapm, event);
+       w->dai_endpoint = 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_stream_event);
+
 /**
  * snd_soc_dapm_enable_pin - enable pin.
  * @dapm: DAPM context
diff --git a/sound/soc/soc-fw.c b/sound/soc/soc-fw.c
new file mode 100644 (file)
index 0000000..ac2f1e3
--- /dev/null
@@ -0,0 +1,1558 @@
+/*
+ * soc-fw.c  --  ALSA SoC Firmware
+ *
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * Author: Liam Girdwood <lrg@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.
+ *
+ *  Support for audio fimrware to contain kcontrols, DAPM graphs, widgets,
+ *  DAIs, equalizers, firmware, coefficienst etc.
+ *
+ *  This file only manages the DAPM and Kcontrol components, all other firmware
+ *  data is passed to component drivers for bespoke handling.
+ */
+
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-fw.h>
+
+/*
+ * We make several passes over the data (since it wont necessarily be ordered)
+ * and process objects in the following order. This guarantees the component
+ * drivers will be ready with any vendor data before the mixers and DAPM objects
+ * are loaded (that may make use of the vendor data).
+ */
+#define SOC_FW_PASS_VENDOR     0
+#define SOC_FW_PASS_MIXER      1
+#define SOC_FW_PASS_COEFF      SOC_FW_PASS_MIXER
+#define SOC_FW_PASS_WIDGET     2
+#define SOC_FW_PASS_GRAPH      3
+#define SOC_FW_PASS_PINS       4
+
+#define SOC_FW_PASS_START      SOC_FW_PASS_VENDOR
+#define SOC_FW_PASS_END        SOC_FW_PASS_PINS
+
+struct soc_fw {
+       const char *file;
+       const struct firmware *fw;
+
+       /* runtime FW parsing */
+       const u8 *pos;          /* read postion */
+       const u8 *hdr_pos;      /* header position */
+       unsigned int pass;      /* pass number */
+
+       /* component caller */
+       struct device *dev;
+       struct snd_soc_codec *codec;
+       struct snd_soc_platform *platform;
+       struct snd_soc_card *card;
+       u32 index;
+
+       /* kcontrol operations */
+       const struct snd_soc_fw_kcontrol_ops *io_ops;
+       int io_ops_count;
+
+       /* optional fw loading callbacks to component drivers */
+       union {
+               struct snd_soc_fw_codec_ops *codec_ops;
+               struct snd_soc_fw_platform_ops *platform_ops;
+               struct snd_soc_fw_card_ops *card_ops;
+       };
+};
+
+static int soc_fw_process_headers(struct soc_fw *sfw);
+static void soc_fw_complete(struct soc_fw *sfw);
+
+/* List of Kcontrol types and associated operations. */
+static const struct snd_soc_fw_kcontrol_ops io_ops[] = {
+       {SOC_CONTROL_IO_VOLSW, snd_soc_get_volsw,
+               snd_soc_put_volsw, snd_soc_info_volsw},
+       {SOC_CONTROL_IO_VOLSW_SX, snd_soc_get_volsw_sx,
+               snd_soc_put_volsw_sx, NULL},
+       {SOC_CONTROL_IO_VOLSW_S8, snd_soc_get_volsw_s8,
+               snd_soc_put_volsw_s8, snd_soc_info_volsw_s8},
+       {SOC_CONTROL_IO_ENUM, snd_soc_get_enum_double,
+               snd_soc_put_enum_double, snd_soc_info_enum_double},
+       {SOC_CONTROL_IO_ENUM_EXT, NULL,
+               NULL, snd_soc_info_enum_ext},
+       {SOC_CONTROL_IO_BYTES, snd_soc_bytes_get,
+               snd_soc_bytes_put, snd_soc_bytes_info},
+       {SOC_CONTROL_IO_BOOL_EXT, NULL,
+               NULL, snd_ctl_boolean_mono_info},
+       {SOC_CONTROL_IO_ENUM_VALUE, snd_soc_get_value_enum_double,
+               snd_soc_put_value_enum_double, NULL},
+       {SOC_CONTROL_IO_RANGE, snd_soc_get_volsw_range,
+               snd_soc_put_volsw_range, snd_soc_info_volsw_range},
+       {SOC_CONTROL_IO_VOLSW_XR_SX, snd_soc_get_xr_sx,
+               snd_soc_put_xr_sx, snd_soc_info_xr_sx},
+       {SOC_CONTROL_IO_STROBE, snd_soc_get_strobe,
+               snd_soc_put_strobe, NULL},
+
+       {SOC_DAPM_IO_VOLSW, snd_soc_dapm_get_volsw,
+               snd_soc_dapm_put_volsw, NULL},
+       {SOC_DAPM_IO_ENUM_DOUBLE, snd_soc_dapm_get_enum_double,
+               snd_soc_dapm_put_enum_double, snd_soc_info_enum_double},
+       {SOC_DAPM_IO_ENUM_VIRT, snd_soc_dapm_get_enum_virt,
+               snd_soc_dapm_put_enum_virt, NULL},
+       {SOC_DAPM_IO_ENUM_VALUE, snd_soc_dapm_get_value_enum_double,
+               snd_soc_dapm_put_value_enum_double, NULL},
+       {SOC_DAPM_IO_PIN, snd_soc_dapm_get_pin_switch,
+               snd_soc_dapm_put_pin_switch, snd_soc_dapm_info_pin_switch},
+};
+
+static inline void soc_fw_list_add_enum(struct soc_fw *sfw, struct soc_enum *se)
+{
+       if (sfw->codec)
+               list_add(&se->list, &sfw->codec->denums);
+       else if (sfw->platform)
+               list_add(&se->list, &sfw->platform->denums);
+       else if (sfw->card)
+               list_add(&se->list, &sfw->card->denums);
+       else
+               BUG();
+}
+
+static inline void soc_fw_list_add_mixer(struct soc_fw *sfw,
+       struct soc_mixer_control *mc)
+{
+       if (sfw->codec)
+               list_add(&mc->list, &sfw->codec->dmixers);
+       else if (sfw->platform)
+               list_add(&mc->list, &sfw->platform->dmixers);
+       else if (sfw->card)
+               list_add(&mc->list, &sfw->card->dmixers);
+       else
+               BUG();
+}
+
+static inline struct snd_soc_dapm_context *soc_fw_dapm_get(struct soc_fw *sfw)
+{
+       if (sfw->codec)
+               return &sfw->codec->dapm;
+       else if (sfw->platform)
+               return &sfw->platform->dapm;
+       else if (sfw->card)
+               return &sfw->card->dapm;
+       BUG();
+}
+
+static inline struct snd_soc_card *soc_fw_card_get(struct soc_fw *sfw)
+{
+       if (sfw->codec)
+               return sfw->codec->card;
+       else if (sfw->platform)
+               return sfw->platform->card;
+       else if (sfw->card)
+               return sfw->card;
+       BUG();
+}
+
+/* check we dont overflow the data for this control chunk */
+static int soc_fw_check_control_count(struct soc_fw *sfw, size_t elem_size,
+       unsigned int count, size_t bytes)
+{
+       const u8 *end = sfw->pos + elem_size * count;
+
+       if (end > sfw->fw->data + sfw->fw->size) {
+               dev_err(sfw->dev, "ASoC: controls overflow end of data\n");
+               return -EINVAL;
+       }
+
+       /* check there is enough room in chunk for control.
+          extra bytes at the end of control are for vendor data here  */
+       if (elem_size * count > bytes) {
+               dev_err(sfw->dev,
+                       "ASoC: controls count %d of elem size %d are bigger than chunk %d\n",
+                       count, elem_size, bytes);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static inline int soc_fw_is_eof(struct soc_fw *sfw)
+{
+       const u8 *end = sfw->hdr_pos;
+
+       if (end >= sfw->fw->data + sfw->fw->size)
+               return 1;
+       return 0;
+}
+
+static inline unsigned int soc_fw_get_hdr_offset(struct soc_fw *sfw)
+{
+       return (unsigned int)(sfw->hdr_pos - sfw->fw->data);
+}
+
+static inline unsigned int soc_fw_get_offset(struct soc_fw *sfw)
+{
+       return (unsigned int)(sfw->pos - sfw->fw->data);
+}
+
+/* pass vendor data to component driver for processing */
+static int soc_fw_vendor_load_(struct soc_fw *sfw, struct snd_soc_fw_hdr *hdr)
+{
+       int ret = 0;
+
+       if (sfw->codec && sfw->codec_ops && sfw->codec_ops->vendor_load)
+               ret = sfw->codec_ops->vendor_load(sfw->codec, hdr);
+
+       if (sfw->platform && sfw->platform_ops && sfw->platform_ops->vendor_load)
+               ret = sfw->platform_ops->vendor_load(sfw->platform, hdr);
+
+       if (sfw->card && sfw->card_ops && sfw->card_ops->vendor_load)
+               ret = sfw->card_ops->vendor_load(sfw->card, hdr);
+
+       if (ret < 0)
+               dev_err(sfw->dev, "ASoC: vendor load failed at hdr offset %d/0x%x for type %d:%d\n",
+                       soc_fw_get_hdr_offset(sfw), soc_fw_get_hdr_offset(sfw),
+                       hdr->type, hdr->vendor_type);
+       return ret;
+}
+
+/* pass vendor data to component driver for processing */
+static int soc_fw_vendor_load(struct soc_fw *sfw, struct snd_soc_fw_hdr *hdr)
+{
+       if (sfw->pass != SOC_FW_PASS_VENDOR)
+               return 0;
+
+       return soc_fw_vendor_load_(sfw, hdr);
+}
+
+/* optionally pass new dynamic widget to component driver. mainly for external
+ widgets where we can assign private data/ops */
+static int soc_fw_widget_load(struct soc_fw *sfw, struct snd_soc_dapm_widget *w)
+{
+       if (sfw->codec && sfw->codec_ops && sfw->codec_ops->widget_load)
+               return sfw->codec_ops->widget_load(sfw->codec, w);
+
+       if (sfw->platform && sfw->platform_ops && sfw->platform_ops->widget_load)
+               return sfw->platform_ops->widget_load(sfw->platform, w);
+
+       if (sfw->card && sfw->card_ops && sfw->card_ops->widget_load)
+               return sfw->card_ops->widget_load(sfw->card, w);
+
+       dev_dbg(sfw->dev, "ASoC: no handler specified for ext widget %s\n",
+               w->name);
+       return 0;
+}
+
+/* tell the component driver that all firmware has been loaded in this request */
+static void soc_fw_complete(struct soc_fw *sfw)
+{
+       if (sfw->codec && sfw->codec_ops && sfw->codec_ops->complete)
+               sfw->codec_ops->complete(sfw->codec);
+       if (sfw->platform && sfw->platform_ops && sfw->platform_ops->complete)
+               sfw->platform_ops->complete(sfw->platform);
+       if (sfw->card && sfw->card_ops && sfw->card_ops->complete)
+               sfw->card_ops->complete(sfw->card);
+}
+
+/* add a dynamic kcontrol */
+static int soc_fw_add_dcontrol(struct snd_card *card, struct device *dev,
+       const struct snd_kcontrol_new *control_new, const char *prefix,
+       void *data, struct snd_kcontrol **kcontrol)
+{
+       int err;
+
+       *kcontrol = snd_soc_cnew(control_new, data, control_new->name, prefix);
+       if (*kcontrol == NULL) {
+               dev_err(dev, "ASoC: Failed to create new kcontrol %s\n",
+               control_new->name);
+               return -ENOMEM;
+       }
+
+       err = snd_ctl_add(card, *kcontrol);
+       if (err < 0) {
+               kfree(*kcontrol);
+               dev_err(dev, "ASoC: Failed to add %s: %d\n", control_new->name,
+                       err);
+               return err;
+       }
+
+       return 0;
+}
+
+/* add a dynamic kcontrol for component driver */
+static int soc_fw_add_kcontrol(struct soc_fw *sfw, struct snd_kcontrol_new *k,
+       struct snd_kcontrol **kcontrol)
+{
+       if (sfw->codec) {
+               struct snd_soc_codec *codec = sfw->codec;
+
+               return soc_fw_add_dcontrol(codec->card->snd_card, codec->dev,
+                               k, codec->name_prefix, codec, kcontrol);
+       } else if (sfw->platform) {
+               struct snd_soc_platform *platform = sfw->platform;
+
+               return soc_fw_add_dcontrol(platform->card->snd_card,
+                               platform->dev, k, NULL, platform, kcontrol);
+       } else if (sfw->card) {
+               struct snd_soc_card *card = sfw->card;
+
+               return soc_fw_add_dcontrol(card->snd_card, card->dev,
+                               k, NULL, card, kcontrol);
+       } else
+               dev_dbg(sfw->dev,
+                       "ASoC: no handler specified for kcontrol %s\n", k->name);
+       return 0;
+}
+
+/* bind a kcontrol to it's IO handlers */
+static int soc_fw_kcontrol_bind_io(u32 io_type, struct snd_kcontrol_new *k,
+       const struct snd_soc_fw_kcontrol_ops *ops, int num_ops)
+{
+       int i;
+
+       for (i = 0; i < num_ops; i++) {
+
+               if (SOC_CONTROL_GET_ID_PUT(ops[i].id) ==
+                       SOC_CONTROL_GET_ID_PUT(io_type) && ops[i].put)
+                       k->put = ops[i].put;
+               if (SOC_CONTROL_GET_ID_GET(ops[i].id) ==
+                       SOC_CONTROL_GET_ID_GET(io_type) && ops[i].get)
+                       k->get = ops[i].get;
+               if (SOC_CONTROL_GET_ID_INFO(ops[i].id) ==
+                       SOC_CONTROL_GET_ID_INFO(io_type) && ops[i].info)
+                       k->info = ops[i].info;
+       }
+
+       /* let the caller know if we need to bind external kcontrols */
+       if (!k->put || !k->get || !k->info)
+               return 1;
+
+       return 0;
+}
+
+/* optionally pass new dynamic kcontrol to component driver. */
+static int soc_fw_init_kcontrol(struct soc_fw *sfw, struct snd_kcontrol_new *k)
+{
+       if (sfw->codec && sfw->codec_ops && sfw->codec_ops->control_load)
+               return sfw->codec_ops->control_load(sfw->codec, k);
+
+       if (sfw->platform && sfw->platform_ops && sfw->platform_ops->control_load)
+               return sfw->platform_ops->control_load(sfw->platform, k);
+
+       if (sfw->card && sfw->card_ops && sfw->card_ops->control_load)
+               return sfw->card_ops->control_load(sfw->card, k);
+
+       dev_dbg(sfw->dev, "ASoC: no handler specified for kcontrol %s\n",
+               k->name);
+       return 0;
+}
+
+static int soc_fw_create_tlv(struct soc_fw *sfw, struct snd_kcontrol_new *kc,
+       u32 tlv_size)
+{
+       struct snd_soc_fw_ctl_tlv *fw_tlv;
+       struct snd_ctl_tlv *tlv;
+
+       if (tlv_size == 0)
+               return 0;
+
+       fw_tlv = (struct snd_soc_fw_ctl_tlv *) sfw->pos;
+       sfw->pos += tlv_size;
+
+       tlv = kzalloc(sizeof(*tlv) + tlv_size, GFP_KERNEL);
+       if (tlv == NULL)
+               return -ENOMEM;
+
+       dev_dbg(sfw->dev, " created TLV type %d size %d bytes\n",
+               fw_tlv->numid, fw_tlv->length);
+       tlv->numid = fw_tlv->numid;
+       tlv->length = fw_tlv->length;
+       memcpy(tlv->tlv, fw_tlv + 1, fw_tlv->length);
+       kc->tlv.p = (void*)tlv;
+
+       return 0;
+}
+
+static inline void soc_fw_free_tlv(struct soc_fw *sfw,
+       struct snd_kcontrol_new *kc)
+{
+       kfree(kc->tlv.p);
+}
+
+static int soc_fw_dmixer_create(struct soc_fw *sfw, unsigned int count,
+       size_t size)
+{
+       struct snd_soc_fw_mixer_control *mc;
+       struct soc_mixer_control *sm;
+       struct snd_kcontrol_new kc;
+       int i, err, ext;
+
+       if (soc_fw_check_control_count(sfw,
+               sizeof(struct snd_soc_fw_mixer_control), count, size)) {
+               dev_err(sfw->dev, "ASoC: invalid count %d for controls\n",
+                       count);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < count; i++) {
+               mc = (struct snd_soc_fw_mixer_control*)sfw->pos;
+               sfw->pos += sizeof(struct snd_soc_fw_mixer_control);
+
+               /* validate kcontrol */
+               if (strnlen(mc->hdr.name, SND_SOC_FW_TEXT_SIZE) ==
+                       SND_SOC_FW_TEXT_SIZE)
+                       return -EINVAL;
+
+               sm = kzalloc(sizeof(*sm), GFP_KERNEL);
+               if (!sm)
+                       return -ENOMEM;
+
+               dev_dbg(sfw->dev,
+                       "ASoC: adding mixer kcontrol %s with access 0x%x\n",
+                       mc->hdr.name, mc->hdr.access);
+
+               memset(&kc, 0, sizeof(kc));
+               kc.name = mc->hdr.name;
+               kc.private_value = (long)sm;
+               kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+               kc.access = mc->hdr.access;
+
+               sm->reg = mc->reg;
+               sm->rreg = mc->rreg;
+               sm->shift = mc->shift;
+               sm->rshift = mc->rshift;
+               sm->max = mc->max;
+               sm->min = mc->min;
+               sm->invert = mc->invert;
+               sm->platform_max = mc->platform_max;
+               sm->index = sfw->index;
+               INIT_LIST_HEAD(&sm->list);
+
+               /* map standard io handlers and check for external handlers */
+               ext = soc_fw_kcontrol_bind_io(mc->hdr.index, &kc, io_ops,
+                       ARRAY_SIZE(io_ops));
+               if (ext) {
+                       /* none exist, so now try and map ext handlers */
+                       ext = soc_fw_kcontrol_bind_io(mc->hdr.index, &kc,
+                               sfw->io_ops, sfw->io_ops_count);
+                       if (ext) {
+                               dev_err(sfw->dev,
+                                       "ASoC: no complete mixer IO handler for %s type (g,p,i) %d:%d:%d\n",
+                                       mc->hdr.name,
+                                       SOC_CONTROL_GET_ID_GET(mc->hdr.index),
+                                       SOC_CONTROL_GET_ID_PUT(mc->hdr.index),
+                                       SOC_CONTROL_GET_ID_INFO(mc->hdr.index));
+                               kfree(sm);
+                               continue;
+                       }
+
+                       err = soc_fw_init_kcontrol(sfw, &kc);
+                       if (err < 0) {
+                               dev_err(sfw->dev, "ASoC: failed to init %s\n",
+                                       mc->hdr.name);
+                               kfree(sm);
+                               continue;
+                       }
+               }
+
+               /* create any TLV data */
+               soc_fw_create_tlv(sfw, &kc, mc->hdr.tlv_size);
+
+               /* register control here */
+               err = soc_fw_add_kcontrol(sfw, &kc, &sm->dcontrol);
+               if (err < 0) {
+                       dev_err(sfw->dev, "ASoC: failed to add %s\n", mc->hdr.name);
+                       soc_fw_free_tlv(sfw, &kc);
+                       kfree(sm);
+                       continue;
+               }
+
+               soc_fw_list_add_mixer(sfw, sm);
+       }
+
+       return 0;
+}
+
+static inline void soc_fw_denum_free_data(struct soc_enum *se)
+{
+       int i;
+
+       if (se->dvalues)
+               kfree(se->dvalues);
+       for (i = 0; i < se->max - 1; i++)
+               kfree(se->dtexts[i]);
+}
+
+static int soc_fw_denum_create_texts(struct soc_enum *se,
+       struct snd_soc_fw_enum_control *ec)
+{
+       int i, ret;
+
+       se->dtexts = kzalloc(sizeof(char *) * ec->max, GFP_KERNEL);
+       if (se->dtexts == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < ec->max; i++) {
+
+               if (strnlen(ec->texts[i], SND_SOC_FW_TEXT_SIZE) ==
+                       SND_SOC_FW_TEXT_SIZE) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               se->dtexts[i] = kstrdup(ec->texts[i], GFP_KERNEL);
+               if (!se->dtexts[i]) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+       }
+
+       return 0;
+
+err:
+       for (--i; i >= 0; i--)
+               kfree(se->dtexts[i]);
+       kfree(se->dtexts);
+       return ret;
+}
+
+static int soc_fw_denum_create_values(struct soc_enum *se,
+       struct snd_soc_fw_enum_control *ec)
+{
+       if (ec->max > sizeof(*ec->values))
+               return -EINVAL;
+
+       se->dvalues = kmalloc(ec->max * sizeof(u32), GFP_KERNEL);
+       if (!se->dvalues)
+               return -ENOMEM;
+
+       memcpy(se->dvalues, ec->values, ec->max * sizeof(u32));
+       return 0;
+}
+
+static int soc_fw_denum_create(struct soc_fw *sfw, unsigned int count,
+       size_t size)
+{
+       struct snd_soc_fw_enum_control *ec;
+       struct soc_enum *se;
+       struct snd_kcontrol_new kc;
+       int i, ret, err, ext;
+
+       if (soc_fw_check_control_count(sfw,
+               sizeof(struct snd_soc_fw_enum_control), count, size)) {
+               dev_err(sfw->dev, "ASoC: invalid count %d for enum controls\n",
+                       count);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < count; i++) {
+               ec = (struct snd_soc_fw_enum_control*)sfw->pos;
+               sfw->pos += sizeof(struct snd_soc_fw_enum_control);
+
+               /* validate kcontrol */
+               if (strnlen(ec->hdr.name, SND_SOC_FW_TEXT_SIZE) ==
+                       SND_SOC_FW_TEXT_SIZE)
+                       return -EINVAL;
+
+               se = kzalloc(sizeof(*se), GFP_KERNEL);
+               if (!se)
+                       return -ENOMEM;
+
+               dev_dbg(sfw->dev, "ASoC: adding enum kcontrol %s size %d\n",
+                       ec->hdr.name, ec->max);
+
+               memset(&kc, 0, sizeof(kc));
+               kc.name = ec->hdr.name;
+               kc.private_value = (long)se;
+               kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+               kc.access = ec->hdr.access;
+
+               se->reg = ec->reg;
+               se->reg2 = ec->reg2;
+               se->shift_l = ec->shift_l;
+               se->shift_r = ec->shift_r;
+               se->max = ec->max;
+               se->mask = ec->mask;
+               se->index = sfw->index;
+               INIT_LIST_HEAD(&se->list);
+
+               switch (SOC_CONTROL_GET_ID_INFO(ec->hdr.index)) {
+               case SOC_DAPM_TYPE_ENUM_VALUE:
+               case SOC_CONTROL_TYPE_ENUM_VALUE:
+                       err = soc_fw_denum_create_values(se, ec);
+                       if (err < 0) {
+                               dev_err(sfw->dev,
+                                       "ASoC: could not create values for %s\n",
+                                       ec->hdr.name);
+                               kfree(se);
+                               continue;
+                       }
+                       /* fall through and create texts */
+               case SOC_CONTROL_TYPE_ENUM:
+               case SOC_CONTROL_TYPE_ENUM_EXT:
+               case SOC_DAPM_TYPE_ENUM_EXT:
+               case SOC_DAPM_TYPE_ENUM_DOUBLE:
+               case SOC_DAPM_TYPE_ENUM_VIRT:
+                       err = soc_fw_denum_create_texts(se, ec);
+                       if (err < 0) {
+                               dev_err(sfw->dev,
+                                       "ASoC: could not create texts for %s\n",
+                                       ec->hdr.name);
+                               kfree(se);
+                               continue;
+                       }
+                       break;
+               default:
+                       dev_err(sfw->dev,
+                               "ASoC: invalid enum control type %d for %s\n",
+                               ec->hdr.index, ec->hdr.name);
+                       kfree(se);
+                       continue;
+               }
+
+               /* map standard io handlers and check for external handlers */
+               ext = soc_fw_kcontrol_bind_io(ec->hdr.index, &kc, io_ops,
+                       ARRAY_SIZE(io_ops));
+               if (ext) {
+                       /* none exist, so now try and map ext handlers */
+                       ext = soc_fw_kcontrol_bind_io(ec->hdr.index, &kc,
+                               sfw->io_ops, sfw->io_ops_count);
+                       if (ext) {
+                               dev_err(sfw->dev, "ASoC: no complete enum IO handler for %s type (g,p,i) %d:%d:%d\n",
+                                       ec->hdr.name,
+                                       SOC_CONTROL_GET_ID_GET(ec->hdr.index),
+                                       SOC_CONTROL_GET_ID_PUT(ec->hdr.index),
+                                       SOC_CONTROL_GET_ID_INFO(ec->hdr.index));
+                               kfree(se);
+                               continue;
+                       }
+
+                       err = soc_fw_init_kcontrol(sfw, &kc);
+                       if (err < 0) {
+                               dev_err(sfw->dev, "ASoC: failed to init %s\n",
+                                       ec->hdr.name);
+                               kfree(se);
+                               continue;
+                       }
+               }
+
+               /* register control here */
+               ret = soc_fw_add_kcontrol(sfw, &kc, &se->dcontrol);
+               if (ret < 0) {
+                       dev_err(sfw->dev, "ASoC: could not add kcontrol %s\n",
+                               ec->hdr.name);
+                       kfree(se);
+                       continue;
+               }
+
+               soc_fw_list_add_enum(sfw, se);
+       }
+
+       return 0;
+}
+
+static int soc_fw_kcontrol_load(struct soc_fw *sfw, struct snd_soc_fw_hdr *hdr)
+{
+       struct snd_soc_fw_kcontrol *sfwk =
+               (struct snd_soc_fw_kcontrol*)sfw->pos;
+       struct snd_soc_fw_control_hdr *control_hdr;
+       int i;
+
+       if (sfw->pass != SOC_FW_PASS_MIXER) {
+               sfw->pos += sizeof(struct snd_soc_fw_kcontrol) + hdr->size;
+               return 0;
+       }
+
+       sfw->pos += sizeof(struct snd_soc_fw_kcontrol);
+       control_hdr = (struct snd_soc_fw_control_hdr*)sfw->pos;
+
+       dev_dbg(sfw->dev, "ASoC: adding %d kcontrols\n", sfwk->count);
+
+       for (i = 0; i < sfwk->count; i++) {
+               switch (SOC_CONTROL_GET_ID_INFO(control_hdr->index)) {
+               case SOC_CONTROL_TYPE_VOLSW:
+               case SOC_CONTROL_TYPE_STROBE:
+               case SOC_CONTROL_TYPE_VOLSW_SX:
+               case SOC_CONTROL_TYPE_VOLSW_S8:
+               case SOC_CONTROL_TYPE_VOLSW_XR_SX:
+               case SOC_CONTROL_TYPE_BYTES:
+               case SOC_CONTROL_TYPE_BOOL_EXT:
+               case SOC_CONTROL_TYPE_RANGE:
+               case SOC_DAPM_TYPE_VOLSW:
+               case SOC_DAPM_TYPE_PIN:
+                       soc_fw_dmixer_create(sfw, 1, hdr->size);
+                       break;
+               case SOC_CONTROL_TYPE_ENUM:
+               case SOC_CONTROL_TYPE_ENUM_EXT:
+               case SOC_CONTROL_TYPE_ENUM_VALUE:
+               case SOC_DAPM_TYPE_ENUM_DOUBLE:
+               case SOC_DAPM_TYPE_ENUM_VIRT:
+               case SOC_DAPM_TYPE_ENUM_VALUE:
+               case SOC_DAPM_TYPE_ENUM_EXT:
+                       soc_fw_denum_create(sfw, 1, hdr->size);
+                       break;
+               default:
+                       dev_err(sfw->dev, "ASoC: invalid control type %d:%d:%d count %d\n",
+                               SOC_CONTROL_GET_ID_GET(control_hdr->index),
+                               SOC_CONTROL_GET_ID_PUT(control_hdr->index),
+                               SOC_CONTROL_GET_ID_INFO(control_hdr->index),
+                               sfwk->count);
+               }
+       }
+
+       return 0;
+}
+
+static int soc_fw_dapm_graph_load(struct soc_fw *sfw,
+       struct snd_soc_fw_hdr *hdr)
+{
+       struct snd_soc_dapm_context *dapm = soc_fw_dapm_get(sfw);
+       struct snd_soc_dapm_route route;
+       struct snd_soc_fw_dapm_elems *elem_info =
+               (struct snd_soc_fw_dapm_elems*)sfw->pos;
+       struct snd_soc_fw_dapm_graph_elem *elem;
+       int count = elem_info->count, i;
+
+       if (sfw->pass != SOC_FW_PASS_GRAPH) {
+               sfw->pos += sizeof(struct snd_soc_fw_dapm_elems) + hdr->size;
+               return 0;
+       }
+
+       sfw->pos += sizeof(struct snd_soc_fw_dapm_elems);
+
+       if (soc_fw_check_control_count(sfw,
+               sizeof(struct snd_soc_fw_dapm_graph_elem), count, hdr->size)) {
+               dev_err(sfw->dev, "ASoC: invalid count %d for DAPM routes\n",
+                       count);
+               return -EINVAL;
+       }
+
+       dev_dbg(sfw->dev, "ASoC: adding %d DAPM routes\n", count);
+
+       for (i = 0; i < count; i++) {
+               elem = (struct snd_soc_fw_dapm_graph_elem *)sfw->pos;
+               sfw->pos += sizeof(struct snd_soc_fw_dapm_graph_elem);
+
+               /* validate routes */
+               if (strnlen(elem->source, SND_SOC_FW_TEXT_SIZE) ==
+                       SND_SOC_FW_TEXT_SIZE)
+                       return -EINVAL;
+               if (strnlen(elem->sink, SND_SOC_FW_TEXT_SIZE) ==
+                       SND_SOC_FW_TEXT_SIZE)
+                       return -EINVAL;
+               if (strnlen(elem->control, SND_SOC_FW_TEXT_SIZE) ==
+                       SND_SOC_FW_TEXT_SIZE)
+                       return -EINVAL;
+
+               route.source = elem->source;
+               route.sink = elem->sink;
+               if (strnlen(elem->control, SND_SOC_FW_TEXT_SIZE) == 0)
+                       route.control = NULL;
+               else
+                       route.control = elem->control;
+
+               /* add route, but keep going if some fail */
+               snd_soc_dapm_add_routes(dapm, &route, 1);
+       }
+
+       return 0;
+}
+
+static struct snd_kcontrol_new *soc_fw_dapm_widget_dmixer_create(struct soc_fw *sfw,
+       int num_kcontrols)
+{
+       struct snd_kcontrol_new *kc;
+       struct soc_mixer_control *sm;
+       struct snd_soc_fw_mixer_control *mc;
+       int i, err, ext;
+
+       kc = kzalloc(sizeof(*kc) * num_kcontrols, GFP_KERNEL);
+       if (!kc)
+               return NULL;
+
+       for (i = 0; i < num_kcontrols; i++) {
+               sm = kzalloc(sizeof(*sm), GFP_KERNEL);
+               if (!sm)
+                       goto err;
+
+               mc = (struct snd_soc_fw_mixer_control*)sfw->pos;
+               sfw->pos += sizeof(struct snd_soc_fw_mixer_control);
+
+               /* validate kcontrol */
+               if (strnlen(mc->hdr.name, SND_SOC_FW_TEXT_SIZE) ==
+                       SND_SOC_FW_TEXT_SIZE)
+                       goto err_str;
+
+               dev_dbg(sfw->dev, " adding DAPM widget mixer control %s at %d\n",
+                       mc->hdr.name, i);
+
+               kc[i].name = mc->hdr.name;
+               kc[i].private_value = (long)sm;
+               kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+               kc[i].access = mc->hdr.access;
+
+               sm->reg = mc->reg;
+               sm->rreg = mc->rreg;
+               sm->shift = mc->shift;
+               sm->rshift = mc->rshift;
+               sm->max = mc->max;
+               sm->min = mc->min;
+               sm->invert = mc->invert;
+               sm->platform_max = mc->platform_max;
+               sm->index = sfw->index;
+               INIT_LIST_HEAD(&sm->list);
+
+               /* map standard io handlers and check for external handlers */
+               ext = soc_fw_kcontrol_bind_io(mc->hdr.index, &kc[i], io_ops,
+                       ARRAY_SIZE(io_ops));
+               if (ext) {
+                       /* none exist, so now try and map ext handlers */
+                       ext = soc_fw_kcontrol_bind_io(mc->hdr.index, &kc[i],
+                               sfw->io_ops, sfw->io_ops_count);
+                       if (ext) {
+                               dev_err(sfw->dev,
+                                       "ASoC: no complete widget mixer IO handler for %s type (g,p,i) %d:%d:%d\n",
+                                       mc->hdr.name,
+                                       SOC_CONTROL_GET_ID_GET(mc->hdr.index),
+                                       SOC_CONTROL_GET_ID_PUT(mc->hdr.index),
+                                       SOC_CONTROL_GET_ID_INFO(mc->hdr.index));
+                               kfree(sm);
+                               continue;
+                       }
+
+                       err = soc_fw_init_kcontrol(sfw, &kc[i]);
+                       if (err < 0) {
+                               dev_err(sfw->dev, "ASoC: failed to init %s\n",
+                                       mc->hdr.name);
+                               kfree(sm);
+                               continue;
+                       }
+               }
+       }
+       return kc;
+err_str:
+       kfree(sm);
+err:
+       for (--i; i >= 0; i--)
+               kfree((void*)kc[i].private_value);
+       kfree(kc);
+       return NULL;
+}
+
+static struct snd_kcontrol_new *soc_fw_dapm_widget_denum_create(struct soc_fw *sfw)
+{
+       struct snd_kcontrol_new *kc;
+       struct snd_soc_fw_enum_control *ec;
+       struct soc_enum *se;
+       int i, err, ext;
+
+       ec = (struct snd_soc_fw_enum_control*)sfw->pos;
+       sfw->pos += sizeof(struct snd_soc_fw_enum_control);
+
+       /* validate kcontrol */
+       if (strnlen(ec->hdr.name, SND_SOC_FW_TEXT_SIZE) ==
+               SND_SOC_FW_TEXT_SIZE)
+               return NULL;
+
+       kc = kzalloc(sizeof(*kc), GFP_KERNEL);
+       if (!kc)
+               return NULL;
+
+       se = kzalloc(sizeof(*se), GFP_KERNEL);
+       if (!se)
+               goto err_se;
+
+       dev_dbg(sfw->dev, " adding DAPM widget enum control %s\n",
+               ec->hdr.name);
+
+       kc->name = ec->hdr.name;
+       kc->private_value = (long)se;
+       kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       kc->access = ec->hdr.access;
+
+       se->reg = ec->reg;
+       se->reg2 = ec->reg2;
+       se->shift_l = ec->shift_l;
+       se->shift_r = ec->shift_r;
+       se->max = ec->max;
+       se->mask = ec->mask;
+       se->index = sfw->index;
+
+       switch (SOC_CONTROL_GET_ID_INFO(ec->hdr.index)) {
+       case SOC_CONTROL_TYPE_ENUM_VALUE:
+       case SOC_DAPM_TYPE_ENUM_VALUE:
+               err = soc_fw_denum_create_values(se, ec);
+               if (err < 0) {
+                       dev_err(sfw->dev, "ASoC: could not create"
+                               " values for %s\n", ec->hdr.name);
+                       goto err_se;
+               }
+               /* fall through to create texts */
+       case SOC_CONTROL_TYPE_ENUM:
+       case SOC_CONTROL_TYPE_ENUM_EXT:
+       case SOC_DAPM_TYPE_ENUM_EXT:
+       case SOC_DAPM_TYPE_ENUM_DOUBLE:
+       case SOC_DAPM_TYPE_ENUM_VIRT:
+               err = soc_fw_denum_create_texts(se, ec);
+               if (err < 0) {
+                       dev_err(sfw->dev, "ASoC: could not create"
+                               " texts for %s\n", ec->hdr.name);
+                       goto err_se;
+               }
+               break;
+       default:
+               dev_err(sfw->dev, "ASoC: invalid enum control type %d for %s\n",
+                       ec->hdr.index, ec->hdr.name);
+               goto err_se;
+       }
+
+       /* map standard io handlers and check for external handlers */
+       ext = soc_fw_kcontrol_bind_io(ec->hdr.index, kc, io_ops,
+               ARRAY_SIZE(io_ops));
+       if (ext) {
+               /* none exist, so now try and map ext handlers */
+               ext = soc_fw_kcontrol_bind_io(ec->hdr.index, kc,
+                       sfw->io_ops, sfw->io_ops_count);
+               if (ext) {
+                       dev_err(sfw->dev,
+                               "ASoC: no complete widget enum IO handler for %s type (g,p,i) %d:%d:%d\n",
+                               ec->hdr.name,
+                               SOC_CONTROL_GET_ID_GET(ec->hdr.index),
+                               SOC_CONTROL_GET_ID_PUT(ec->hdr.index),
+                               SOC_CONTROL_GET_ID_INFO(ec->hdr.index));
+                       goto err_se;
+               }
+
+               err = soc_fw_init_kcontrol(sfw, kc);
+               if (err < 0) {
+                       dev_err(sfw->dev, "ASoC: failed to init %s\n",
+                               ec->hdr.name);
+                       goto err_se;
+               }
+       }
+       return kc;
+
+err_se:
+       kfree(kc);
+
+       /* free values and texts */
+       if (se->dvalues)
+               kfree(se->dvalues);
+       for (i = 0; i < ec->max; i++)
+               kfree(se->dtexts[i]);
+
+       kfree(se);
+
+       return NULL;
+}
+
+static int soc_fw_dapm_widget_create(struct soc_fw *sfw,
+       struct snd_soc_fw_dapm_widget *w)
+{
+       struct snd_soc_dapm_context *dapm = soc_fw_dapm_get(sfw);
+       struct snd_soc_dapm_widget widget;
+       struct snd_soc_fw_control_hdr *control_hdr;
+       int ret = 0;
+
+       if (strnlen(w->name, SND_SOC_FW_TEXT_SIZE) ==
+               SND_SOC_FW_TEXT_SIZE)
+               return -EINVAL;
+       if (strnlen(w->sname, SND_SOC_FW_TEXT_SIZE) ==
+               SND_SOC_FW_TEXT_SIZE)
+               return -EINVAL;
+
+       dev_dbg(sfw->dev, "ASoC: creating DAPM widget %s id %d\n",
+               w->name, w->id);
+
+       memset(&widget, 0, sizeof(widget));
+       widget.id = w->id;
+       widget.name = kstrdup(w->name, GFP_KERNEL);
+       if (!widget.name)
+               return -ENOMEM;
+       widget.sname = kstrdup(w->sname, GFP_KERNEL);
+       if (!widget.sname) {
+               ret = -ENOMEM;
+               goto err;
+       }
+       widget.reg = w->reg;
+       widget.shift = w->shift;
+       widget.mask = w->mask;
+       widget.invert = w->invert;
+       widget.ignore_suspend = w->ignore_suspend;
+       widget.index = sfw->index;
+
+       sfw->pos += sizeof(struct snd_soc_fw_dapm_widget);
+       if (w->kcontrol.count == 0) {
+               widget.num_kcontrols = 0;
+               goto widget;
+       }
+
+       control_hdr = (struct snd_soc_fw_control_hdr*)sfw->pos;
+       dev_dbg(sfw->dev, "ASoC: widget %s has %d controls of type %x\n",
+               w->name, w->kcontrol.count, control_hdr->index);
+
+       switch (SOC_CONTROL_GET_ID_INFO(control_hdr->index)) {
+       case SOC_CONTROL_TYPE_VOLSW:
+       case SOC_CONTROL_TYPE_STROBE:
+       case SOC_CONTROL_TYPE_VOLSW_SX:
+       case SOC_CONTROL_TYPE_VOLSW_S8:
+       case SOC_CONTROL_TYPE_VOLSW_XR_SX:
+       case SOC_CONTROL_TYPE_BYTES:
+       case SOC_CONTROL_TYPE_BOOL_EXT:
+       case SOC_CONTROL_TYPE_RANGE:
+       case SOC_DAPM_TYPE_VOLSW:
+               widget.num_kcontrols = w->kcontrol.count;
+               widget.kcontrol_news = soc_fw_dapm_widget_dmixer_create(sfw,
+                       widget.num_kcontrols);
+               if (!widget.kcontrol_news) {
+                       ret = -ENOMEM;
+                       goto hdr_err;
+               }
+               ret = soc_fw_widget_load(sfw, &widget);
+               if (ret < 0)
+                       goto hdr_err;
+               break;
+       case SOC_CONTROL_TYPE_ENUM:
+       case SOC_CONTROL_TYPE_ENUM_EXT:
+       case SOC_CONTROL_TYPE_ENUM_VALUE:
+       case SOC_DAPM_TYPE_ENUM_DOUBLE:
+       case SOC_DAPM_TYPE_ENUM_VIRT:
+       case SOC_DAPM_TYPE_ENUM_VALUE:
+       case SOC_DAPM_TYPE_ENUM_EXT:
+               widget.num_kcontrols = 1;
+               widget.kcontrol_enum = 1;
+               widget.kcontrol_news = soc_fw_dapm_widget_denum_create(sfw);
+               if (!widget.kcontrol_news) {
+                       ret = -ENOMEM;
+                       goto hdr_err;
+               }
+               ret = soc_fw_widget_load(sfw, &widget);
+               if (ret < 0)
+                       goto hdr_err;
+               break;
+       default:
+               dev_err(sfw->dev, "ASoC: invalid widget control type %d:%d:%d\n",
+                       SOC_CONTROL_GET_ID_GET(control_hdr->index),
+                       SOC_CONTROL_GET_ID_PUT(control_hdr->index),
+                       SOC_CONTROL_GET_ID_INFO(control_hdr->index));
+               ret = -EINVAL;
+               goto hdr_err;
+       }
+
+widget:
+       ret = snd_soc_dapm_new_controls(dapm, &widget, 1);
+       if (ret < 0) {
+               dev_err(sfw->dev, "ASoC: failed to create widget %s controls\n",
+                       w->name);
+               goto hdr_err;
+       }
+
+hdr_err:
+       kfree(widget.sname);
+err:
+       kfree(widget.name);
+       return ret;
+}
+
+static int soc_fw_dapm_widget_load(struct soc_fw *sfw, struct snd_soc_fw_hdr *hdr)
+{
+       struct snd_soc_fw_dapm_elems *elem_info =
+               (struct snd_soc_fw_dapm_elems*)sfw->pos;
+       struct snd_soc_fw_dapm_widget *widget;
+       int ret, count = elem_info->count, i;
+
+       if (sfw->pass != SOC_FW_PASS_WIDGET)
+               return 0;
+
+       sfw->pos += sizeof(struct snd_soc_fw_dapm_elems);
+
+       if (soc_fw_check_control_count(sfw,
+               sizeof(struct snd_soc_fw_dapm_graph_elem), count, hdr->size)) {
+               dev_err(sfw->dev, "ASoC: invalid count %d for widgets\n", count);
+               return -EINVAL;
+       }
+
+       dev_dbg(sfw->dev, "ASoC: adding %d DAPM widgets\n", count);
+
+       for (i = 0; i < count; i++) {
+               widget = (struct snd_soc_fw_dapm_widget*) sfw->pos;
+               ret = soc_fw_dapm_widget_create(sfw, widget);
+               if (ret < 0)
+                       dev_err(sfw->dev, "ASoC: failed to load widget %s\n",
+                               widget->name);
+       }
+
+       return 0;
+}
+
+static int soc_fw_dapm_complete(struct soc_fw *sfw)
+{
+       struct snd_soc_dapm_context *dapm = soc_fw_dapm_get(sfw);
+       int ret;
+
+       ret = snd_soc_dapm_new_widgets(dapm);
+       if (ret < 0)
+               dev_err(sfw->dev, "ASoC: failed to create new widgets %d\n",
+                       ret);
+
+       return ret;
+}
+
+/* Coefficients with mixer header */
+static int soc_fw_coeff_load(struct soc_fw *sfw, struct snd_soc_fw_hdr *hdr)
+{
+       struct snd_soc_fw_kcontrol *sfwk =
+               (struct snd_soc_fw_kcontrol*)sfw->pos;
+       struct snd_soc_fw_control_hdr *control_hdr;
+       struct snd_soc_fw_hdr *vhdr;
+       int ret;
+
+       if (sfw->pass != SOC_FW_PASS_COEFF)
+               return 0;
+
+       /* vendor coefficient data is encapsulated with hdrs in generic
+         coefficient controls */
+       if (hdr->vendor_type != 0)
+               return 0;
+
+       dev_dbg(sfw->dev, "ASoC: got %d new coefficients\n", sfwk->count);
+
+       sfw->pos += sizeof(struct snd_soc_fw_kcontrol);
+       control_hdr = (struct snd_soc_fw_control_hdr*)sfw->pos;
+
+       switch (SOC_CONTROL_GET_ID_INFO(control_hdr->index)) {
+       case SOC_CONTROL_TYPE_ENUM:
+       case SOC_CONTROL_TYPE_ENUM_EXT:
+       case SOC_CONTROL_TYPE_ENUM_VALUE:
+               ret = soc_fw_denum_create(sfw, 1, hdr->size);
+               if (ret < 0) {
+                       dev_err(sfw->dev, "ASoC: failed to create coeff enum %d\n",
+                               ret);
+                       return ret;
+               }
+               break;
+       default:
+               dev_err(sfw->dev, "ASoC: invalid coeff control type %d count %d\n",
+                       SOC_CONTROL_GET_ID_INFO(control_hdr->index),
+                       sfwk->count);
+               return -EINVAL;
+       }
+
+       vhdr = (struct snd_soc_fw_hdr *)sfw->pos;
+
+       ret = soc_fw_vendor_load_(sfw, vhdr);
+       if (ret < 0) {
+               dev_err(sfw->dev, "ASoC: unabled to load coeff data %d\n", ret);
+               return ret;
+       }
+       sfw->pos += sizeof(*vhdr) + vhdr->size;
+       vhdr = (struct snd_soc_fw_hdr *)sfw->pos;
+
+       return 0;
+}
+
+static int soc_fw_dai_link_load(struct soc_fw *sfw, struct snd_soc_fw_hdr *hdr)
+{
+       /* TODO: add DAI links based on FW routing between components */
+       dev_err(sfw->dev, "ASoC: Firmware DAIs not supported\n");
+       return 0;
+}
+
+static int soc_valid_header(struct soc_fw *sfw, struct snd_soc_fw_hdr *hdr)
+{
+       if (soc_fw_get_hdr_offset(sfw) >= sfw->fw->size)
+               return 0;
+
+       /* big endian firmware objects not supported atm */
+       if (hdr->magic == cpu_to_be32(SND_SOC_FW_MAGIC)) {
+               dev_err(sfw->dev,
+                       "ASoC: %s at pass %d big endian not supported header got %x at offset 0x%x size 0x%x.\n",
+                       sfw->file, sfw->pass, hdr->magic,
+                       soc_fw_get_hdr_offset(sfw), sfw->fw->size);
+               return -EINVAL;
+       }
+
+       if (hdr->magic != SND_SOC_FW_MAGIC) {
+               dev_err(sfw->dev,
+                       "ASoC: %s at pass %d does not have a valid header got %x at offset 0x%x size 0x%x.\n",
+                       sfw->file, sfw->pass, hdr->magic,
+                       soc_fw_get_hdr_offset(sfw), sfw->fw->size);
+               return -EINVAL;
+       }
+
+       if (hdr->abi != SND_SOC_FW_ABI_VERSION) {
+               dev_err(sfw->dev,
+                       "ASoC: %s at pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%x size 0x%x.\n",
+                       sfw->file, sfw->pass, hdr->abi, SND_SOC_FW_ABI_VERSION,
+                       soc_fw_get_hdr_offset(sfw), sfw->fw->size);
+               return -EINVAL;
+       }
+
+       if (hdr->size == 0) {
+               dev_err(sfw->dev, "ASoC: %s header has 0 size at offset 0x%x.\n",
+                       sfw->file, soc_fw_get_hdr_offset(sfw));
+               return -EINVAL;
+       }
+
+       if (sfw->pass == hdr->type)
+               dev_dbg(sfw->dev,
+                       "ASoC: Got 0x%x bytes of type %d version %d vendor %d at pass %d\n",
+                       hdr->size, hdr->type, hdr->version, hdr->vendor_type,
+                       sfw->pass);
+
+       return 1;
+}
+
+static int soc_fw_load_header(struct soc_fw *sfw, struct snd_soc_fw_hdr *hdr)
+{
+       sfw->pos = sfw->hdr_pos + sizeof(struct snd_soc_fw_hdr);
+
+       switch (hdr->type) {
+       case SND_SOC_FW_MIXER:
+               return soc_fw_kcontrol_load(sfw, hdr);
+       case SND_SOC_FW_DAPM_GRAPH:
+               return soc_fw_dapm_graph_load(sfw, hdr);
+       case SND_SOC_FW_DAPM_WIDGET:
+               return soc_fw_dapm_widget_load(sfw, hdr);
+       case SND_SOC_FW_DAI_LINK:
+               return soc_fw_dai_link_load(sfw, hdr);
+       case SND_SOC_FW_COEFF:
+               return soc_fw_coeff_load(sfw, hdr);
+       default:
+               return soc_fw_vendor_load(sfw, hdr);
+       }
+
+       return 0;
+}
+
+static int soc_fw_process_headers(struct soc_fw *sfw)
+{
+       struct snd_soc_fw_hdr *hdr;
+       int ret;
+
+       sfw->pass = SOC_FW_PASS_START;
+
+       while (sfw->pass <= SOC_FW_PASS_END) {
+
+               sfw->hdr_pos = sfw->fw->data;
+               hdr = (struct snd_soc_fw_hdr *)sfw->hdr_pos;
+
+               while (!soc_fw_is_eof(sfw)) {
+
+                       ret = soc_valid_header(sfw, hdr);
+                       if (ret < 0)
+                               return ret;
+                       else if (ret == 0)
+                               break;
+
+                       ret = soc_fw_load_header(sfw, hdr);
+                       if (ret < 0)
+                               return ret;
+
+                       sfw->hdr_pos += hdr->size + sizeof(struct snd_soc_fw_hdr);
+                       hdr = (struct snd_soc_fw_hdr *)sfw->hdr_pos;
+               }
+               sfw->pass++;
+       }
+
+       ret = soc_fw_dapm_complete(sfw);
+       if (ret < 0)
+               dev_err(sfw->dev, "ASoC: failed to initialise DAPM from Firmware\n");
+
+       return ret;
+}
+
+static int soc_fw_load(struct soc_fw *sfw)
+{
+       int ret;
+
+       ret = soc_fw_process_headers(sfw);
+       if (ret == 0)
+               soc_fw_complete(sfw);
+
+       return ret;
+}
+
+int snd_soc_fw_load_codec(struct snd_soc_codec *codec,
+       struct snd_soc_fw_codec_ops *ops, const struct firmware *fw,
+       u32 index)
+{
+       struct soc_fw sfw;
+
+       memset(&sfw, 0, sizeof(sfw));
+
+       sfw.fw = fw;
+       sfw.dev = codec->dev;
+       sfw.codec = codec;
+       sfw.codec_ops = ops;
+       sfw.index = index;
+       sfw.io_ops = ops->io_ops;
+       sfw.io_ops_count = ops->io_ops_count;
+
+       return soc_fw_load(&sfw);
+}
+EXPORT_SYMBOL_GPL(snd_soc_fw_load_codec);
+
+int snd_soc_fw_load_platform(struct snd_soc_platform *platform,
+       struct snd_soc_fw_platform_ops *ops, const struct firmware *fw,
+       u32 index)
+{
+       struct soc_fw sfw;
+
+       memset(&sfw, 0, sizeof(sfw));
+
+       sfw.fw = fw;
+       sfw.dev = platform->dev;
+       sfw.platform = platform;
+       sfw.platform_ops = ops;
+       sfw.index = index;
+       sfw.io_ops = ops->io_ops;
+       sfw.io_ops_count = ops->io_ops_count;
+
+       return soc_fw_load(&sfw);
+}
+EXPORT_SYMBOL_GPL(snd_soc_fw_load_platform);
+
+int snd_soc_fw_load_card(struct snd_soc_card *card,
+       struct snd_soc_fw_card_ops *ops, const struct firmware *fw,
+       u32 index)
+{
+       struct soc_fw sfw;
+
+       memset(&sfw, 0, sizeof(sfw));
+
+       sfw.fw = fw;
+       sfw.dev = card->dev;
+       sfw.card = card;
+       sfw.card_ops = ops;
+       sfw.index = index;
+       sfw.io_ops = ops->io_ops;
+       sfw.io_ops_count = ops->io_ops_count;
+
+       return soc_fw_load(&sfw);
+}
+EXPORT_SYMBOL_GPL(snd_soc_fw_load_card);
+
+/* remove this dynamic widget */
+void snd_soc_fw_dcontrols_remove_widget(struct snd_soc_dapm_widget *w)
+{
+       struct snd_card *card = w->dapm->card->snd_card;
+       int i;
+
+       /*
+        * Dynamic Widgets either have 1 enum kcontrol or 1..N mixers.
+        * The enumm may either have an array of values or strings.
+        */
+       if (w->kcontrol_enum) {
+               struct soc_enum *se =
+                       (struct soc_enum *)w->kcontrols[0]->private_value;
+
+               snd_ctl_remove(card, w->kcontrols[0]);
+
+               if (se->dvalues)
+                       kfree(se->dvalues);
+               for (i = 0; i < se->max; i++)
+                       kfree(se->dtexts[i]);
+
+               kfree(se);
+               kfree(w->kcontrol_news);
+       } else {
+               for (i = 0; i < w->num_kcontrols; i++) {
+                       struct snd_kcontrol *kcontrol = w->kcontrols[i];
+                       struct soc_mixer_control *sm =
+                       (struct soc_mixer_control *) kcontrol->private_value;
+
+                       if (w->kcontrols[i]->tlv.p)
+                               kfree(w->kcontrols[i]->tlv.p);
+
+                       snd_ctl_remove(card, w->kcontrols[i]);
+                       kfree(sm);
+               }
+               kfree(w->kcontrol_news);
+       }
+
+}
+EXPORT_SYMBOL_GPL(snd_soc_fw_dcontrols_remove_widget);
+
+/* remove all dynamic widgets from this context */
+void snd_soc_fw_dcontrols_remove_widgets(struct snd_soc_dapm_context *dapm,
+       u32 index)
+{
+       struct snd_soc_dapm_widget *w, *next_w;
+       struct snd_soc_dapm_path *p, *next_p;
+
+       list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) {
+               if (w->index != index || w->dapm != dapm)
+                       continue;
+
+               list_del(&w->list);
+               /*
+                * remove source and sink paths associated to this widget.
+                * While removing the path, remove reference to it from both
+                * source and sink widgets so that path is removed only once.
+                */
+               list_for_each_entry_safe(p, next_p, &w->sources, list_sink) {
+                       list_del(&p->list_sink);
+                       list_del(&p->list_source);
+                       list_del(&p->list);
+                       kfree(p->long_name);
+                       kfree(p);
+               }
+               list_for_each_entry_safe(p, next_p, &w->sinks, list_source) {
+                       list_del(&p->list_sink);
+                       list_del(&p->list_source);
+                       list_del(&p->list);
+                       kfree(p->long_name);
+                       kfree(p);
+               }
+               /* check and free and dynamic widget kcontrols */
+               snd_soc_fw_dcontrols_remove_widget(w);
+               kfree(w->kcontrols);
+               kfree(w->name);
+               kfree(w);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_soc_fw_dcontrols_remove_widgets);
+
+/* remove dynamic controls from the codec driver only */
+void snd_soc_fw_dcontrols_remove_codec(struct snd_soc_codec *codec,
+       u32 index)
+{
+       struct soc_mixer_control *sm, *next_sm;
+       struct soc_enum *se, *next_se;
+       struct snd_card *card = codec->card->snd_card;
+       const unsigned int *p = NULL;
+       int i;
+
+       list_for_each_entry_safe(sm, next_sm, &codec->dmixers, list) {
+
+               if (sm->index != index)
+                       continue;
+
+               if (sm->dcontrol->tlv.p)
+                       p = sm->dcontrol->tlv.p;
+               snd_ctl_remove(card, sm->dcontrol);
+               list_del(&sm->list);
+               kfree(sm);
+               kfree(p);
+       }
+
+       list_for_each_entry_safe(se, next_se, &codec->denums, list) {
+
+               if (sm->index != index)
+                       continue;
+
+               snd_ctl_remove(card, se->dcontrol);
+               list_del(&se->list);
+
+               if (se->dvalues)
+                       kfree(se->dvalues);
+               for (i = 0; i < se->max; i++)
+                       kfree(se->dtexts[i]);
+               kfree(se);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_soc_fw_dcontrols_remove_codec);
+
+/* remove dynamic controls from the platform driver only */
+void snd_soc_fw_dcontrols_remove_platform(struct snd_soc_platform *platform,
+       u32 index)
+{
+       struct soc_mixer_control *sm, *next_sm;
+       struct soc_enum *se, *next_se;
+       struct snd_card *card = platform->card->snd_card;
+       const unsigned int *p = NULL;
+       int i;
+
+       list_for_each_entry_safe(sm, next_sm, &platform->dmixers, list) {
+
+               if (sm->index != index)
+                       continue;
+
+               if (sm->dcontrol->tlv.p)
+                       p = sm->dcontrol->tlv.p;
+               snd_ctl_remove(card, sm->dcontrol);
+               list_del(&sm->list);
+               kfree(sm);
+               kfree(p);
+       }
+
+       list_for_each_entry_safe(se, next_se, &platform->denums, list) {
+
+               if (sm->index != index)
+                       continue;
+
+               snd_ctl_remove(card, se->dcontrol);
+               list_del(&se->list);
+
+               if (se->dvalues)
+                       kfree(se->dvalues);
+               for (i = 0; i < se->max; i++)
+                       kfree(se->dtexts[i]);
+               kfree(se);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_soc_fw_dcontrols_remove_platform);
+
+/* remove dynamic controls from the card driver only */
+void snd_soc_fw_dcontrols_remove_card(struct snd_soc_card *soc_card,
+       u32 index)
+{
+       struct soc_mixer_control *sm, *next_sm;
+       struct soc_enum *se, *next_se;
+       struct snd_card *card = soc_card->snd_card;
+       const unsigned int *p = NULL;
+       int i;
+
+       list_for_each_entry_safe(sm, next_sm, &soc_card->dmixers, list) {
+
+               if (sm->index != index)
+                       continue;
+
+               if (sm->dcontrol->tlv.p)
+                       p = sm->dcontrol->tlv.p;
+               snd_ctl_remove(card, sm->dcontrol);
+               list_del(&sm->list);
+               kfree(sm);
+               kfree(p);
+       }
+
+       list_for_each_entry_safe(se, next_se, &soc_card->denums, list) {
+
+               if (sm->index != index)
+                       continue;
+
+               snd_ctl_remove(card, se->dcontrol);
+               list_del(&se->list);
+
+               if (se->dvalues)
+                       kfree(se->dvalues);
+               for (i = 0; i < se->max; i++)
+                       kfree(se->dtexts[i]);
+               kfree(se);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_soc_fw_dcontrols_remove_card);
+
+/* remove all dynamic controls from sound card and components */
+int snd_soc_fw_dcontrols_remove_all(struct snd_soc_card *card, u32 index)
+{
+       struct snd_soc_codec *codec;
+       struct snd_soc_platform *platform;
+
+       list_for_each_entry(codec, &card->codec_dev_list, card_list)
+               snd_soc_fw_dcontrols_remove_codec(codec, index);
+
+       list_for_each_entry(platform, &card->platform_dev_list, card_list)
+               snd_soc_fw_dcontrols_remove_platform(platform, index);
+
+       snd_soc_fw_dcontrols_remove_card(card, index);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_fw_dcontrols_remove_all);
index cf191e6aebbe1d1bfeb9dd511d0fe9b5e5bd3bbd..dfd13bbd8766d36529c49bad4ea3218598986d55 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/workqueue.h>
 #include <linux/export.h>
 #include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 
 #define DPCM_MAX_BE_USERS      8
 
+/* ASoC no host IO hardware.
+ * TODO: fine tune these values for all host less transfers.
+ */
+static const struct snd_pcm_hardware no_host_hardware = {
+       .info                   = SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME,
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE |
+                                 SNDRV_PCM_FMTBIT_S32_LE,
+       .period_bytes_min       = PAGE_SIZE >> 2,
+       .period_bytes_max       = PAGE_SIZE >> 1,
+       .periods_min            = 2,
+       .periods_max            = 4,
+       .buffer_bytes_max       = PAGE_SIZE,
+};
+
 /* DPCM stream event, send event to FE and all active BEs. */
 static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
        int event)
@@ -146,6 +165,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
+       if (rtd->dai_link->no_host_mode)
+               snd_soc_set_runtime_hwparams(substream, &no_host_hardware);
        /* startup the audio subsystem */
        if (cpu_dai->driver->ops->startup) {
                ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
@@ -551,6 +572,19 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        cpu_dai->rate = params_rate(params);
        codec_dai->rate = params_rate(params);
 
+       /* malloc a page for hostless IO.
+        * FIXME: rework with alsa-lib changes so that this malloc is not required.
+        */
+       if (rtd->dai_link->no_host_mode) {
+               substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV;
+               substream->dma_buffer.dev.dev = rtd->dev;
+               substream->dma_buffer.dev.dev->coherent_dma_mask = DMA_BIT_MASK(32);
+               substream->dma_buffer.private_data = NULL;
+
+               ret = snd_pcm_lib_malloc_pages(substream, PAGE_SIZE);
+               if (ret < 0)
+                       goto platform_err;
+       }
 out:
        mutex_unlock(&rtd->pcm_mutex);
        return ret;
@@ -602,6 +636,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
 
        if (cpu_dai->driver->ops->hw_free)
                cpu_dai->driver->ops->hw_free(substream, cpu_dai);
+       if (rtd->dai_link->no_host_mode)
+               snd_pcm_lib_free_pages(substream);
 
        mutex_unlock(&rtd->pcm_mutex);
        return 0;
@@ -818,7 +854,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
                }
        }
 
-       dev_err(card->dev, "ASoC: can't get %s BE for %s\n",
+       dev_dbg(card->dev, "ASoC: can't get %s BE for %s\n",
                stream ? "capture" : "playback", widget->name);
        return NULL;
 }
@@ -935,7 +971,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
                /* is there a valid BE rtd for this widget */
                be = dpcm_get_be(card, list->widgets[i], stream);
                if (!be) {
-                       dev_err(fe->dev, "ASoC: no BE found for %s\n",
+                       dev_dbg(fe->dev, "ASoC: no BE found for %s\n",
                                        list->widgets[i]->name);
                        continue;
                }
@@ -1059,6 +1095,7 @@ static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
                                        be->dpcm[stream].state);
 
                        be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+                       be_substream->runtime = NULL;
                        goto unwind;
                }
 
@@ -1729,20 +1766,16 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
 
        /* startup must always be called for new BEs */
        ret = dpcm_be_dai_startup(fe, stream);
-       if (ret < 0) {
+       if (ret < 0)
                goto disconnect;
-               return ret;
-       }
 
        /* keep going if FE state is > open */
        if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN)
                return 0;
 
        ret = dpcm_be_dai_hw_params(fe, stream);
-       if (ret < 0) {
+       if (ret < 0)
                goto close;
-               return ret;
-       }
 
        /* keep going if FE state is > hw_params */
        if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS)
@@ -1750,10 +1783,8 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
 
 
        ret = dpcm_be_dai_prepare(fe, stream);
-       if (ret < 0) {
+       if (ret < 0)
                goto hw_free;
-               return ret;
-       }
 
        /* run the stream event for each BE */
        dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
@@ -1888,6 +1919,8 @@ int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *widget)
                        dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
                }
 
+               dpcm_path_put(&list);
+
 capture:
                /* skip if FE doesn't have capture capability */
                if (!fe->cpu_dai->driver->capture.channels_min)
@@ -2027,17 +2060,21 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
        /* create the PCM */
        if (rtd->dai_link->no_pcm) {
                snprintf(new_name, sizeof(new_name), "(%s)",
-                       rtd->dai_link->stream_name);
+                       rtd->dai_link->stream_name ?
+                       rtd->dai_link->stream_name : rtd->dai_link->name);
 
                ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
                                playback, capture, &pcm);
        } else {
                if (rtd->dai_link->dynamic)
                        snprintf(new_name, sizeof(new_name), "%s (*)",
-                               rtd->dai_link->stream_name);
+                               rtd->dai_link->stream_name ?
+                               rtd->dai_link->stream_name : rtd->dai_link->name);
                else
                        snprintf(new_name, sizeof(new_name), "%s %s-%d",
-                               rtd->dai_link->stream_name, codec_dai->name, num);
+                               rtd->dai_link->stream_name ?
+                               rtd->dai_link->stream_name : rtd->dai_link->name,
+                               codec_dai->name, num);
 
                ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
                        capture, &pcm);
@@ -2063,6 +2100,18 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
                goto out;
        }
 
+       /* setup any hostless PCMs - i.e. no host IO is performed */
+       if (rtd->dai_link->no_host_mode) {
+               pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->hw_no_buffer = 1;
+               pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->hw_no_buffer = 1;
+               snd_soc_set_runtime_hwparams(
+                       pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+                       &no_host_hardware);
+               snd_soc_set_runtime_hwparams(
+                       pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+                       &no_host_hardware);
+       }
+
        /* ASoC PCM operations */
        if (rtd->dai_link->dynamic) {
                rtd->ops.open           = dpcm_fe_dai_open;
@@ -2224,6 +2273,106 @@ int snd_soc_platform_trigger(struct snd_pcm_substream *substream,
 }
 EXPORT_SYMBOL_GPL(snd_soc_platform_trigger);
 
+int snd_soc_dai_startup(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int ret = 0;
+
+       mutex_lock(&rtd->pcm_mutex);
+
+       if (dai->driver->ops->startup)
+               ret = dai->driver->ops->startup(substream, dai);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dai->playback_active++;
+       else
+               dai->capture_active++;
+
+       dai->active++;
+
+       mutex_unlock(&rtd->pcm_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_startup);
+
+void snd_soc_dai_shutdown(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+       mutex_lock(&rtd->pcm_mutex);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dai->playback_active--;
+       else
+               dai->capture_active--;
+
+       dai->active--;
+
+       if (dai->driver->ops->shutdown)
+               dai->driver->ops->shutdown(substream, dai);
+       mutex_unlock(&rtd->pcm_mutex);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_shutdown);
+
+int snd_soc_dai_hw_params(struct snd_pcm_substream * substream,
+               struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int ret = 0;
+
+       mutex_lock(&rtd->pcm_mutex);
+
+       if (dai->driver->ops->hw_params)
+               ret = dai->driver->ops->hw_params(substream, hw_params, dai);
+
+       mutex_unlock(&rtd->pcm_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_hw_params);
+
+int snd_soc_dai_hw_free(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int ret = 0;
+
+       mutex_lock(&rtd->pcm_mutex);
+
+       if (dai->driver->ops->hw_free)
+               ret = dai->driver->ops->hw_free(substream, dai);
+
+       mutex_unlock(&rtd->pcm_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_hw_free);
+
+int snd_soc_dai_prepare(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int ret = 0;
+
+       mutex_lock(&rtd->pcm_mutex);
+
+       if (dai->driver->ops->prepare)
+               ret = dai->driver->ops->prepare(substream, dai);
+
+       mutex_unlock(&rtd->pcm_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_prepare);
+
+int snd_soc_dai_trigger(struct snd_pcm_substream *substream,
+       int cmd, struct snd_soc_dai *dai)
+{
+       if (dai->driver->ops->trigger)
+               return dai->driver->ops->trigger(substream, cmd, dai);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_trigger);
+
 #ifdef CONFIG_DEBUG_FS
 static char *dpcm_state_string(enum snd_soc_dpcm_state state)
 {
index 19e5fe7cc403ac8e55822a52e05bc69c21d1835f..dbc27ce1d4deb32d14f8e6e40291b828d179fbf7 100644 (file)
@@ -6,6 +6,16 @@ config SND_SOC_TEGRA
        help
          Say Y or M here if you want support for SoC audio on Tegra.
 
+config SND_SOC_TEGRA20_AC97
+       tristate
+       depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
+       select SND_SOC_AC97_BUS
+       select SND_SOC_TEGRA20_DAS
+       help
+         Say Y or M if you want to add support for codecs attached to the
+         Tegra20 AC97 interface. You will also need to select the individual
+         machine drivers to support below.
+
 config SND_SOC_TEGRA20_DAS
        tristate
        depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
@@ -70,6 +80,15 @@ config SND_SOC_TEGRA_WM8903
          boards using the WM8093 codec. Currently, the supported boards are
          Harmony, Ventana, Seaboard, Kaen, and Aebl.
 
+config SND_SOC_TEGRA_WM9712
+       tristate "SoC Audio support for Tegra boards using a WM9712 codec"
+       depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
+       select SND_SOC_TEGRA20_AC97
+       select SND_SOC_WM9712
+       help
+         Say Y or M here if you want to add support for SoC audio on Tegra
+         boards using the WM9712 (or compatible) codec.
+
 config SND_SOC_TEGRA_TRIMSLICE
        tristate "SoC Audio support for TrimSlice board"
        depends on SND_SOC_TEGRA && I2C
index 391e78a34c0602b3dfdb6867dcd0fdd48441c219..416a14bde41b59452dc350fa64633e491b28916b 100644 (file)
@@ -1,6 +1,7 @@
 # Tegra platform Support
 snd-soc-tegra-pcm-objs := tegra_pcm.o
 snd-soc-tegra-utils-objs += tegra_asoc_utils.o
+snd-soc-tegra20-ac97-objs := tegra20_ac97.o
 snd-soc-tegra20-das-objs := tegra20_das.o
 snd-soc-tegra20-i2s-objs := tegra20_i2s.o
 snd-soc-tegra20-spdif-objs := tegra20_spdif.o
@@ -9,6 +10,7 @@ snd-soc-tegra30-i2s-objs := tegra30_i2s.o
 
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
+obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o
 obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o
 obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o
 obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o
@@ -18,10 +20,12 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
 # Tegra machine Support
 snd-soc-tegra-wm8753-objs := tegra_wm8753.o
 snd-soc-tegra-wm8903-objs := tegra_wm8903.o
+snd-soc-tegra-wm9712-objs := tegra_wm9712.o
 snd-soc-tegra-trimslice-objs := trimslice.o
 snd-soc-tegra-alc5632-objs := tegra_alc5632.o
 
 obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o
 obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
+obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o
 obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
 obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
new file mode 100644 (file)
index 0000000..336dcdd
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * tegra20_ac97.c - Tegra20 AC97 platform driver
+ *
+ * Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
+ *
+ * Partly based on code copyright/by:
+ *
+ * Copyright (c) 2011,2012 Toradex 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.
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_asoc_utils.h"
+#include "tegra20_ac97.h"
+
+#define DRV_NAME "tegra20-ac97"
+
+static struct tegra20_ac97 *workdata;
+
+static void tegra20_ac97_codec_reset(struct snd_ac97 *ac97)
+{
+       u32 readback;
+       unsigned long timeout;
+
+       /* reset line is not driven by DAC pad group, have to toggle GPIO */
+       gpio_set_value(workdata->reset_gpio, 0);
+       udelay(2);
+
+       gpio_set_value(workdata->reset_gpio, 1);
+       udelay(2);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+
+       do {
+               regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
+               if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY)
+                       break;
+               usleep_range(1000, 2000);
+       } while (!time_after(jiffies, timeout));
+}
+
+static void tegra20_ac97_codec_warm_reset(struct snd_ac97 *ac97)
+{
+       u32 readback;
+       unsigned long timeout;
+
+       /*
+        * although sync line is driven by the DAC pad group warm reset using
+        * the controller cmd is not working, have to toggle sync line
+        * manually.
+        */
+       gpio_request(workdata->sync_gpio, "codec-sync");
+
+       gpio_direction_output(workdata->sync_gpio, 1);
+
+       udelay(2);
+       gpio_set_value(workdata->sync_gpio, 0);
+       udelay(2);
+       gpio_free(workdata->sync_gpio);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+
+       do {
+               regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
+               if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY)
+                       break;
+               usleep_range(1000, 2000);
+       } while (!time_after(jiffies, timeout));
+}
+
+static unsigned short tegra20_ac97_codec_read(struct snd_ac97 *ac97_snd,
+                                             unsigned short reg)
+{
+       u32 readback;
+       unsigned long timeout;
+
+       regmap_write(workdata->regmap, TEGRA20_AC97_CMD,
+                    (((reg | 0x80) << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) &
+                     TEGRA20_AC97_CMD_CMD_ADDR_MASK) |
+                    TEGRA20_AC97_CMD_BUSY);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+
+       do {
+               regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
+               if (readback & TEGRA20_AC97_STATUS1_STA_VALID1)
+                       break;
+               usleep_range(1000, 2000);
+       } while (!time_after(jiffies, timeout));
+
+       return ((readback & TEGRA20_AC97_STATUS1_STA_DATA1_MASK) >>
+               TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT);
+}
+
+static void tegra20_ac97_codec_write(struct snd_ac97 *ac97_snd,
+                                    unsigned short reg, unsigned short val)
+{
+       u32 readback;
+       unsigned long timeout;
+
+       regmap_write(workdata->regmap, TEGRA20_AC97_CMD,
+                    ((reg << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) &
+                     TEGRA20_AC97_CMD_CMD_ADDR_MASK) |
+                    ((val << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) &
+                     TEGRA20_AC97_CMD_CMD_DATA_MASK) |
+                    TEGRA20_AC97_CMD_BUSY);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+
+       do {
+               regmap_read(workdata->regmap, TEGRA20_AC97_CMD, &readback);
+               if (!(readback & TEGRA20_AC97_CMD_BUSY))
+                       break;
+               usleep_range(1000, 2000);
+       } while (!time_after(jiffies, timeout));
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+       .read           = tegra20_ac97_codec_read,
+       .write          = tegra20_ac97_codec_write,
+       .reset          = tegra20_ac97_codec_reset,
+       .warm_reset     = tegra20_ac97_codec_warm_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static inline void tegra20_ac97_start_playback(struct tegra20_ac97 *ac97)
+{
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
+                          TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN,
+                          TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN);
+
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL,
+                          TEGRA20_AC97_CTRL_PCM_DAC_EN |
+                          TEGRA20_AC97_CTRL_STM_EN,
+                          TEGRA20_AC97_CTRL_PCM_DAC_EN |
+                          TEGRA20_AC97_CTRL_STM_EN);
+}
+
+static inline void tegra20_ac97_stop_playback(struct tegra20_ac97 *ac97)
+{
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
+                          TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, 0);
+
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL,
+                          TEGRA20_AC97_CTRL_PCM_DAC_EN, 0);
+}
+
+static inline void tegra20_ac97_start_capture(struct tegra20_ac97 *ac97)
+{
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
+                          TEGRA20_AC97_FIFO_SCR_REC_FULL_EN,
+                          TEGRA20_AC97_FIFO_SCR_REC_FULL_EN);
+}
+
+static inline void tegra20_ac97_stop_capture(struct tegra20_ac97 *ac97)
+{
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
+                          TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, 0);
+}
+
+static int tegra20_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+                               struct snd_soc_dai *dai)
+{
+       struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       tegra20_ac97_start_playback(ac97);
+               else
+                       tegra20_ac97_start_capture(ac97);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       tegra20_ac97_stop_playback(ac97);
+               else
+                       tegra20_ac97_stop_capture(ac97);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = {
+       .trigger        = tegra20_ac97_trigger,
+};
+
+static int tegra20_ac97_probe(struct snd_soc_dai *dai)
+{
+       struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai);
+
+       dai->capture_dma_data = &ac97->capture_dma_data;
+       dai->playback_dma_data = &ac97->playback_dma_data;
+
+       return 0;
+}
+
+static struct snd_soc_dai_driver tegra20_ac97_dai = {
+       .name = "tegra-ac97-pcm",
+       .ac97_control = 1,
+       .probe = tegra20_ac97_probe,
+       .playback = {
+               .stream_name = "PCM Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "PCM Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .ops = &tegra20_ac97_dai_ops,
+};
+
+static bool tegra20_ac97_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case TEGRA20_AC97_CTRL:
+       case TEGRA20_AC97_CMD:
+       case TEGRA20_AC97_STATUS1:
+       case TEGRA20_AC97_FIFO1_SCR:
+       case TEGRA20_AC97_FIFO_TX1:
+       case TEGRA20_AC97_FIFO_RX1:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+
+static bool tegra20_ac97_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case TEGRA20_AC97_STATUS1:
+       case TEGRA20_AC97_FIFO1_SCR:
+       case TEGRA20_AC97_FIFO_TX1:
+       case TEGRA20_AC97_FIFO_RX1:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+
+static bool tegra20_ac97_precious_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case TEGRA20_AC97_FIFO_TX1:
+       case TEGRA20_AC97_FIFO_RX1:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+
+static const struct regmap_config tegra20_ac97_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = TEGRA20_AC97_FIFO_RX1,
+       .writeable_reg = tegra20_ac97_wr_rd_reg,
+       .readable_reg = tegra20_ac97_wr_rd_reg,
+       .volatile_reg = tegra20_ac97_volatile_reg,
+       .precious_reg = tegra20_ac97_precious_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static int tegra20_ac97_platform_probe(struct platform_device *pdev)
+{
+       struct tegra20_ac97 *ac97;
+       struct resource *mem, *memregion;
+       u32 of_dma[2];
+       void __iomem *regs;
+       int ret = 0;
+
+       ac97 = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_ac97),
+                           GFP_KERNEL);
+       if (!ac97) {
+               dev_err(&pdev->dev, "Can't allocate tegra20_ac97\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+       dev_set_drvdata(&pdev->dev, ac97);
+
+       ac97->clk_ac97 = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(ac97->clk_ac97)) {
+               dev_err(&pdev->dev, "Can't retrieve ac97 clock\n");
+               ret = PTR_ERR(ac97->clk_ac97);
+               goto err;
+       }
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(&pdev->dev, "No memory resource\n");
+               ret = -ENODEV;
+               goto err_clk_put;
+       }
+
+       memregion = devm_request_mem_region(&pdev->dev, mem->start,
+                                           resource_size(mem), DRV_NAME);
+       if (!memregion) {
+               dev_err(&pdev->dev, "Memory region already claimed\n");
+               ret = -EBUSY;
+               goto err_clk_put;
+       }
+
+       regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+       if (!regs) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               ret = -ENOMEM;
+               goto err_clk_put;
+       }
+
+       ac97->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+                                           &tegra20_ac97_regmap_config);
+       if (IS_ERR(ac97->regmap)) {
+               dev_err(&pdev->dev, "regmap init failed\n");
+               ret = PTR_ERR(ac97->regmap);
+               goto err_clk_put;
+       }
+
+       if (of_property_read_u32_array(pdev->dev.of_node,
+                                      "nvidia,dma-request-selector",
+                                      of_dma, 2) < 0) {
+               dev_err(&pdev->dev, "No DMA resource\n");
+               ret = -ENODEV;
+               goto err_clk_put;
+       }
+
+       ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node,
+                                            "nvidia,codec-reset-gpio", 0);
+       if (gpio_is_valid(ac97->reset_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev, ac97->reset_gpio,
+                                           GPIOF_OUT_INIT_HIGH, "codec-reset");
+               if (ret) {
+                       dev_err(&pdev->dev, "could not get codec-reset GPIO\n");
+                       goto err_clk_put;
+               }
+       } else {
+               dev_err(&pdev->dev, "no codec-reset GPIO supplied\n");
+               goto err_clk_put;
+       }
+
+       ac97->sync_gpio = of_get_named_gpio(pdev->dev.of_node,
+                                           "nvidia,codec-sync-gpio", 0);
+       if (!gpio_is_valid(ac97->sync_gpio)) {
+               dev_err(&pdev->dev, "no codec-sync GPIO supplied\n");
+               goto err_clk_put;
+       }
+
+       ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1;
+       ac97->capture_dma_data.wrap = 4;
+       ac97->capture_dma_data.width = 32;
+       ac97->capture_dma_data.req_sel = of_dma[1];
+
+       ac97->playback_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_TX1;
+       ac97->playback_dma_data.wrap = 4;
+       ac97->playback_dma_data.width = 32;
+       ac97->playback_dma_data.req_sel = of_dma[1];
+
+       ret = snd_soc_register_dais(&pdev->dev, &tegra20_ac97_dai, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+               ret = -ENOMEM;
+               goto err_clk_put;
+       }
+
+       ret = tegra_pcm_platform_register(&pdev->dev);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+               goto err_unregister_dai;
+       }
+
+       ret = tegra_asoc_utils_init(&ac97->util_data, &pdev->dev);
+       if (ret)
+               goto err_unregister_pcm;
+
+       ret = tegra_asoc_utils_set_ac97_rate(&ac97->util_data);
+       if (ret)
+               goto err_asoc_utils_fini;
+
+       ret = clk_prepare_enable(ac97->clk_ac97);
+       if (ret) {
+               dev_err(&pdev->dev, "clk_enable failed: %d\n", ret);
+               goto err_asoc_utils_fini;
+       }
+
+       /* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */
+       workdata = ac97;
+
+       return 0;
+
+err_asoc_utils_fini:
+       tegra_asoc_utils_fini(&ac97->util_data);
+err_unregister_pcm:
+       tegra_pcm_platform_unregister(&pdev->dev);
+err_unregister_dai:
+       snd_soc_unregister_dai(&pdev->dev);
+err_clk_put:
+       clk_put(ac97->clk_ac97);
+err:
+       return ret;
+}
+
+static int tegra20_ac97_platform_remove(struct platform_device *pdev)
+{
+       struct tegra20_ac97 *ac97 = dev_get_drvdata(&pdev->dev);
+
+       tegra_pcm_platform_unregister(&pdev->dev);
+       snd_soc_unregister_dai(&pdev->dev);
+
+       tegra_asoc_utils_fini(&ac97->util_data);
+
+       clk_disable_unprepare(ac97->clk_ac97);
+       clk_put(ac97->clk_ac97);
+
+       return 0;
+}
+
+static const struct of_device_id tegra20_ac97_of_match[] = {
+       { .compatible = "nvidia,tegra20-ac97", },
+       {},
+};
+
+static struct platform_driver tegra20_ac97_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = tegra20_ac97_of_match,
+       },
+       .probe = tegra20_ac97_platform_probe,
+       .remove = tegra20_ac97_platform_remove,
+};
+module_platform_driver(tegra20_ac97_driver);
+
+MODULE_AUTHOR("Lucas Stach");
+MODULE_DESCRIPTION("Tegra20 AC97 ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra20_ac97_of_match);
diff --git a/sound/soc/tegra/tegra20_ac97.h b/sound/soc/tegra/tegra20_ac97.h
new file mode 100644 (file)
index 0000000..dddc682
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * tegra20_ac97.h - Definitions for the Tegra20 AC97 controller driver
+ *
+ * Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
+ *
+ * Partly based on code copyright/by:
+ *
+ * Copyright (c) 2011,2012 Toradex 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#ifndef __TEGRA20_AC97_H__
+#define __TEGRA20_AC97_H__
+
+#include "tegra_pcm.h"
+
+#define TEGRA20_AC97_CTRL                              0x00
+#define TEGRA20_AC97_CMD                               0x04
+#define TEGRA20_AC97_STATUS1                           0x08
+/* ... */
+#define TEGRA20_AC97_FIFO1_SCR                         0x1c
+/* ... */
+#define TEGRA20_AC97_FIFO_TX1                          0x40
+#define TEGRA20_AC97_FIFO_RX1                          0x80
+
+/* TEGRA20_AC97_CTRL */
+#define TEGRA20_AC97_CTRL_STM2_EN                      (1 << 16)
+#define TEGRA20_AC97_CTRL_DOUBLE_SAMPLING_EN           (1 << 11)
+#define TEGRA20_AC97_CTRL_IO_CNTRL_EN                  (1 << 10)
+#define TEGRA20_AC97_CTRL_HSET_DAC_EN                  (1 << 9)
+#define TEGRA20_AC97_CTRL_LINE2_DAC_EN                 (1 << 8)
+#define TEGRA20_AC97_CTRL_PCM_LFE_EN                   (1 << 7)
+#define TEGRA20_AC97_CTRL_PCM_SUR_EN                   (1 << 6)
+#define TEGRA20_AC97_CTRL_PCM_CEN_DAC_EN               (1 << 5)
+#define TEGRA20_AC97_CTRL_LINE1_DAC_EN                 (1 << 4)
+#define TEGRA20_AC97_CTRL_PCM_DAC_EN                   (1 << 3)
+#define TEGRA20_AC97_CTRL_COLD_RESET                   (1 << 2)
+#define TEGRA20_AC97_CTRL_WARM_RESET                   (1 << 1)
+#define TEGRA20_AC97_CTRL_STM_EN                       (1 << 0)
+
+/* TEGRA20_AC97_CMD */
+#define TEGRA20_AC97_CMD_CMD_ADDR_SHIFT                        24
+#define TEGRA20_AC97_CMD_CMD_ADDR_MASK                 (0xff << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT)
+#define TEGRA20_AC97_CMD_CMD_DATA_SHIFT                        8
+#define TEGRA20_AC97_CMD_CMD_DATA_MASK                 (0xffff << TEGRA20_AC97_CMD_CMD_DATA_SHIFT)
+#define TEGRA20_AC97_CMD_CMD_ID_SHIFT                  2
+#define TEGRA20_AC97_CMD_CMD_ID_MASK                   (0x3 << TEGRA20_AC97_CMD_CMD_ID_SHIFT)
+#define TEGRA20_AC97_CMD_BUSY                          (1 << 0)
+
+/* TEGRA20_AC97_STATUS1 */
+#define TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT           24
+#define TEGRA20_AC97_STATUS1_STA_ADDR1_MASK            (0xff << TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT)
+#define TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT           8
+#define TEGRA20_AC97_STATUS1_STA_DATA1_MASK            (0xffff << TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT)
+#define TEGRA20_AC97_STATUS1_STA_VALID1                        (1 << 2)
+#define TEGRA20_AC97_STATUS1_STANDBY1                  (1 << 1)
+#define TEGRA20_AC97_STATUS1_CODEC1_RDY                        (1 << 0)
+
+/* TEGRA20_AC97_FIFO1_SCR */
+#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT         27
+#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_MASK          (0x1f << TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT)
+#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT          22
+#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_MASK           (0x1f << TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT)
+#define TEGRA20_AC97_FIFO_SCR_REC_OVERRUN_INT_STA      (1 << 19)
+#define TEGRA20_AC97_FIFO_SCR_PB_UNDERRUN_INT_STA      (1 << 18)
+#define TEGRA20_AC97_FIFO_SCR_REC_FORCE_MT             (1 << 17)
+#define TEGRA20_AC97_FIFO_SCR_PB_FORCE_MT              (1 << 16)
+#define TEGRA20_AC97_FIFO_SCR_REC_FULL_EN              (1 << 15)
+#define TEGRA20_AC97_FIFO_SCR_REC_3QRT_FULL_EN         (1 << 14)
+#define TEGRA20_AC97_FIFO_SCR_REC_QRT_FULL_EN          (1 << 13)
+#define TEGRA20_AC97_FIFO_SCR_REC_EMPTY_EN             (1 << 12)
+#define TEGRA20_AC97_FIFO_SCR_PB_NOT_FULL_EN           (1 << 11)
+#define TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN             (1 << 10)
+#define TEGRA20_AC97_FIFO_SCR_PB_3QRT_MT_EN            (1 << 9)
+#define TEGRA20_AC97_FIFO_SCR_PB_EMPTY_MT_EN           (1 << 8)
+
+struct tegra20_ac97 {
+       struct clk *clk_ac97;
+       struct tegra_pcm_dma_params capture_dma_data;
+       struct tegra_pcm_dma_params playback_dma_data;
+       struct regmap *regmap;
+       int reset_gpio;
+       int sync_gpio;
+       struct tegra_asoc_utils_data util_data;
+};
+#endif /* __TEGRA20_AC97_H__ */
index 654318483877e0094e071ba73c1a7fe7e7bc0681..e72392927bd2ba3b7ca192e4ff75dde96b2488bd 100644 (file)
@@ -191,6 +191,19 @@ static int tegra20_das_probe(struct platform_device *pdev)
                goto err;
        }
 
+       ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_3,
+                                            TEGRA20_DAS_DAP_SEL_DAC3);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't set up DAS DAP connection\n");
+               goto err;
+       }
+       ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_3,
+                                            TEGRA20_DAS_DAC_SEL_DAP3);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't set up DAS DAC connection\n");
+               goto err;
+       }
+
        platform_set_drvdata(pdev, das);
 
        return 0;
index f354dc390a0be4c1b57ae473f8b357f9c66cb632..dd146f10fef2acc86737640d57caffe2af48de34 100644 (file)
@@ -580,7 +580,7 @@ err_clk_put_apbif:
        clk_put(ahub->clk_apbif);
 err_clk_put_d_audio:
        clk_put(ahub->clk_d_audio);
-       ahub = 0;
+       ahub = NULL;
 err:
        return ret;
 }
@@ -597,7 +597,7 @@ static int tegra30_ahub_remove(struct platform_device *pdev)
        clk_put(ahub->clk_apbif);
        clk_put(ahub->clk_d_audio);
 
-       ahub = 0;
+       ahub = NULL;
 
        return 0;
 }
index 27e91dd0b91c7d9b8b69fcdfb18056cee31d1c96..f4e1ce82750abf1d8ae02f2389b52476ec7bccbd 100644 (file)
@@ -71,7 +71,7 @@ static int tegra30_i2s_runtime_resume(struct device *dev)
        return 0;
 }
 
-int tegra30_i2s_startup(struct snd_pcm_substream *substream,
+static int tegra30_i2s_startup(struct snd_pcm_substream *substream,
                        struct snd_soc_dai *dai)
 {
        struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
@@ -98,7 +98,7 @@ int tegra30_i2s_startup(struct snd_pcm_substream *substream,
        return ret;
 }
 
-void tegra30_i2s_shutdown(struct snd_pcm_substream *substream,
+static void tegra30_i2s_shutdown(struct snd_pcm_substream *substream,
                        struct snd_soc_dai *dai)
 {
        struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
index 6872c77a1196e948de6827c88581120ec19064f3..ba419f86384d4cbd6bc568be7273e968100c7312 100644 (file)
@@ -112,6 +112,59 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
 }
 EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
 
+int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data)
+{
+       const int pll_rate = 73728000;
+       const int ac97_rate = 24576000;
+       int err;
+
+       clk_disable_unprepare(data->clk_cdev1);
+       clk_disable_unprepare(data->clk_pll_a_out0);
+       clk_disable_unprepare(data->clk_pll_a);
+
+       /*
+        * AC97 rate is fixed at 24.576MHz and is used for both the host
+        * controller and the external codec
+        */
+       err = clk_set_rate(data->clk_pll_a, pll_rate);
+       if (err) {
+               dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
+               return err;
+       }
+
+       err = clk_set_rate(data->clk_pll_a_out0, ac97_rate);
+       if (err) {
+               dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
+               return err;
+       }
+
+       /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
+
+       err = clk_prepare_enable(data->clk_pll_a);
+       if (err) {
+               dev_err(data->dev, "Can't enable pll_a: %d\n", err);
+               return err;
+       }
+
+       err = clk_prepare_enable(data->clk_pll_a_out0);
+       if (err) {
+               dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
+               return err;
+       }
+
+       err = clk_prepare_enable(data->clk_cdev1);
+       if (err) {
+               dev_err(data->dev, "Can't enable cdev1: %d\n", err);
+               return err;
+       }
+
+       data->set_baseclock = pll_rate;
+       data->set_mclk = ac97_rate;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
+
 int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
                          struct device *dev)
 {
index 44db1dbb8f210bebca2e0305fd8bc087e0c0820b..974c9f8830f9062c177bd1f3b7da77079f8b8f3f 100644 (file)
@@ -43,6 +43,7 @@ struct tegra_asoc_utils_data {
 
 int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
                              int mclk);
+int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data);
 int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
                          struct device *dev);
 void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c
new file mode 100644 (file)
index 0000000..68d4240
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * tegra20_wm9712.c - Tegra machine ASoC driver for boards using WM9712 codec.
+ *
+ * Copyright 2012 Lucas Stach <dev@lynxeye.de>
+ *
+ * Partly based on code copyright/by:
+ * Copyright 2011,2012 Toradex 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.
+ *
+ * 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/gpio.h>
+#include <linux/of_gpio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "tegra-snd-wm9712"
+
+struct tegra_wm9712 {
+       struct platform_device *codec;
+};
+
+static const struct snd_soc_dapm_widget tegra_wm9712_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_LINE("LineIn", NULL),
+       SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+       snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+
+       return snd_soc_dapm_sync(dapm);
+}
+
+static struct snd_soc_dai_link tegra_wm9712_dai = {
+       .name = "AC97 HiFi",
+       .stream_name = "AC97 HiFi",
+       .cpu_dai_name = "tegra-ac97-pcm",
+       .codec_dai_name = "wm9712-hifi",
+       .codec_name = "wm9712-codec",
+       .init = tegra_wm9712_init,
+};
+
+static struct snd_soc_card snd_soc_tegra_wm9712 = {
+       .name = "tegra-wm9712",
+       .owner = THIS_MODULE,
+       .dai_link = &tegra_wm9712_dai,
+       .num_links = 1,
+
+       .dapm_widgets = tegra_wm9712_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(tegra_wm9712_dapm_widgets),
+       .fully_routed = true,
+};
+
+static int tegra_wm9712_driver_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct snd_soc_card *card = &snd_soc_tegra_wm9712;
+       struct tegra_wm9712 *machine;
+       int ret;
+
+       if (!pdev->dev.of_node) {
+               dev_err(&pdev->dev, "No platform data supplied\n");
+               return -EINVAL;
+       }
+
+       machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm9712),
+                              GFP_KERNEL);
+       if (!machine) {
+               dev_err(&pdev->dev, "Can't allocate tegra_wm9712 struct\n");
+               return -ENOMEM;
+       }
+
+       card->dev = &pdev->dev;
+       platform_set_drvdata(pdev, card);
+       snd_soc_card_set_drvdata(card, machine);
+
+       machine->codec = platform_device_alloc("wm9712-codec", -1);
+       if (!machine->codec) {
+               dev_err(&pdev->dev, "Can't allocate wm9712 platform device\n");
+               return -ENOMEM;
+       }
+
+       ret = platform_device_add(machine->codec);
+       if (ret)
+               goto codec_put;
+
+       ret = snd_soc_of_parse_card_name(card, "nvidia,model");
+       if (ret)
+               goto codec_unregister;
+
+       ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
+       if (ret)
+               goto codec_unregister;
+
+       tegra_wm9712_dai.cpu_of_node = of_parse_phandle(np,
+                                      "nvidia,ac97-controller", 0);
+       if (!tegra_wm9712_dai.cpu_of_node) {
+               dev_err(&pdev->dev,
+                       "Property 'nvidia,ac97-controller' missing or invalid\n");
+               ret = -EINVAL;
+               goto codec_unregister;
+       }
+
+       tegra_wm9712_dai.platform_of_node = tegra_wm9712_dai.cpu_of_node;
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+                       ret);
+               goto codec_unregister;
+       }
+
+       return 0;
+
+codec_unregister:
+       platform_device_del(machine->codec);
+codec_put:
+       platform_device_put(machine->codec);
+       return ret;
+}
+
+static int tegra_wm9712_driver_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct tegra_wm9712 *machine = snd_soc_card_get_drvdata(card);
+
+       snd_soc_unregister_card(card);
+
+       platform_device_unregister(machine->codec);
+
+       return 0;
+}
+
+static const struct of_device_id tegra_wm9712_of_match[] = {
+       { .compatible = "nvidia,tegra-audio-wm9712", },
+       {},
+};
+
+static struct platform_driver tegra_wm9712_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+               .of_match_table = tegra_wm9712_of_match,
+       },
+       .probe = tegra_wm9712_driver_probe,
+       .remove = tegra_wm9712_driver_remove,
+};
+module_platform_driver(tegra_wm9712_driver);
+
+MODULE_AUTHOR("Lucas Stach");
+MODULE_DESCRIPTION("Tegra+WM9712 machine ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_wm9712_of_match);